Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Access Permissions Bundle Laravel Package

dmytrof/access-permissions-bundle

Symfony 4/5 bundle for defining access permissions with security voters. Provides AbstractVoter plus CRUD helpers/traits and an AdminInterface to expose per-user allowed attributes (view/create/edit/delete) for your entities.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use Case

  1. Install the Bundle:

    composer require dmytrof/access-permissions-bundle
    

    Enable in config/bundles.php:

    Dmytrof\AccessPermissionsBundle\DmytrofAccessPermissionsBundle::class => ['all' => true],
    
  2. Set Up Security: Ensure Symfony’s security component is installed and configured in security.yaml. Example:

    security:
        role_hierarchy:
            ROLE_ADMIN: ROLE_USER
    
  3. Create a Voter for an Entity: For an Article entity, generate a voter:

    // src/Security/ArticleVoter.php
    use Dmytrof\AccessPermissionsBundle\Security\{AbstractVoter, CRUDVoterInterface, Traits\CRUDVoterTrait};
    
    class ArticleVoter extends AbstractVoter implements CRUDVoterInterface
    {
        use CRUDVoterTrait;
    
        protected const SUBJECT = [Article::class];
        public const PREFIX = 'app.article.';
        public const VIEW = self::PREFIX . 'view';
        public const CREATE = self::PREFIX . 'create';
        public const EDIT = self::PREFIX . 'edit';
        public const DELETE = self::PREFIX . 'delete';
    
        public const ATTRIBUTES = [self::VIEW, self::CREATE, self::EDIT, self::DELETE];
    }
    
  4. Implement AdminInterface in User Model: Extend your User entity to support admin permissions:

    // src/Entity/User.php
    use Dmytrof\AccessPermissionsBundle\Security\AdminInterface;
    
    class User implements UserInterface, AdminInterface
    {
        public function getAdminAccessAttributes(): array
        {
            return [
                ArticleVoter::VIEW,
                ArticleVoter::CREATE,
            ];
        }
    }
    
  5. Protect a Controller Action: Use denyAccessUnlessGranted in your controller:

    // src/Controller/ArticleController.php
    public function create(Request $request)
    {
        $this->denyAccessUnlessGranted(ArticleVoter::CREATE);
        // Handle creation logic
    }
    
  6. Test Permissions: Log in as an admin/user and verify access is enforced. Use Symfony’s debug toolbar or bin/console debug:security to inspect voters.


First Use Case: Restrict Article Creation to Admins

  • Goal: Only users with ArticleVoter::CREATE permission can create articles.
  • Steps:
    1. Define ArticleVoter (above).
    2. Grant ArticleVoter::CREATE to admin users via getAdminAccessAttributes().
    3. Add $this->denyAccessUnlessGranted(ArticleVoter::CREATE) to the create() action.
    4. Test by logging in as a non-admin and attempting to create an article (should fail).

Implementation Patterns

Core Workflows

1. Voter Creation Pattern

  • For CRUD Operations: Extend AbstractVoter and use CRUDVoterTrait for standard VIEW, CREATE, EDIT, DELETE permissions.
    class ArticleVoter extends AbstractVoter implements CRUDVoterInterface
    {
        use CRUDVoterTrait;
        protected const SUBJECT = [Article::class];
        public const PREFIX = 'app.article.';
    }
    
  • For Custom Logic: Override canRoleXyz methods for role-specific rules. Example for authors:
    protected function canRoleAuthorEdit(TokenInterface $token, $subject = null): bool
    {
        return $subject instanceof Article
            && $token->getUser() instanceof Author
            && $subject->getAuthor() === $token->getUser();
    }
    
    Order Matters: Voters check canRoleAuthorEditcanRoleAuthorcan (fallback).

2. Permission Assignment

  • Admin-Granted Permissions: Store attributes in the database (e.g., user.permissions table) and return them via getAdminAccessAttributes().
    public function getAdminAccessAttributes(): array
    {
        return $this->permissions->toArray(); // Load from DB
    }
    
  • Role-Based Permissions: Use Symfony’s role_hierarchy in security.yaml or dynamically add roles via RolesContainer:
    $rolesContainer->addRole('ROLE_AUTHOR');
    

3. Controller Integration

  • Protect Actions: Use denyAccessUnlessGranted with the attribute:
    $this->denyAccessUnlessGranted(ArticleVoter::EDIT, $article);
    
  • Subject Binding: Pass the entity as the second argument to enforce object-level permissions:
    $this->denyAccessUnlessGranted(ArticleVoter::DELETE, $article);
    

4. UI Integration

  • Permission Selection Forms: Use AccessAttributesChoiceType or AccessAttributesCollectionType in user forms:
    $builder->add('permissions', AccessAttributesChoiceType::class, [
        'voters' => [$articleVoter, $authorVoter],
    ]);
    
  • API Metadata: Expose permission labels/descriptions via a controller:
    public function getAccessAttributes(VotersContainer $votersContainer)
    {
        return $votersContainer->getAttributeDescriptionsCollection()->sort()->getAsArray();
    }
    

5. Translation Setup

  • Localize Labels: Create translations/access_attributes.en.yaml:
    app:
        attributes:
            view: "View"
            create: "Create"
        subjects:
            article:
                label: "Articles"
                attributes:
                    view: "View articles"
    
    Access labels in templates:
    {{ 'app.attributes.view.label'|trans }}
    

Integration Tips

1. Symfony Security Stack

  • Register Voters: Voters are auto-discovered via Symfony’s autowiring. No manual registration needed.
  • Custom Voters: For non-CRUD logic, create a voter without CRUDVoterTrait:
    class CustomVoter extends AbstractVoter
    {
        protected function can(TokenInterface $token, $object, array $attributes): bool
        {
            // Custom logic
        }
    }
    

2. Database Schema

  • Store Permissions: Add a permissions JSON column to your users table or a separate user_permissions table:
    /**
     * @ORM\Column(type="json")
     */
    private array $permissions = [];
    
    Populate via getAdminAccessAttributes().

3. Testing

  • Unit Test Voters: Mock the TokenInterface and test canRoleXyz methods:
    public function testCanRoleAuthorEdit()
    {
        $token = $this->createMock(TokenInterface::class);
        $token->method('getUser')->willReturn($author);
    
        $this->assertTrue($articleVoter->canRoleAuthorEdit($token, $article));
    }
    
  • Functional Test Permissions: Use Symfony’s login fixture and denyAccessUnlessGranted assertions:
    public function testCreateArticleRequiresPermission()
    {
        $client = static::createClient();
        $client->loginUser($nonAdminUser);
    
        $client->request('POST', '/articles');
        $this->assertResponseStatusCodeSame(403); // Forbidden
    }
    

4. Performance

  • Cache Voters: Symfony caches voters by default. For dynamic permissions, implement isAttributeEnabled() in your voter:
    public function isAttributeEnabled(string $attribute, TokenInterface $token): bool
    {
        return $this->permissionService->check($attribute, $token->getUser());
    }
    
  • Avoid N+1 Queries: Eager-load permissions in getAdminAccessAttributes():
    public function getAdminAccessAttributes(): array
    {
        return $this->permissions->matching($this->queryBuilder)->toArray();
    }
    

5. Extending the Bundle

  • Custom Voter Traits: Create reusable traits for common logic (e.g., OwnershipVoterTrait):
    trait OwnershipVoterTrait
    {
        protected function isOwner(TokenInterface $token, $subject): bool
        {
            return $subject->getOwner() === $token->getUser();
        }
    }
    
  • Event Listeners: Trigger events when permissions change (e.g., PermissionUpdatedEvent):
    // src/EventListener/PermissionListener.php
    public function onPermissionUpdated(PermissionUpdatedEvent $event)
    {
        $this->eventDispatcher->dispatch(new PermissionChangedEvent($event->getUser()));
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui