{"id":15277439,"url":"https://github.com/tattersoftware/codeigniter4-permits","last_synced_at":"2025-08-05T16:27:08.290Z","repository":{"id":42020293,"uuid":"177658198","full_name":"tattersoftware/codeigniter4-permits","owner":"tattersoftware","description":"Model permission handling for CodeIgniter 4","archived":false,"fork":false,"pushed_at":"2024-01-17T19:22:56.000Z","size":157,"stargazers_count":13,"open_issues_count":1,"forks_count":5,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2025-05-14T23:43:05.394Z","etag":null,"topics":[],"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/tattersoftware.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":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-25T20:19:55.000Z","updated_at":"2025-04-03T09:54:34.000Z","dependencies_parsed_at":"2024-06-19T10:00:12.327Z","dependency_job_id":"f6f506ca-4fec-4f72-9c0d-78cbbbe88e7b","html_url":"https://github.com/tattersoftware/codeigniter4-permits","commit_stats":{"total_commits":57,"total_committers":3,"mean_commits":19.0,"dds":"0.45614035087719296","last_synced_commit":"58b462fded11a252c2c96ddb5959ce6d5aa31d45"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/tattersoftware/codeigniter4-permits","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattersoftware%2Fcodeigniter4-permits","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattersoftware%2Fcodeigniter4-permits/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattersoftware%2Fcodeigniter4-permits/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattersoftware%2Fcodeigniter4-permits/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tattersoftware","download_url":"https://codeload.github.com/tattersoftware/codeigniter4-permits/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattersoftware%2Fcodeigniter4-permits/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266039796,"owners_count":23867926,"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-09-30T11:06:21.544Z","updated_at":"2025-07-22T17:04:28.834Z","avatar_url":"https://github.com/tattersoftware.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tatter\\Permits\nModel permission handling for CodeIgniter 4\n\n[![](https://github.com/tattersoftware/codeigniter4-permits/workflows/PHPUnit/badge.svg)](https://github.com/tattersoftware/codeigniter4-permits/actions/workflows/test.yml)\n[![](https://github.com/tattersoftware/codeigniter4-permits/workflows/PHPStan/badge.svg)](https://github.com/tattersoftware/codeigniter4-permits/actions/workflows/analyze.yml)\n[![](https://github.com/tattersoftware/codeigniter4-permits/workflows/Deptrac/badge.svg)](https://github.com/tattersoftware/codeigniter4-permits/actions/workflows/inspect.yml)\n[![Coverage Status](https://coveralls.io/repos/github/tattersoftware/codeigniter4-permits/badge.svg?branch=develop)](https://coveralls.io/github/tattersoftware/codeigniter4-permits?branch=develop)\n\n## Quick Start\n\n1. Install with Composer: `\u003e composer require tatter/permits`\n2. Add the trait to your models:\n```php\nclass BlogModel extends Model\n{\n    use PermitsTrait;\n```\n3. Use the CRUDL verbs to check access: `if ($blogs-\u003emayCreate()) ...`\n\n## Features\n\n`Permits` solves a common problem with object rights management: \"Can this user\nadd/change/remove this item?\" This library provides object-level access rights to your\nModel classes via a single trait that adds CRUDL-style verbs to your model.\n\n## Installation\n\nInstall easily via Composer to take advantage of CodeIgniter 4's autoloading capabilities\nand always be up-to-date:\n```bash\ncomposer require tatter/permits\n```\n\nOr, install manually by downloading the source files and adding the directory to\n**app/Config/Autoload.php**.\n\n`Permits` requires the Composer provision for `codeigniter4/authentication-implementation`\nas describe in the [CodeIgniter authentication guidelines](https://codeigniter4.github.io/CodeIgniter4/extending/authentication.html).\nYou must install and configure a [supported package](https://packagist.org/providers/codeigniter4/authentication-implementation)\nto handle authentication and authorization in order for `Permits` to understand your users.\n\n## Configuration (optional)\n\nThe library's default behavior can be altered by extending its config file. Copy\n**examples/Permits.php to **app/Config/** and follow the instructions in the comments.\nIf no config file is found in **app/Config** the library will use its own.\n\nThe Config files includes a set of `$default` access levels which you may modify in your\nown version. Each Model you intend to use should have a Config property corresponding to\nits `$table` property with properties for anything that needs adjusting from the defaults.\n\nOnce your configuration is complete simply include the `PermitsTrait` on your models to\nenable the methods:\n```php\nuse CodeIgniter\\Model;\nuse Tatter\\Permits\\Traits\\PermitsTrait;\n\nclass FruitModel extends Model\n{\n    use PermitsTrait;\n...\n```\n\n## Usage\n\nThere are two types of permissions: explicit and inferred. Both rely on this common set of\nCRUDL verbs:\n* **create**: Make new items\n* **read**: View a single item\n* **update**: Make changes to a single item\n* **delete**: Delete a single item\n* **list**: View an index of all items\n* **admin**: Perform any of the above regardless of other rights\n\nUse the corresponding Model verbs to check the access:\n```php\nif (! $model-\u003emayCreate()) {\n    return redirect()-\u003eback()-\u003ewith('error', 'You do not have permission to do that!');\n}\n\n$item = $model-\u003efind($id);\nif (! $model-\u003emayUpdate($item)) {\n    return redirect()-\u003eback()-\u003ewith('error', 'You can only update your own items!');\n}\n```\n\n`PermitsTrait` will check access rights based on the current logged in user (if there is one)\nbut you may also pass an explicit user ID to check instead:\n```php\nif (! $model-\u003emayAdmin($userId)) {\n    log_message('debug', \"User #{$userId} attempted to access item administration.\");\n}\n```\n\n### Explicit \n\nExplicit permissions are granted to users or groups via your authorization library. `Permits`\nuses [Tatter\\Users](https://packagist.org/packages/tatter/users) to interact with user records\nand determine explicit permissions. If your authentication package is not supported by\n`Users` autodiscovery then be sure to [read the docs](https://github.com/tattersoftware/codeigniter4-users)\non how to include it.\n\nWhen checking explicit permissions `Permits` uses the format \"table.verb\". For example, if\nyour project includes a `BlogModel` and you want to allow Blog Editors access to edit anybody's\nblog entries you would assign \"blogs.edit\" to the \"editors\" group.\n\n\u003e Note: Explicit permissions always take precedence over inferred permissions.\n\n### Inferred\n\nInferred permissions use the configuration (see above) to determine any individual user's\naccess to an item or group of items. There are four access levels which may be applied to\neach verb (these are constants on `Tatter\\Permits\\Config\\Permits`):\n\n* `NOBODY`: Prohibits all access without explicit permission\n* `OWNERS`: Requires the authenticated user to own the item\n* `OWNERS`: Requires the authenticated user to own the item\n* `USERS`: Requires any authenticated user\n* `ANYBODY`: Allows anyone regardless of authentication or ownership\n\nIn addition to the access levels, each model should be configured on how to determine item\nownership. Set whichever of the following values are necessary on your table's Config property:\n * `userKey`: Field for the user ID in the item or its pivot table\n * `pivotKey`: Field for the item's ID in the pivot tables\n * `pivotTable`: Table that joins the items to their owners\n\n## Example\n\nYour web app includes a Content Management System that allows the site owners to log in and\nupdate various parts of the site. This includes a blog section, with its own Model, Controller,\nand Views. Any visitors to your site can create an account and submit a blog entry, but it\nneeds to be approved before it \"goes live\". Being the brilliant developer you are, you decide\nto use `Tatter\\Permits` to manage access to the blog entries.\n\n\u003e `Permits` requires an [authentication implementation](https://packagist.org/providers/codeigniter4/authentication-implementation);\n\u003e for this example we will use [Shield](https://github.com/lonnieezell/codeigniter-shield).\n\nFirst we need to make sure our authentication package is ready with the correct permissions.\nThis may vary back package, but `Shield` defines Groups and Permissions using\n[Config files](https://github.com/lonnieezell/codeigniter-shield/blob/develop/docs/3%20-%20authorization.md).\nWe will leave the existing groups and add a new \"editors\" group.\n**app/Config/AuthGroups.php**:\n```php\n    public $groups = [\n        'superadmin' =\u003e [\n            'title'       =\u003e 'Super Admin',\n            'description' =\u003e 'Complete control of the site.',\n        ],\n        'admin' =\u003e [\n            'title'       =\u003e 'Admin',\n            'description' =\u003e 'Day to day administrators of the site.',\n        ],\n        'developer' =\u003e [\n            'title'       =\u003e 'Developer',\n            'description' =\u003e 'Site programmers.',\n        ],\n        'user' =\u003e [\n            'title'       =\u003e 'User',\n            'description' =\u003e 'General users of the site. Often customers.',\n        ],\n        'beta' =\u003e [\n            'title'       =\u003e 'Beta User',\n            'description' =\u003e 'Has access to beta-level features.',\n        ],\n        'editor' =\u003e [\n            'title'       =\u003e 'Blog Editors',\n            'description' =\u003e 'Has access to all blog entries.',\n        ],\n    ];\n```\n\nWe want to give explicit permission for blog administration to some groups, so in the same\nfile we add a new permission in the format \"{table}.{verb}\":\n```php\n    public $permissions = [\n        'admin.access'        =\u003e 'Can access the sites admin area',\n        'admin.settings'      =\u003e 'Can access the main site settings',\n        'users.manage-admins' =\u003e 'Can manage other admins',\n        'users.create'        =\u003e 'Can create new non-admin users',\n        'users.edit'          =\u003e 'Can edit existing non-admin users',\n        'users.delete'        =\u003e 'Can delete existing non-admin users',\n        'beta.access'         =\u003e 'Can access beta-level features',\n        'blogs.admin'         =\u003e 'Allows all access to blog model operations',\n    ];\n```\n\nFinally we add the new permission to the groups we want to have it, in the same file still:\n```php\n    public $matrix = [\n        'superadmin' =\u003e [\n            'admin.*',\n            'users.*',\n            'beta.*',\n            'blogs.*',\n        ],\n        'admin' =\u003e [\n            'admin.access',\n            'users.create',\n            'users.edit',\n            'users.delete',\n            'beta.access',\n            'blogs.admin',\n        ],\n        'developer' =\u003e [\n            'admin.access',\n            'admin.settings',\n            'users.create',\n            'users.edit',\n            'beta.access',\n        ],\n        'user' =\u003e [],\n        'beta' =\u003e [\n            'beta.access',\n        ],\n        'editor' =\u003e [\n            'blogs.admin',\n        ],\n    ];\n```\n\nThat's it for the third-party authorization configuration! On to `Permits` - first thing we\nneed is to set the permissions in our Config file. We can leave the defaults as they are\nand add our own property.\n**app/Config/Permits.php**:\n```php\n    /*\n     * @var array\u003cstring,mixed\u003e\n     */\n    public $blogs = [\n        'admin'      =\u003e self::NOBODY,\n        'create'     =\u003e self::USERS,\n        'list'       =\u003e self::USERS,\n        'read'       =\u003e self::OWNERS,\n        'update'     =\u003e self::OWNERS,\n        'delete'     =\u003e self::OWNERS,\n        'userKey'    =\u003e 'user_id',\n        'pivotKey'   =\u003e null,\n        'pivotTable' =\u003e null,\n    ];\n}\n```\n\nLet's break that down.\n\n1. The first permission, \"admin\": we gave explicit rights to above in our\nauth package so we do not want anyone else having access, hence `NOBODY`. Explicit permissions\ntake precedence so our \"superadmin\", \"admin\", and \"editor\" groups will still have full access.\n\n2. Next are \"list\" and \"create\": both are available to `USERS` - that is, anyone who is logged\nin. They will be able to create new entries and see a list of others' entries.\n\n3. However, \"read\", \"update\", and \"delete\" are all restricted to `OWNERS` - authenticated users will only\nbe able to click on their own entries to read and modify the content.\n\n4. Finally, we need a way for `Permits` to decide \"who owns this\". In this case we set \"userKey\"\nbut leave the pivot properties blank - meaning, our `blogs` table has a field called `user_id`\nwhich corresponds to the ID of the user that created the blog.\n\n\u003e In more complex setups where multiple users are assigned to multiple blogs we might have a\n\u003e join table, in which case we would also have set \"pivotTable\" to something like `blogs_users`\n\u003e and \"pivotKey\" like `blog_id`.\n\nConfiguration complete! The final piece to the integration is to add our trait to the blog\nmodel, which will handle activating our access verbs.\n**app/Models/BlogModel.php**:\n```php\n\nuse App\\Entities\\Blog;\nuse CodeIgniter\\Model;\nuse Tatter\\Permits\\Traits\\PermitsTrait;\n\nclass BlogModel extends Model\n{\n    use PermitsTrait;\n\n    protected $table      = 'blogs';\n    protected $primaryKey = 'id';\n    protected $returnType = Blog::class;\n...\n}\n```\n\nIntegration complete! Now you are ready to start using `Permits` in your code. Let's make\na Controller for our blogs and add some permissions checks before the regular code.\n**app/Controllers/Blogs.php**:\n```php\n\u003c?php\n\nnamespace App\\Controllers;\n\nuse App\\Models\\BlogModel;\nuse CodeIgniter\\HTTP\\RedirectResponse;\n\nclass Blogs extends BaseController\n{\n    /**\n     * @var BlogModel\n     */\n    protected $model;\n\n    /**\n     * Preloads the model.\n     */\n    public function __construct()\n    {\n        $this-\u003emodel = model(BlogModel::class);\n    }\n\n    /**\n     * Displays the list of approved blogs\n     * for all visitors of the website.\n     */\n    public function index(): string\n    {\n        return view('blogs/public', [\n            'blogs' =\u003e $this-\u003emodel-\u003efindAll(),\n        ]);\n    }\n    \n    /**\n     * Displays blogs eligible for updating\n     * based on the authenticated user (handled\n     * by our authentication Filter).\n     */\n    public function manage(): string\n    {\n        // Admin access sees all blogs, otherwise limit to the current user\n        if (! $this-\u003emodel-\u003emayAdmin()) {\n            $this-\u003emodel-\u003ewhere('user_id', user_id());\n        }\n\n        return view('blogs/manage', [\n            'blogs' =\u003e $this-\u003emodel-\u003efindAll(),\n        ]);\n    }\n    \n    /**\n     * Shows a single blog with options\n     * to update or delete.\n     *\n     * @return RedirectResponse|string\n     */\n    public function edit($blogId)\n    {\n        // Verify the blog\n        if (empty($blogId) || null === $blog = $this-\u003emodel-\u003efind($blogId)) {\n            return redirect()-\u003eback()-\u003ewith('error', 'Could not find that blog entry.');\n        }\n\n        // Check access\n        if (! $this-\u003emodel-\u003emayUpdate($blog)) {\n            return redirect()-\u003eback()-\u003ewith('error', 'You do not have permission to do that.');\n        }\n\n        return view('blogs/edit', [\n            'blog' =\u003e $blog,\n        ]);\n    }\n...\n```\n\nHopefully you get the idea from here! For developers who like to keep their controllers\neven more lightweight you could even put some of these checks into a Filter.\n\n## Extending\n\nThe CRUDL-style methods are just a starting point! Your models can override these built-in\nmethods or add new methods that take advantage of the library's structure and methods.\nCheck out the code in the source repo for ideas how to leverage both explicit and inferred\npermissions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftattersoftware%2Fcodeigniter4-permits","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftattersoftware%2Fcodeigniter4-permits","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftattersoftware%2Fcodeigniter4-permits/lists"}