{"id":22049945,"url":"https://github.com/tobento-ch/service-acl","last_synced_at":"2025-10-14T17:45:52.816Z","repository":{"id":57070809,"uuid":"345329870","full_name":"tobento-ch/service-acl","owner":"tobento-ch","description":"A simple PHP role and user-level access control system.","archived":false,"fork":false,"pushed_at":"2025-09-24T07:27:49.000Z","size":77,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"2.x","last_synced_at":"2025-10-03T07:29:10.478Z","etag":null,"topics":["acl","permissions","php","role-based-access-control"],"latest_commit_sha":null,"homepage":"","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/tobento-ch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-03-07T11:33:44.000Z","updated_at":"2025-05-24T16:46:57.000Z","dependencies_parsed_at":"2025-09-24T09:17:09.539Z","dependency_job_id":"8adaae34-3ca9-42b8-8d5e-d3bdcf95431a","html_url":"https://github.com/tobento-ch/service-acl","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/tobento-ch/service-acl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobento-ch%2Fservice-acl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobento-ch%2Fservice-acl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobento-ch%2Fservice-acl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobento-ch%2Fservice-acl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tobento-ch","download_url":"https://codeload.github.com/tobento-ch/service-acl/tar.gz/refs/heads/2.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobento-ch%2Fservice-acl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279020045,"owners_count":26086806,"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","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["acl","permissions","php","role-based-access-control"],"created_at":"2024-11-30T14:17:07.182Z","updated_at":"2025-10-14T17:45:52.810Z","avatar_url":"https://github.com/tobento-ch.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ACL Service\n\nThe ACL Service is a simple role and user-level access control system.\n\n## Table of Contents\n\n- [Getting started](#getting-started)\n\t- [Requirements](#requirements)\n\t- [Highlights](#highlights)\n\t- [Simple Example](#simple-example)\n- [Documentation](#documentation)\n\t- [Rules](#rules)\n        - [Default Rule](#default-rule)\n        - [Default Rule Custom](#default-rule-custom)\n        - [Custom Rules](#custom-rules)\n\t- [Permissions](#permissions)\n\t- [Roles](#roles)   \n- [Credits](#credits)\n___\n\n# Getting started\n\nAdd the latest version of the Acl service running this command.\n\n```\ncomposer require tobento/service-acl\n```\n\n## Requirements\n\n- PHP 8.4 or greater\n\n## Highlights\n\n- Framework-agnostic, will work with any project\n- Customize permission behaviour\n- Decoupled design\n\n## Simple Example\n\nHere is a simple example of how to use the Acl service.\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\nuse Tobento\\Service\\Acl\\Authorizable;\nuse Tobento\\Service\\Acl\\AuthorizableAware;\nuse Tobento\\Service\\Acl\\Role;\n\n// User class example.\nclass User implements Authorizable\n{\n    use AuthorizableAware;\n    \n    public function __construct(\n        protected string $name,\n    ) {}\n}\n\n// Create Acl.\n$acl = new Acl();\n\n// Adding rules.\n$acl-\u003erule('articles.read')\n    -\u003etitle('Article Read')\n    -\u003edescription('If a user can read articles');\n    \n$acl-\u003erule('articles.create');\n$acl-\u003erule('articles.update');\n\n// Create role.\n$guestRole = new Role('guest');\n\n// Adding permissions on role.\n$guestRole-\u003eaddPermissions(['articles.read']);\n\n// Create and set user role.\n$user = new User('Nick')-\u003esetRole($guestRole);\n\n// Adding permissions on user.\n// If permissions are set on user, role permissions will not count anymore.\n$user-\u003eaddPermissions(['articles.read']);\n\n// Set current user.\n$acl-\u003esetCurrentUser($user);\n\n// Adding additional permissions for the current user only.\n$acl-\u003eaddPermissions(['articles.create']);\n\n// Check permissions for current user.\nif ($acl-\u003ecan('articles.read')) {\n    // user has permission to read articles.\n}\n\n// check permission for specific user.\nif ($acl-\u003ecant(key: 'articles.read', user: $user)) {\n    // user has not permission to read articles.\n}\n```\n\n# Documentation\n\n## Rules\n\nAdding and getting rules.\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\nuse Tobento\\Service\\Acl\\RuleInterface;\n\n// Create Acl.\n$acl = new Acl();\n\n// Add default rule.\n$acl-\u003erule('articles.read');\n\n// Add custom rule.\n$acl-\u003eaddRule(RuleInterface $rule);\n\n// Get rules.\nforeach($acl-\u003egetRules() as $rule)\n{\n    $key = $rule-\u003egetKey();\n    $inputKey = $rule-\u003egetInputKey();\n    $title = $rule-\u003egetTitle();\n    $description = $rule-\u003egetDescription();\n    $area = $rule-\u003egetArea();\n}\n\n// get specific rules\n$rule = $acl-\u003egetRule('articles.read');\n```\n\n### Default Rule\n\nThe default rule has the following permission behaviour:\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\n\n// Create Acl.\n$acl = new Acl();\n\n$acl-\u003erule('articles.read');\n$acl-\u003erule('articles.update');\n\n// Create role.\n$role = new Role('guest');\n\n// Create and set user role.\n$user = new User('Nick')-\u003esetRole($role);\n\n// Adding permissions on acl, only for current user.\n$acl-\u003eaddPermissions(['articles.read']);\n\n// Adding permissions on role.\n$role-\u003eaddPermissions(['articles.read']);\n\n// Adding permissions on user.\n// If permissions are set on user, role permissions will not count anymore.\n// Only acl and user specific permissions.\n$user-\u003eaddPermissions(['articles.read']);\n```\n\nAreas bahviour:\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\n\n// Create Acl.\n$acl = new Acl();\n\n$acl-\u003erule('articles.read', 'frontend');\n$acl-\u003erule('articles.update', 'backend');\n\n// Guest Role taking only frontend rules into account,\n// ignoring any permission from backend rules even if permission is given.\n$role = new Role('guest', ['frontend']);\n\n// Editor can have frontend and backend rules.\n$role = new Role('editor', ['frontend', 'backend']);\n```\n\n### Default Rule Custom\n\nYou can easily add a custom handler for extending a specific rule behaviour.\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\nuse Tobento\\Service\\Acl\\Authorizable;\nuse Tobento\\Service\\Acl\\AuthorizableAware;\nuse Tobento\\Service\\Acl\\Role;\n\n// User class example.\nclass User implements Authorizable\n{\n    use AuthorizableAware;\n    \n    public function __construct(\n        protected string $name,\n    ) {}\n}\n\n// Article class example\nclass Article\n{    \n    public function __construct(\n        protected string $name,\n        protected array $roles = [],\n        protected null|Authorizable $user = null\n    ) {}\n\n    public function getName(): string\n    {\n        return $this-\u003ename;\n    }\n    \n    public function getUser(): null|Authorizable\n    {\n        return $this-\u003euser;\n    }\n\n    public function getRoles(): array\n    {\n        return $this-\u003eroles;\n    }    \n}\n\n// Create Acl.\n$acl = new Acl();\n\n// Rule to check if user is allowed to access a specific resource.\n$acl-\u003erule('resource')\n    -\u003eneedsPermission(false)\n    -\u003ehandler(function(Authorizable $user, null|Authorizable $resourceUser): bool {\n\n        if (is_null($resourceUser)) {\n            return false;\n        }\n\n        return $user === $resourceUser;\n    });\n\n// Rule to check if user has role for a specific resource.\n$acl-\u003erule('has_role')\n    -\u003eneedsPermission(false)\n    -\u003ehandler(function(Authorizable $user, array $roles = []) {\n\n        if (empty($roles)) {\n            return true;\n        }\n\n        return in_array($user-\u003erole()-\u003ekey(), $roles);                    \n    });\n            \n$user = (new User('Nick'))-\u003esetRole(new Role('editor'));\n\n$acl-\u003esetCurrentUser($user);    \n\n$article = new Article('About us', ['editor'], $user);\n\n// Check resource access.\nif ($acl-\u003ecan('resource', [$article-\u003egetUser()])) {\n    // user can access about page.\n}\n\n// Check resource role access.\nif ($acl-\u003ecan('has_role', [$article-\u003egetRoles()])) {\n    // user has the right role to access this resource.\n}\n```\n\n### Custom Rules\n\nYou can easily add a custom rule for a different permission strategy.\n\nYour Rule must implement the following RuleInterface.\n\n```php\n/**\n * RuleInterface\n */\ninterface RuleInterface\n{\n    /**\n     * Get the key.\n     *\n     * @return string The key such as 'user.create'.\n     */    \n    public function getKey(): string;\n\n    /**\n     * Get the input key. May be used for form input.\n     *\n     * @return string The key such as 'user_create'.\n     */    \n    public function getInputKey(): string;    \n    \n    /**\n     * Get the title.\n     *\n     * @return string The title\n     */    \n    public function getTitle(): string;\n    \n    /**\n     * Get the description.\n     *\n     * @return string The description\n     */    \n    public function getDescription(): string;\n    \n    /**\n     * Get the area.\n     *\n     * @return string\n     */    \n    public function getArea(): string;\n\n    /**\n     * If the rule requires permissions to match the rule.\n     *\n     * @return bool\n     */    \n    public function requiresPermission(): bool;    \n\n    /**\n     * Return if the rule matches the criteria.\n     *\n     * @param AclInterface\n     * @param string A permission key 'user.create'.\n     * @param array Any parameters for custom handler\n     * @param null|Authorizable\n     * @return bool True if rule matches, otherwise false\n     */    \n    public function matches(\n        AclInterface $acl,\n        string $key,\n        array $parameters = [],\n        ?Authorizable $user = null\n    ): bool;    \n}\n```\n\nLets make a custum rule for just letting user specific permissions have access ignoring acl and role permissions.\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\nuse Tobento\\Service\\Acl\\AclInterface;\nuse Tobento\\Service\\Acl\\RuleInterface;\nuse Tobento\\Service\\Acl\\Authorizable;\nuse Tobento\\Service\\Acl\\AuthorizableAware;\nuse Tobento\\Service\\Acl\\Role;\n\n// Custom rule\nclass CustomRule implements RuleInterface\n{    \n    public function __construct(\n        protected string $key,\n        protected string $area,\n    ) {}\n \n    public function getKey(): string\n    {\n        return $this-\u003ekey;\n    }\n\n    public function getInputKey(): string\n    {\n        return $this-\u003ekey;\n    }\n       \n    public function getTitle(): string\n    {        \n        return $this-\u003ekey;\n    }\n   \n    public function getDescription(): string\n    {\n        return '';\n    }\n \n    public function getArea(): string\n    {\n        return $this-\u003earea;\n    }\n   \n    public function requiresPermission(): bool\n    {\n        return true;\n    }\n \n    public function matches(\n        AclInterface $acl,\n        string $key,\n        array $parameters = [],\n        ?Authorizable $user = null\n    ): bool {\n            \n        $user = $user ?: $acl-\u003egetCurrentUser();\n\n        // not user at all\n        if (is_null($user)) {\n            return false;\n        }\n        \n        // user needs a role.\n        if (! $user-\u003ehasRole()) {\n            return false;\n        }\n\n        // collect only user permissions.\n        if (!$user-\u003ehasPermissions()) {\n            return false;\n        }\n        \n        $permissions = $user-\u003egetPermissions();\n\n        // permission check\n        if (!in_array($key, $permissions)) {\n            return false;\n        }\n        \n        // area check\n        if (!in_array($this-\u003egetArea(), $user-\u003erole()-\u003eareas())) {\n            return false;\n        }\n        \n        return true;\n    }\n}\n\n// User class example.\nclass User implements Authorizable\n{\n    use AuthorizableAware;\n    \n    public function __construct(\n        protected string $name,\n    ) {}\n}\n\n// Create Acl.\n$acl = new Acl();\n\n// Adding default rules.\n$acl-\u003eaddRule(new CustomRule('articles.read', 'frontend'));\n$acl-\u003eaddRule(new CustomRule('articles.create', 'frontend'));\n\n// Create role.\n$role = new Role('guest');\n\n// Adding permissions on role does has no effect.\n$role-\u003eaddPermissions(['articles.read']);\n\n// Create and set user role.\n$user = new User('Nick')-\u003esetRole($role);\n$user-\u003eaddPermissions(['articles.create']);\n\n// Set current user.\n$acl-\u003esetCurrentUser($user);\n\nif ($acl-\u003ecan('articles.create')) {\n    // user has permission to read articles.\n}\n```\n\n## Permissions\n\nThe following methods are available on objects implementing the Permissionable Interface or the Authorizable Interface.\n\n- Tobento\\Service\\Acl\\Acl::class\n- Tobento\\Service\\Acl\\Role::class\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\nuse Tobento\\Service\\Acl\\Permissionable;\nuse Tobento\\Service\\Acl\\Authorizable;\n\n// Create Acl.\n$acl = new Acl();\n\n// Set all permissions.\n$acl-\u003esetPermissions(['user.create', 'user.update']);\n\n// Adding more permissions.\n$acl-\u003eaddPermissions(['user.delete']);\n\n$permissions = $acl-\u003egetPermissions(); // ['user.create', 'user.update', 'user.delete']\n\n// Has any permissions at all.\n$hasPermissions = $acl-\u003ehasPermissions();\n\n// Removing permissions.\n$acl-\u003eremovePermissions(['user.delete']);\n\n// Has specific permission.\n$hasPermission = $acl-\u003ehasPermission('user.update');\n```\n\nAvailable methods for checking permissions on acl:\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\n\n// Create Acl.\n$acl = new Acl();\n\n// Check permissions for current user.\nif ($acl-\u003ecan('articles.read')) {\n    // user has permission to read articles.\n}\n\n// Check permission for specific user.\nif ($acl-\u003ecant(key: 'articles.read', user: $user)) {\n    // user has not permission to read articles.\n}\n\n// You can check multiple permissions too.\nif ($acl-\u003ecan('articles.create|articles.update')) {\n    // user has permission to create and update articles.\n}\n\n// Multiple permissions with parameters.\nif ($acl-\u003ecan('articles.create|resource', ['resource' =\u003e [$article-\u003egetUser()]])) {\n    // user has permission to create and access the specific article.\n}\n```\n\nChecking permissions on Authorizable object:\n\nFor more information on Helper Function visit [tobento/helper-function](https://github.com/tobento-ch/helper-function)\n\n```php\nuse Tobento\\Service\\HelperFunction\\Functions;\nuse Psr\\Container\\ContainerInterface;\nuse Tobento\\Service\\Di\\Container;\nuse Tobento\\Service\\Acl\\Acl;\nuse Tobento\\Service\\Acl\\AclInterface;\nuse Tobento\\Service\\Acl\\Authorizable;\nuse Tobento\\Service\\Acl\\AuthorizableAware;\nuse Tobento\\Service\\Acl\\Role;\n\n// create container.\n$container = new Container();\n\n// Set up Helper Function acl() for supporting\n// checking permission directly on Authorizable objects.\n$functions = new Functions();\n$functions-\u003eset(ContainerInterface::class, $container);\n\n// Register Acl functions.\n$functions-\u003eregister('dir/to/acl/functions.php');\n\n// User class example.\nclass User implements Authorizable\n{\n    use AuthorizableAware;\n    \n    public function __construct(\n        protected string $name,\n    ) {}\n}\n\n// Create Acl.\n$acl = new Acl();\n\n// Add Acl to container.\n$container-\u003eset(AclInterface::class, $acl);\n\n// Adding rules.\n$acl-\u003erule('articles.read');\n\n// Create role.\n$guestRole = new Role('guest');\n\n// Adding permissions on role.\n$guestRole-\u003eaddPermissions(['articles.read']);\n\n// Create and set user role.\n$user = new User('Nick')-\u003esetRole($guestRole);\n\n// Check permissions on user.\nif ($user-\u003ecan('articles.read')) {\n    // user has permission to read articles.\n}\n\n// check permission for specific user.\nif ($user-\u003ecant('articles.read')) {\n    // user has not permission to read articles.\n}\n```\n\n## Roles\n\nWorking with roles.\n\n```php\nuse Tobento\\Service\\Acl\\Acl;\nuse Tobento\\Service\\Acl\\Role;\nuse Tobento\\Service\\Acl\\RoleInterface;\nuse Tobento\\Service\\Acl\\Roles;\nuse Tobento\\Service\\Acl\\RolesInterface;\n\n// Create Acl.\n$acl = new Acl();\n\n// Set roles on acl for later reusage if needed.\n$acl-\u003esetRoles([\n    new Role('guest'),\n    new Role('editor'),\n]);\n// or\n$acl-\u003esetRoles(new Roles(\n    new Role('guest'),\n    new Role('editor'),\n));\n\n// Get roles:\n$roles = $acl-\u003eroles();\n\nvar_dump($roles instanceof RolesInterface);\n// bool(true)\n\n// Iterate roles:\nforeach($acl-\u003eroles() as $role) {\n    $key = $role-\u003ekey();\n    $active = $role-\u003eactive();\n    $areas = $role-\u003eareas();\n    $name = $role-\u003ename();\n}\n\n// Get Specific role:\n$role = $roles-\u003eget('editor');\n$role = $acl-\u003egetRole('editor'); // or\n// null|RoleInterface\n\n// Check if role exists:\nvar_dump($roles-\u003ehas('editor'));\nvar_dump($acl-\u003ehasRole('editor')); // or\n// bool(true)\n\n// Sort roles returning a new instance:\n$roles = $roles-\u003esort(fn(RoleInterface $a, RoleInterface $b): int =\u003e $a-\u003ename() \u003c=\u003e $b-\u003ename());\n\n// Filter roles returning a new instance:\n$roles = $roles-\u003efilter(fn(RoleInterface $role): bool =\u003e $role-\u003eactive());\n\n// Filter by area roles returning a new instance:\n$roles = $roles-\u003earea('frontend');\n\n// Filter (in)active roles returning a new instance:\n$roles = $roles-\u003eactive();\n$roles = $roles-\u003eactive(false);\n\n// Returns a new instance only the roles specified:\n$roles = $roles-\u003eonly(['editor']);\n\n// Returns a new instance except the roles specified:\n$roles = $roles-\u003eexcept(['editor']);\n\n// Add a role returning a new instance:\n$roles = $roles-\u003eadd(new Role('admin'));\n\n// Remove a role returning a new instance:\n$roles = $roles-\u003eremove('editor');\n\n// Get first role:\n$role = $roles-\u003efirst();\n// null|RoleInterface\n\n// Get column of roles:\n$roleNames = $roles-\u003ecolumn('name');\n$roleNamesByKey = $roles-\u003ecolumn('name', 'key');\n\n// Get all roles:\n$roles = $roles-\u003eall();\n$roles = $acl-\u003egetRoles(); // or\n// array\u003cstring, RoleInterface\u003e\n```\n\n# Credits\n\n- [Tobias Strub](https://www.tobento.ch)\n- [All Contributors](../../contributors)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobento-ch%2Fservice-acl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftobento-ch%2Fservice-acl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobento-ch%2Fservice-acl/lists"}