{"id":15025116,"url":"https://github.com/asseco-voice/laravel-json-authorization","last_synced_at":"2025-04-12T13:12:46.764Z","repository":{"id":40485487,"uuid":"280736238","full_name":"asseco-voice/laravel-json-authorization","owner":"asseco-voice","description":"JSON based microservices authorization for Laravel","archived":false,"fork":false,"pushed_at":"2025-01-09T15:48:53.000Z","size":495,"stargazers_count":3,"open_issues_count":4,"forks_count":2,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-12T13:12:37.256Z","etag":null,"topics":["authorization","eloquent","eloquent-models","json","laravel","laravel-8-package","laravel-framework","laravel-package","microservice","microservices","php","scopes"],"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/asseco-voice.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}},"created_at":"2020-07-18T20:42:33.000Z","updated_at":"2023-08-23T12:01:47.000Z","dependencies_parsed_at":"2024-09-30T10:40:44.554Z","dependency_job_id":"e76c82ae-3422-47cd-98f8-900380396c25","html_url":"https://github.com/asseco-voice/laravel-json-authorization","commit_stats":{"total_commits":81,"total_committers":5,"mean_commits":16.2,"dds":"0.49382716049382713","last_synced_commit":"0123d663543d42df505f4496cb7247699349b24f"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asseco-voice%2Flaravel-json-authorization","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asseco-voice%2Flaravel-json-authorization/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asseco-voice%2Flaravel-json-authorization/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asseco-voice%2Flaravel-json-authorization/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asseco-voice","download_url":"https://codeload.github.com/asseco-voice/laravel-json-authorization/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248571873,"owners_count":21126522,"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":["authorization","eloquent","eloquent-models","json","laravel","laravel-8-package","laravel-framework","laravel-package","microservice","microservices","php","scopes"],"created_at":"2024-09-24T20:01:32.272Z","updated_at":"2025-04-12T13:12:46.744Z","avatar_url":"https://github.com/asseco-voice.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003ca href=\"https://see.asseco.com\" target=\"_blank\"\u003e\u003cimg src=\"https://github.com/asseco-voice/art/blob/main/evil_logo.png\" width=\"500\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n# Laravel JSON authorization \n\nThis package enables authorization via JSON objects imposed on each model which can be authorized.\n\nPackage is developed mainly for the purpose of multiple Laravel microservices\nauthorization having in mind to avoiding the additional trips to authorization service.\n\nThis also makes non-auth services self-contained. Authentication service should provide roles (\nor any other form of authorization), while services should provide limits that are imposed on any of\nthe roles. Should auth service ever need to be replaced, the only responsibility is to \nre-map roles on a new auth service, and role limits will stay intact.  \n\n## Why this approach?\n\nThis package offers a great flexibility for imposing rights on Eloquent models.\nWhat makes the package unique is the concept switch in a way that you do not want to protect your\nroutes, but rather **protecting the resource** itself.\n\nThis in turn results in two great benefits which the route approach doesn't have out-of-the-box:\n- calling a single endpoint doesn't mean that it operates on a single model, making it impossible\nfor the route approach to do the underlying protection for something which you meant to stay\nprotected.\n- calling a relation on a protected model doesn't protect the related model, so if you're eager/lazy loading\nsomething through Eloquent relations you have no way of protecting what is being resolved.\n\nResource protection here imposes limits you provided independently of where your request comes from.\nWe are doing that by taking advantage of Laravel scopes and Eloquent events.\n\nOf course, there are also some limitations:\n- relation will not be protected if you manually forward a ``relation_id`` to model\nI.e. ``ContactType`` has many `Contacts`. If you impose the right to only update contacts\nwith contact type ID 1, the following will still pass as valid:\n``Contacts::create([... 'contact_type_id' = 2 ...])``\n- package will try to authorize early based on the limitations provided, however on complex\nlimits imposed package will make a select on a DB which in some cases may prove to be a heavy action. \nThis mostly affects create/update/delete rights, not read ones.\n\n## Installation\n\nInstall the package through composer. It is automatically registered\nas a Laravel service provider, so no additional actions are required to register the package.\n\n``composer require asseco-voice/laravel-json-authorization``\n\n## Terminology\n\n- calling something **authorizable** means it is capable of being authorized\n- **authorizable set** - collection of authorizable user properties \n(i.e. a collection of **roles** classifies as an **authorizable set**)  \n- **authorizable set value** - single object within an **authorizable set** (i.e. a single role - `example_role_1`)\n- **authorizable set type** - logical **authorizable sets** separation\n(i.e. you can have a set of **roles**, set of **groups**... which would classify as an **authorizable set type**)\n- **authorizable model** - model upon which the authorization can be enforced\n- **right** - a single CRUD right for a single **authorizable model**, and a single **authorizable set value**\n(i.e. having a **create right** for some model)\n- **rule** - set of **rights** for a single **authorizable model**, and a single **authorizable set value**\n\n## Usage\n\nPackage initialization requires few steps to set up:\n\n1. [Pick authorizable models](#pick-authorizable-models)\n1. [Migrate tables](#migrate-tables)\n1. [Modify User](#modify-user)\n1. [Attach rules](#attach-rules)\n1. [Flush cache](#flush-cache)\n\n### Pick authorizable models\n\nModels you want protected MUST implement ``Asseco\\JsonAuthorization\\App\\Traits\\Authorizable`` trait.\n\nAfter this is done, be sure to run ``php artisan asseco:sync-authorizable-models`` to sync models which\nimplement ``Authorizable`` trait with the DB.\n\nRun this command each time you add or remove ``Authorizable`` trait from a model.\n\nIf model already has relation to some rules, the command will throw an exception. This is purposely done\nto make you manually delete rules for the models you're about to delete, so that it doesn't happen\nby accident.\n\n### Migrate tables\n\nRunning ``php artisan migrate`` will publish 3 tables:\n\n```\n    authorization_rules ----M:1--- authorizable_models\n          |\n          |\n          M\n          -\n          1\n          |\n          |\n authorizable_set_types\n```\n\n``authorizable_models`` - a list of full Eloquent (namespaced) models for \n[authorizable models](#pick-authorizable-models). This table is filled out automatically \nupon package usage but is not deleted automatically if you remove the trait after it is already written\nin the DB. Only models within ``app`` folder are scanned. In case you have a different folder \nstructure, or need to implement external models, [modify the config](#additional) ``models_path`` variable to include \nwhat you need.\n\n``authorization_rules`` - a list of [authorizable set values](#terminology) and [rules](#terminology) \nimposed on them.\n\n``authorizable_set_types`` - types represent different sets of things to authorize by. If you are\nauthorizing only by roles, then it makes sense to have only ``roles`` there, however there may be cases\nwhere you'd like to merge [authorizable set values](#terminology) from different \n[authorizable set types](#terminology) in which case you will add those as well. \n\nWith regard to the performance, everything is cached to the great extent, invalidated and re-cached\nupon change. \n\nSeeders are available to use by including `AuthorizationSeeder` (wrapper for several seeders)\nwithin your app ``DatabaseSeeder``. If needed, you can include single seeders from that class as well. \n\n### Modify User\n\nUser should implement ``AuthorizesUsers`` interface which requires you to implement a single method.\n\nThe method should return an array of [authorizable sets and their values](#terminology) for \ncurrently authenticated user.\n \nThis needs to reflect names from ``authorizable_set_types`` table as array keys, \nand [authorizable set values](#terminology) for each [authorizable set type](#terminology) set.\n\nExample:\n\n``authorizable_set_types``\n```\nID Name\n1  roles\n2  groups\n3  id\n```\n\n```\npublic function getAuthorizableSets(): array\n{\n    return [\n        'roles'  =\u003e Auth::user()-\u003eroles,\n        'groups' =\u003e Auth::user()-\u003egroups,\n        'id'     =\u003e Auth::user()-\u003eid,\n    ];\n}\n```\n\nYou don't need to implement all of these though. This is valid as well (as long as `roles` are under \n`authorizable_set_types` table):\n\n```\npublic function getAuthorizableSets(): array\n{\n    return [\n        'roles'  =\u003e Auth::user()-\u003eroles\n    ];\n}\n```\n\nDepending on where the set is coming from, you can give it any method which will return an array of \nthings to authorize by:\n\n```\npublic function getAuthorizableSets(): array\n{\n    return [\n        'roles'  =\u003e $someClass-\u003emethodCall(Auth::user()-\u003eid, 'https://my-external-service')\n    ];\n}\n```\n\nOnce resolved, function should return for example:\n\n```\nreturn [\n    'roles' =\u003e ['role1', 'role2'...],\n    'groups' =\u003e ['group1', 'group2'...],\n    ...\n]\n```\n\nIt is worth mentioning that final product is merge of role rules. \n\nExample:\n\n```\nrole 1: \"read\" right for IDs 1, 2 and 3\nrole 2: \"read\" right for IDs 4, 5 and 6\n\nFinal \"read\" right for that user are IDs 1, 2, 3, 4, 5 and 6\n```\n\n### Attach rules\n\nIf a model is [authorizable](#terminology), and no limit is present within ``authorization_rules`` table for the \ncurrently logged in user, we are assuming that user has no rights to operate on the model. \nYou are obligated to explicitly say who has the right for what. \n\nPossible rights are:\n- create\n- read\n- update\n- delete\n\nEach [authorizable set value](#terminology) will have a set of [rules](#terminology) (in JSON format) \nfor a single model. \n\nPackage is built on top of [JSON query builder](https://github.com/asseco-voice/laravel-json-query-builder)\nwhere you can check query logic in depth, with the addition of an absolute right ``*``. \n\nTo use the absolute right, you can do:\n\n```\n{\n    \"read\": \"*\"\n}\n```\n\nGiving you a read right to all rows for the given model.\n\nIn case you need some sort of admin available which has absolute rights to everything, \n[publish the configuration](#additional) and add it to the ``absolute_rights`` key, \nand you will not need to give the explicit CRUD rights for it.\n\n#### Virtual role\n\nIf you have the need to protect resources globally or give the permission for a single resource to all users\nacross the system, you can do so by utilizing a virtual role. By default, that role is \n``voice-all-mighty``, but can be overridden with `.env` value `VIRTUAL_ROLE`.\n\nA virtual role **MUST NOT** exist as a standard role within your auth service. It will conflict with this and\nwill not work well.\n\nThis works in a way that you will i.e. give a read right for some resource to **virtual role** which will \nthen be inherited by all other users.\n\nExample:\n\n```\nID  Role               Authorization model ID\n1   voice-all-mighty   1                      \n\nRules\n{\n\t\"read\": {\n\t\t\"search\": {\n\t\t\t\"id\": \"=1\"\n\t\t}\n\t}\n} \n```\n\nWill give a read right to model ``1`` to all users across the system independently of their system roles.\n\n### Flush cache\n\nDue to the heavy workload this package has to do, everything is cached with 1 day TTL. \nBe sure to flush the cache after each manual code update (i.e. you add `Asseco\\JsonAuthorization\\App\\Traits\\Authorizable` trait on a model).\n\nYou can flush the cache [the Laravel way](https://laravel.com/docs/7.x/cache#removing-items-from-the-cache),\nor if you're using Redis as your cache driver you may use [one of our packages](https://github.com/asseco-voice/laravel-redis-cache-extension)\nto enable a wildcard Redis flush.\n\n## Example\n\nLet's assume we have the following model protected:\n\n``authorizable_models``\n```\nID Name\n1  App\\Contact\n```\n\nLet's impose the rights  for a role called ``agent``\n\n``authorization_rules``\n```\nID  Role    Authorization model ID\n1   agent   1                      \n\nRules\n{\n\t\"create\": \"*\",\n\t\"read\": {\n\t\t\"search\": {\n\t\t\t\"id\": \"=1;2;3;4;5\"\n\t\t}\n\t},\n\t\"update\": {\n\t\t\"search\": {\n\t\t\t\"id\": \"=!2\"\n\t\t}\n\t}\n} \n```\n\nThese rights can be roughly translated as follows:\n- you can create a contact without limitations\n- you can only read contacts with IDs 1, 2, 3, 4 and 5. This means that calling ``Contact::all()`` will \nreturn only 5 records. Also, calling ``Contact::find(6)`` will not return the record. \n- you can update any contact with ID != 2. It is important to say that read right is a top-level right \nwhich will in start limit the possible output to 1, 2, 3, 4 and 5 effectively saying that you can \nupdate IDs 1, 3, 4, and 5. Others are forbidden through an imposed read right.\n- since delete option is omitted, you have no right for deleting any contact\n\n# Extending the package \u0026 other\n\nPublishing the configuration will enable you to change package models as\nwell as controlling how migrations behave. If extending the model, make sure\nyou're extending the original model in your implementation.\n\nFor dev purposes, you can disable authorization completely by adding this to your ``.env`` file:\n\n    OVERRIDE_AUTHORIZATION=true\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasseco-voice%2Flaravel-json-authorization","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasseco-voice%2Flaravel-json-authorization","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasseco-voice%2Flaravel-json-authorization/lists"}