{"id":19978358,"url":"https://github.com/geekcell/user-policy-bundle","last_synced_at":"2025-03-01T19:16:01.159Z","repository":{"id":213289499,"uuid":"657666134","full_name":"geekcell/user-policy-bundle","owner":"geekcell","description":"An opinionated bundle, which provides a way to implement user policies for your Symfony app.","archived":false,"fork":false,"pushed_at":"2024-12-02T20:20:47.000Z","size":136,"stargazers_count":0,"open_issues_count":3,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-01T18:48:52.647Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/geekcell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-23T15:00:28.000Z","updated_at":"2023-06-23T15:01:10.000Z","dependencies_parsed_at":"2023-12-22T16:50:46.253Z","dependency_job_id":"96925fb9-f3a7-4956-935b-c05e1dc422ed","html_url":"https://github.com/geekcell/user-policy-bundle","commit_stats":null,"previous_names":["geekcell/user-policy-bundle"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geekcell%2Fuser-policy-bundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geekcell%2Fuser-policy-bundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geekcell%2Fuser-policy-bundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geekcell%2Fuser-policy-bundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/geekcell","download_url":"https://codeload.github.com/geekcell/user-policy-bundle/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241411543,"owners_count":19958753,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T03:33:09.482Z","updated_at":"2025-03-01T19:16:01.105Z","avatar_url":"https://github.com/geekcell.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Symfony Bundle for User Policies\n\nThis Symfony bundle provides an opinionated way to implement user policies for your app. User policies are useful when you need fine-grained/complex rules regarding access to a particular resource. These policies are not static configurations, but written in PHP, i.e. you get full flexibility, but without cluttering your code base.\n\n## Example\n\nLet's say you are working on a video platform where users are only allowed to upload if they either have a premium subscription or a remaining upload quota.\n\n```php\n#[AsPolicy(Video::class)]\nclass VideoPolicy implements Policy\n{\n    public function __construct(\n        private readonly QuotaService $quotaService,\n    ) {\n    }\n\n    public function upload(User $user): bool\n    {\n        return (\n            $user-\u003ehasPremiumSubscription() || \n            $this-\u003equotaService-\u003egetRemainingUserUploads($user) \u003e 0\n        );\n    }\n}\n```\n\n```php\nclass VideoController extends AbstractController\n{\n    #[Route('/videos/new_upload')]\n    public function create(): Response\n    {\n        if ($this-\u003egetUser()-\u003ecanUpload(Video::class)) {\n            // Proceed with upload ...\n        }\n\n        $this-\u003ecreateAccessDeniedException('Operation not allowed.');\n    }\n}\n```\n\nPretty nice, isn't it? The business logic is encapsulated in policy classes and can be _magically queried_ directly from the user object.\n\n## Installation\n\nTo use this bundle, require it in Composer\n\n```bash\ncomposer require geekcell/user-policy-bundle\n```\n\nWhen installed, add the following lines in your `config/services.yaml`\n\n```yaml\nservices:\n\n    # Add these lines below to your services.yaml\n\n    _instanceof:\n        GeekCell\\UserPolicyBundle\\Contracts\\Policy:\n            tags: ['geek_cell.user_policy.policy']\n```\n\nThese lines are crucial for Symfony to auto-discover the policies defined in your app. Alternatively, policies can be manually configured or even guessed by name, but these methods are not recommended.\n\nNow add the `HasPolicies` trait to you user class.\n\n```php\n\u003c?php\n\nnamespace App\\Security;\n\nuse GeekCell\\UserPolicyBundle\\Trait\\HasPolicies;\nuse Symfony\\Component\\Security\\Core\\User\\PasswordAuthenticatedUserInterface;\nuse Symfony\\Component\\Security\\Core\\User\\UserInterface;\n\nclass User implements UserInterface, PasswordAuthenticatedUserInterface\n{\n    use HasPolicies;\n\n    // ...\n}\n```\n\nYou are now ready to go.\n\n## Writing Policies\n\nA basic policy looks like this:\n\n```php\n\u003c?php\n\nnamespace App\\Security\\Policy;\n\nuse App\\Entity\\Book;\nuse App\\Security\\User;\nuse GeekCell\\UserPolicyBundle\\Contracts\\Policy;\nuse GeekCell\\UserPolicyBundle\\Support\\Attribute\\AsPolicy;\n\n#[AsPolicy(Book::class)]\nclass BookPolicy implements Policy\n{\n    public function create(User $user): bool\n    {\n        // ...\n    }\n\n    public function update(User $user, Book $book): bool\n    {\n        // ...\n    }\n\n    public function delete(User $user, Book $book, mixed $someArguments): bool\n    {\n        // ...\n    }\n}\n```\n\nLet's break it down:\n\n- A policy must implement the `GeekCell\\UserPolicyBundle\\Contracts\\Policy` marker interface.\n- Use the `#[AsPolicy]` attribute to associate a policy to a subject.\n- The policy methods can have arbitrary names, i.e. they're not limited to CRUD operations.\n    - The methods always take the current `User` as their first argument.\n    - The second argument can optionally be an instance of the current subject.\n    - The remaining arguments are purely optional and can be used as needed.\n\nFor the time being, you can return either `true` or `false` as indication whether a user is allowed to perform some action on a subject (or subject instance).\n\n## Checking Policies\n\nWith the `HasPolicies` trait, your `User` instance now has some new magical abilities:\n\n```php\n// These below will internally call BookPolicy::create($user)\n$user-\u003ecan('create', 'App\\Entity\\Book');\n$user-\u003ecanCreate('App\\Entity\\Book');\n$user-\u003ecannot('create', 'App\\Entity\\Book');\n$user-\u003ecannotCreate('App\\Entity\\Book');\n\n// These will call BookPolicy::update($user, $book)\n$user-\u003ecan('update', $book);\n$user-\u003ecanUpdate($book);\n$user-\u003ecannot('update', $book);\n$user-\u003ecanUpdate($book);\n\n// And these will call BookPolicy::delete($user, $book, $foo, $bar, $baz)\n$user-\u003ecan('delete', $book, $foo, $bar, $baz);\n$user-\u003ecanDelete($book, $foo, $bar, $baz);\n$user-\u003ecannot('delete', $book, $foo, $bar, $baz);\n$user-\u003ecannnotDelete($book, $foo, $bar, $baz);\n```\n\n## Roles Helper\n\nIn many cases, policies are likely to have some relationship to one or more rules associated with a user. This bundle provides some convenience methods to make roles easier to query.\n\n```php\n$user-\u003esetRoles(['MANAGER_ROLE', 'EDITOR_ROLE']);\n\n$user-\u003eis('editor'); // true\n$user-\u003eisEditor(); // true\n$user-\u003eis('manager'); // true\n$user-\u003eisManager(); // true\n$user-\u003eisNot('admin'); // true\n$user-\u003eisNotAdmin(); // true\n```\n\n## Inspiration(s)\n\n- Laravel Authorization (https://laravel.com/docs/authorization)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeekcell%2Fuser-policy-bundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeekcell%2Fuser-policy-bundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeekcell%2Fuser-policy-bundle/lists"}