├── .github └── FUNDING.yml ├── .nova └── Configuration.json ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── composer.json ├── config └── config.php ├── css └── vue-multiselect.min.css ├── database ├── migrations │ ├── .gitkeep │ ├── 0001_01_02_000001_create_governor_roles_table.php │ ├── 0001_01_02_000002_create_governor_groups_table.php │ ├── 0001_01_02_000003_create_governor_entities_table.php │ ├── 0001_01_02_000004_create_governor_actions_table.php │ ├── 0001_01_02_000005_create_governor_ownerships_table.php │ ├── 0001_01_02_000006_create_governor_role_user_table.php │ ├── 0001_01_02_000007_create_governor_teams_table.php │ ├── 0001_01_02_000008_create_governor_team_user_table.php │ ├── 0001_01_02_000009_create_governor_team_invitations_table.php │ ├── 0001_01_02_000010_create_governor_teamables_table.php │ └── 0001_01_02_000011_create_governor_permissions_table.php └── seeders │ ├── LaravelGovernorActionsTableSeeder.php │ ├── LaravelGovernorAdminSeeder.php │ ├── LaravelGovernorDatabaseSeeder.php │ ├── LaravelGovernorEntitiesTableSeeder.php │ ├── LaravelGovernorOwnershipsTableSeeder.php │ ├── LaravelGovernorRolesTableSeeder.php │ ├── LaravelGovernorSuperAdminSeeder.php │ ├── LaravelGovernorUpgradeTo0100.php │ ├── LaravelGovernorUpgradeTo0110.php │ ├── LaravelGovernorUpgradeTo0115.php │ ├── LaravelGovernorUpgradeTo0120.php │ └── OwnedBySeeder.php ├── dist ├── css │ ├── tool.css │ └── vue-multiselect.min.css ├── js │ ├── governor.js │ └── tool.js └── mix-manifest.json ├── package.json ├── phive ├── phive.xml ├── phpcs.xml ├── phpmd.xml ├── resources ├── js │ ├── components │ │ ├── Assignments.vue │ │ ├── GroupCreate.vue │ │ ├── Groups.vue │ │ ├── PermissionDetailField.vue │ │ ├── PermissionFormField.vue │ │ ├── PermissionIndexField.vue │ │ ├── Permissions.vue │ │ ├── PermissionsTool.vue │ │ ├── RoleCreate.vue │ │ └── Roles.vue │ ├── governor.js │ └── tool.js ├── sass │ └── tool.scss └── views │ ├── assignments │ └── create.blade.php │ ├── components │ ├── menu-bar-item.blade.php │ └── menu-bar.blade.php │ ├── groups │ ├── create.blade.php │ ├── edit.blade.php │ └── index.blade.php │ ├── navigation.blade.php │ ├── roles │ ├── create.blade.php │ ├── edit.blade.php │ └── index.blade.php │ └── teams │ ├── create.blade.php │ ├── edit.blade.php │ └── index.blade.php ├── routes ├── api.php ├── nova.php └── web.php ├── src ├── Action.php ├── Assignment.php ├── Console │ └── Commands │ │ └── Publish.php ├── Entity.php ├── Group.php ├── Http │ ├── Controllers │ │ ├── Api │ │ │ ├── UserCan.php │ │ │ └── UserIs.php │ │ ├── AssignmentsController.php │ │ ├── Controller.php │ │ ├── GroupsController.php │ │ ├── InvitationController.php │ │ ├── Nova │ │ │ ├── AssignmentController.php │ │ │ ├── EntityController.php │ │ │ ├── GroupController.php │ │ │ ├── PermissionController.php │ │ │ ├── RoleController.php │ │ │ ├── TeamController.php │ │ │ └── UserController.php │ │ ├── RolesController.php │ │ └── TeamsController.php │ ├── Middleware │ │ ├── Authorize.php │ │ └── ParseCustomPolicyActions.php │ └── Requests │ │ ├── CreateAssignmentRequest.php │ │ ├── CreateRoleRequest.php │ │ ├── GroupDeleteRequest.php │ │ ├── RoleUpdateRequest.php │ │ ├── StoreGroupRequest.php │ │ ├── StoreRoleRequest.php │ │ ├── TeamStoreRequest.php │ │ ├── UpdateGroupRequest.php │ │ ├── UpdateRoleRequest.php │ │ ├── UpdateTeamRequest.php │ │ ├── UserCan.php │ │ └── UserIs.php ├── Listeners │ ├── CreatedInvitationListener.php │ ├── CreatedListener.php │ ├── CreatedTeamListener.php │ ├── CreatingInvitationListener.php │ └── CreatingListener.php ├── Notifications │ └── TeamInvitation.php ├── Nova │ ├── GovernorAction.php │ ├── GovernorAssignment.php │ ├── GovernorEntity.php │ ├── GovernorGroup.php │ ├── GovernorOwnership.php │ ├── GovernorPermission.php │ ├── GovernorRole.php │ ├── GovernorTeam.php │ ├── GovernorTeamInvitation.php │ ├── GovernorUser.php │ ├── Resource.php │ └── Tools │ │ └── Governor.php ├── Ownership.php ├── Permission.php ├── PermissionField.php ├── PermissionsTool.php ├── Policies │ ├── Action.php │ ├── Assignment.php │ ├── BasePolicy.php │ ├── Entity.php │ ├── Group.php │ ├── Ownership.php │ ├── Permission.php │ ├── Role.php │ ├── Team.php │ └── TeamInvitation.php ├── Providers │ ├── Auth.php │ ├── Nova.php │ ├── Route.php │ └── Service.php ├── Role.php ├── Team.php ├── TeamInvitation.php ├── Traits │ ├── EntityManagement.php │ ├── Governable.php │ ├── Governing.php │ └── GovernorOwnedByField.php ├── User.php └── View │ └── Components │ └── MenuBar.php ├── tools ├── phpcs ├── phpmd ├── phpstan └── phpunit └── webpack.mix.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [mikebronner] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.nova/Configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "workspace.color" : 4 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "GeneaLabs", 4 | "Laravel" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | We encourage and welcome any contributions to the project created with the following guidelines. This will both ease the inclusion process, as well as ensure continued maintainability of the project. 3 | 4 | ### Before You Start 5 | Before you start, please make sure that you are aware of, and agree to, the following conditions of contribution: 6 | 7 | * By making a contribution to **Governor for Laravel**, you accept that you are granting copyright ownership for that contribution to GeneaLabs, LLC. In countries where copyright reassignment is not permitted, you grant GeneaLabs a perpetual, non-exclusive licence to use your contribution in any way and for any purpose. 8 | * By making a contribution to **Governor for Laravel**, you accept that your code will be released under the open-source MIT License. 9 | 10 | GeneaLabs is commited to providing **Governor for Laravel** as a free and open-source project in an effort to give back to the community. 11 | 12 | ### The Contribution Process 13 | 14 | 0. //describe issue create process 15 | 1. //describe fork process 16 | 2. //describe branching process in fork for each feature you work on 17 | 3. //describe pull request process 18 | 4. //describe review process 19 | 5. //describe merge process. 20 | 21 | ### Coding Standards 22 | All pull requests must satisfy the following requirements: 23 | * PSR-1, PSR-2 compliance for all code 24 | * PSR-4 compliance for all namespaces 25 | * Doc-blocks for all classes and methods 26 | * README updated for any changes in functionality. 27 | 28 | If you're working on files that don't satisfy these requirements, bring them up to par while you're working on them. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 GeneaLabs, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Until we publish release 1.0.0 only the latest version will receive security 6 | updates. Once at release 1.0, we will evaluate a sustainable security update 7 | policy moving forward. 8 | 9 | | Version | Supported | 10 | | -------- | ------------------ | 11 | | 0.10.x | :white_check_mark: | 12 | | < 0.10.0 | :x: | 13 | 14 | ## Reporting a Vulnerability 15 | 16 | If you believe you have found a security vulnerability that pertains directly 17 | to this package, and not one of its dependencies, please reach out to us 18 | directly via email at hello@genealabs.com. 19 | 20 | Please include the package version, Laravel version, and complete steps to 21 | recreate the attack. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "genealabs/laravel-governor", 3 | "description": "Managing policy and control in Laravel.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "GeneaLabs, LLC", 8 | "email": "hello@genealabs.com" 9 | } 10 | ], 11 | "require": { 12 | "illuminate/database": "^9.0|^10.0|^11.0|^12.0", 13 | "illuminate/support": "^9.0|^10.0|^11.0|^12.0", 14 | "php": "^7.3|^8.0", 15 | "ramsey/uuid": "^4.0" 16 | }, 17 | "require-dev": { 18 | "doctrine/dbal": "^2.9", 19 | "fakerphp/faker": "^1.23", 20 | "laravel/legacy-factories": "^1.0", 21 | "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", 22 | "phpunit/phpunit": "^9.5|^10.0|^11.0", 23 | "symfony/thanks": "^1.2" 24 | }, 25 | "autoload": { 26 | "classmap": [ 27 | "database/migrations" 28 | ], 29 | "psr-4": { 30 | "GeneaLabs\\LaravelGovernor\\": "src/", 31 | "GeneaLabs\\LaravelGovernor\\Database\\Factories\\": "database/factories/", 32 | "GeneaLabs\\LaravelGovernor\\Database\\Seeders\\": "database/seeders/" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "classmap": [ 37 | "tests/database/migrations" 38 | ], 39 | "psr-4": { 40 | "GeneaLabs\\LaravelGovernor\\Tests\\": "tests/", 41 | "App\\": "tests/Fixtures/App/" 42 | } 43 | }, 44 | "extra": { 45 | "laravel": { 46 | "providers": [ 47 | "GeneaLabs\\LaravelGovernor\\Providers\\Auth", 48 | "GeneaLabs\\LaravelGovernor\\Providers\\Nova", 49 | "GeneaLabs\\LaravelGovernor\\Providers\\Route", 50 | "GeneaLabs\\LaravelGovernor\\Providers\\Service" 51 | ] 52 | } 53 | }, 54 | "config": { 55 | "sort-packages": true, 56 | "preferred-install": "dist", 57 | "allow-plugins": { 58 | "symfony/thanks": true, 59 | "cweagans/composer-patches": true 60 | } 61 | }, 62 | "minimum-stability": "dev", 63 | "prefer-stable": true 64 | } 65 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | 'layouts.app', 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Layout Content Section Name 19 | |-------------------------------------------------------------------------- 20 | | 21 | | Specify the name of the section in the view referenced above that is 22 | | used to render the main page content. If this does not match, you 23 | | will only get blank pages when accessing views in Governor. 24 | */ 25 | 'content-section' => 'content', 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Authorization Model 30 | |-------------------------------------------------------------------------- 31 | | 32 | | Here you can customize what model should be used for authorization checks 33 | | in the event that you have customized your authentication processes. 34 | */ 35 | 'auth-model-primary-key-type' => 'bigInteger', 36 | "models" => [ 37 | "auth" => config('auth.providers.users.model') 38 | ?? config('auth.model'), 39 | "action" => GeneaLabs\LaravelGovernor\Action::class, 40 | "assignment" => GeneaLabs\LaravelGovernor\Assignment::class, 41 | "entity" => GeneaLabs\LaravelGovernor\Entity::class, 42 | "group" => GeneaLabs\LaravelGovernor\Group::class, 43 | "ownership" => GeneaLabs\LaravelGovernor\Ownership::class, 44 | "permission" => GeneaLabs\LaravelGovernor\Permission::class, 45 | "role" => GeneaLabs\LaravelGovernor\Role::class, 46 | "team" => GeneaLabs\LaravelGovernor\Team::class, 47 | "invitation" => GeneaLabs\LaravelGovernor\TeamInvitation::class, 48 | ], 49 | 50 | /* 51 | |-------------------------------------------------------------------------- 52 | | User Model Name Property 53 | |-------------------------------------------------------------------------- 54 | | 55 | | This value is used to display your users when assigning them to roles. 56 | | You can choose any property of your auth-model defined above that is 57 | | exposed via JSON. 58 | */ 59 | 'user-name-property' => 'name', 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | URL Prefix 64 | |-------------------------------------------------------------------------- 65 | | 66 | | If you want to change the URL used by the browser to access the admin 67 | | pages, you can do so here. Be careful to avoid collisions with any 68 | | existing URLs of your app when doing so. 69 | */ 70 | 'url-prefix' => '/genealabs/laravel-governor/', 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | Default SuperAdmin User 75 | |-------------------------------------------------------------------------- 76 | | 77 | | You may optionally specify a set of SuperAdmin and Admin users that will 78 | | be created if they don't already exist, formatted as JSON. 79 | | Example: [{"name":"Joe Doe","email":"joe@example.com","password":"secret1"},{"name":"Jane Doe","email":"jane@example.com","password":"shhhhh1"}] 80 | */ 81 | "superadmins" => env("GOVERNOR_SUPERADMINS"), 82 | "admins" => env("GOVERNOR_ADMINS"), 83 | 84 | ]; 85 | -------------------------------------------------------------------------------- /css/vue-multiselect.min.css: -------------------------------------------------------------------------------- 1 | fieldset[disabled] .multiselect{pointer-events:none}.multiselect__spinner{position:absolute;right:1px;top:1px;width:48px;height:35px;background:#fff;display:block}.multiselect__spinner:after,.multiselect__spinner:before{position:absolute;content:"";top:50%;left:50%;margin:-8px 0 0 -8px;width:16px;height:16px;border-radius:100%;border:2px solid transparent;border-top-color:#41b883;box-shadow:0 0 0 1px transparent}.multiselect__spinner:before{animation:spinning 2.4s cubic-bezier(.41,.26,.2,.62);animation-iteration-count:infinite}.multiselect__spinner:after{animation:spinning 2.4s cubic-bezier(.51,.09,.21,.8);animation-iteration-count:infinite}.multiselect__loading-enter-active,.multiselect__loading-leave-active{transition:opacity .4s ease-in-out;opacity:1}.multiselect__loading-enter,.multiselect__loading-leave-active{opacity:0}.multiselect,.multiselect__input,.multiselect__single{font-family:inherit;font-size:16px;-ms-touch-action:manipulation;touch-action:manipulation}.multiselect{box-sizing:content-box;display:block;position:relative;width:100%;min-height:40px;text-align:left;color:#35495e}.multiselect *{box-sizing:border-box}.multiselect:focus{outline:none}.multiselect--disabled{background:#ededed;pointer-events:none;opacity:.6}.multiselect--active{z-index:50}.multiselect--active:not(.multiselect--above) .multiselect__current,.multiselect--active:not(.multiselect--above) .multiselect__input,.multiselect--active:not(.multiselect--above) .multiselect__tags{border-bottom-left-radius:0;border-bottom-right-radius:0}.multiselect--active .multiselect__select{transform:rotate(180deg)}.multiselect--above.multiselect--active .multiselect__current,.multiselect--above.multiselect--active .multiselect__input,.multiselect--above.multiselect--active .multiselect__tags{border-top-left-radius:0;border-top-right-radius:0}.multiselect__input,.multiselect__single{position:relative;display:inline-block;min-height:20px;line-height:20px;border:none;border-radius:5px;background:#fff;padding:0 0 0 5px;width:100%;transition:border .1s ease;box-sizing:border-box;margin-bottom:8px;vertical-align:top}.multiselect__input:-ms-input-placeholder{color:#35495e}.multiselect__input::placeholder{color:#35495e}.multiselect__tag~.multiselect__input,.multiselect__tag~.multiselect__single{width:auto}.multiselect__input:hover,.multiselect__single:hover{border-color:#cfcfcf}.multiselect__input:focus,.multiselect__single:focus{border-color:#a8a8a8;outline:none}.multiselect__single{padding-left:5px;margin-bottom:8px}.multiselect__tags-wrap{display:inline}.multiselect__tags{min-height:40px;display:block;padding:8px 40px 0 8px;border-radius:5px;border:1px solid #e8e8e8;background:#fff;font-size:14px}.multiselect__tag{position:relative;display:inline-block;padding:4px 26px 4px 10px;border-radius:5px;margin-right:10px;color:#fff;line-height:1;background:#41b883;margin-bottom:5px;white-space:nowrap;overflow:hidden;max-width:100%;text-overflow:ellipsis}.multiselect__tag-icon{cursor:pointer;margin-left:7px;position:absolute;right:0;top:0;bottom:0;font-weight:700;font-style:normal;width:22px;text-align:center;line-height:22px;transition:all .2s ease;border-radius:5px}.multiselect__tag-icon:after{content:"\D7";color:#266d4d;font-size:14px}.multiselect__tag-icon:focus,.multiselect__tag-icon:hover{background:#369a6e}.multiselect__tag-icon:focus:after,.multiselect__tag-icon:hover:after{color:#fff}.multiselect__current{min-height:40px;overflow:hidden;padding:8px 30px 0 12px;white-space:nowrap;border-radius:5px;border:1px solid #e8e8e8}.multiselect__current,.multiselect__select{line-height:16px;box-sizing:border-box;display:block;margin:0;text-decoration:none;cursor:pointer}.multiselect__select{position:absolute;width:40px;height:38px;right:1px;top:1px;padding:4px 8px;text-align:center;transition:transform .2s ease}.multiselect__select:before{position:relative;right:0;top:65%;color:#999;margin-top:4px;border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 0;content:""}.multiselect__placeholder{color:#adadad;display:inline-block;margin-bottom:10px;padding-top:2px}.multiselect--active .multiselect__placeholder{display:none}.multiselect__content-wrapper{position:absolute;display:block;background:#fff;width:100%;max-height:240px;overflow:auto;border:1px solid #e8e8e8;border-top:none;border-bottom-left-radius:5px;border-bottom-right-radius:5px;z-index:50;-webkit-overflow-scrolling:touch}.multiselect__content{list-style:none;display:inline-block;padding:0;margin:0;min-width:100%;vertical-align:top}.multiselect--above .multiselect__content-wrapper{bottom:100%;border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:5px;border-top-right-radius:5px;border-bottom:none;border-top:1px solid #e8e8e8}.multiselect__content::webkit-scrollbar{display:none}.multiselect__element{display:block}.multiselect__option{display:block;padding:12px;min-height:40px;line-height:16px;text-decoration:none;text-transform:none;vertical-align:middle;position:relative;cursor:pointer;white-space:nowrap}.multiselect__option:after{top:0;right:0;position:absolute;line-height:40px;padding-right:12px;padding-left:20px;font-size:13px}.multiselect__option--highlight{background:#41b883;outline:none;color:#fff}.multiselect__option--highlight:after{content:attr(data-select);background:#41b883;color:#fff}.multiselect__option--selected{background:#f3f3f3;color:#35495e;font-weight:700}.multiselect__option--selected:after{content:attr(data-selected);color:silver}.multiselect__option--selected.multiselect__option--highlight{background:#ff6a6a;color:#fff}.multiselect__option--selected.multiselect__option--highlight:after{background:#ff6a6a;content:attr(data-deselect);color:#fff}.multiselect--disabled .multiselect__current,.multiselect--disabled .multiselect__select{background:#ededed;color:#a6a6a6}.multiselect__option--disabled{background:#ededed!important;color:#a6a6a6!important;cursor:text;pointer-events:none}.multiselect__option--group{background:#ededed;color:#35495e}.multiselect__option--group.multiselect__option--highlight{background:#35495e;color:#fff}.multiselect__option--group.multiselect__option--highlight:after{background:#35495e}.multiselect__option--disabled.multiselect__option--highlight{background:#dedede}.multiselect__option--group-selected.multiselect__option--highlight{background:#ff6a6a;color:#fff}.multiselect__option--group-selected.multiselect__option--highlight:after{background:#ff6a6a;content:attr(data-deselect);color:#fff}.multiselect-enter-active,.multiselect-leave-active{transition:all .15s ease}.multiselect-enter,.multiselect-leave-active{opacity:0}.multiselect__strong{margin-bottom:8px;line-height:20px;display:inline-block;vertical-align:top}[dir=rtl] .multiselect{text-align:right}[dir=rtl] .multiselect__select{right:auto;left:1px}[dir=rtl] .multiselect__tags{padding:8px 8px 0 40px}[dir=rtl] .multiselect__content{text-align:right}[dir=rtl] .multiselect__option:after{right:auto;left:0}[dir=rtl] .multiselect__clear{right:auto;left:12px}[dir=rtl] .multiselect__spinner{right:auto;left:1px}@keyframes spinning{0%{transform:rotate(0)}to{transform:rotate(2turn)}} -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/database/migrations/.gitkeep -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000001_create_governor_roles_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_roles', function (Blueprint $table): void { 20 | $table->string('name')->unique()->primary(); 21 | $table->string('description')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | public function down(): void 27 | { 28 | Schema::drop('governor_roles'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000002_create_governor_groups_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_groups', function (Blueprint $table): void { 20 | $table->string('name') 21 | ->unique() 22 | ->primary(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | public function down(): void 28 | { 29 | Schema::drop('governor_groups'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000003_create_governor_entities_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_entities', function (Blueprint $table): void { 20 | $table->string('name') 21 | ->unique() 22 | ->primary(); 23 | $table->string('group_name')->nullable(); 24 | $table->timestamps(); 25 | $table->foreign("group_name") 26 | ->references("name") 27 | ->on("governor_groups") 28 | ->onUpdate("CASCADE") 29 | ->onDelete("SET NULL"); 30 | }); 31 | } 32 | 33 | public function down(): void 34 | { 35 | Schema::drop('governor_entities'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000004_create_governor_actions_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_actions', function (Blueprint $table): void { 20 | $table->string('name') 21 | ->unique() 22 | ->primary(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | public function down(): void 28 | { 29 | Schema::drop('governor_actions'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000005_create_governor_ownerships_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_ownerships', function (Blueprint $table): void { 20 | $table->string('name') 21 | ->unique() 22 | ->primary(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | public function down(): void 28 | { 29 | Schema::drop('governor_ownerships'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000006_create_governor_role_user_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_role_user', function (Blueprint $table): void { 20 | $user = app()->make(config('genealabs-laravel-governor.models.auth')); 21 | $table->bigIncrements("id"); 22 | $table->string('role_name'); 23 | $table->unsignedBigInteger('user_id'); 24 | $table->timestamps(); 25 | 26 | $table->unique(["role_name", "user_id"]); 27 | $table->foreign('role_name') 28 | ->references('name') 29 | ->on('governor_roles') 30 | ->onDelete('CASCADE') 31 | ->onUpdate('CASCADE'); 32 | $table->foreign('user_id') 33 | ->references($user->getKeyName()) 34 | ->on($user->getTable()) 35 | ->onDelete('CASCADE') 36 | ->onUpdate('CASCADE'); 37 | }); 38 | } 39 | 40 | public function down(): void 41 | { 42 | Schema::drop('governor_role_user'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000007_create_governor_teams_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 16 | $this->connection = config("tenancy.db.tenant-connection-name"); 17 | } 18 | 19 | $this->userInstance = app(config('genealabs-laravel-governor.models.auth')); 20 | } 21 | 22 | public function up(): void 23 | { 24 | Schema::create('governor_teams', function (Blueprint $table): void { 25 | $table->bigIncrements("id"); 26 | $table->unsignedBigInteger("governor_owned_by") 27 | ->nullable(); 28 | $table->timestamps(); 29 | $table->softDeletes(); 30 | 31 | $table->string('name'); 32 | $table->text("description") 33 | ->nullable(); 34 | 35 | $table->foreign('governor_owned_by') 36 | ->references($this->userInstance->getKeyName()) 37 | ->on($this->userInstance->getTable()) 38 | ->onDelete('SET NULL') 39 | ->onUpdate('CASCADE'); 40 | }); 41 | 42 | Schema::table($this->userInstance->getTable(), function (Blueprint $table): void { 43 | $table->foreignIdFor(Team::class, "current_team_id") 44 | ->nullable() 45 | ->constrained() 46 | ->cascadeOnDelete() 47 | ->cascadeOnUpdate(); 48 | }); 49 | } 50 | 51 | public function down(): void 52 | { 53 | Schema::drop('governor_teams'); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000008_create_governor_team_user_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_team_user', function (Blueprint $table): void { 20 | $user = app(config('genealabs-laravel-governor.models.auth')); 21 | 22 | $table->unsignedBigInteger('team_id') 23 | ->index(); 24 | $table->unsignedBigInteger('user_id') 25 | ->index(); 26 | $table->timestamps(); 27 | 28 | $table->foreign('team_id') 29 | ->references('id') 30 | ->on('governor_teams') 31 | ->onDelete('CASCADE') 32 | ->onUpdate('CASCADE'); 33 | $table->foreign('user_id') 34 | ->references($user->getKeyName()) 35 | ->on($user->getTable()) 36 | ->onDelete('CASCADE') 37 | ->onUpdate('CASCADE'); 38 | }); 39 | } 40 | 41 | public function down(): void 42 | { 43 | Schema::drop('governor_team_user'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000009_create_governor_team_invitations_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | 20 | Schema::create('governor_team_invitations', function (Blueprint $table): void { 21 | $user = app(config('genealabs-laravel-governor.models.auth')); 22 | 23 | $table->bigIncrements("id"); 24 | $table->unsignedBigInteger("governor_owned_by") 25 | ->nullable(); 26 | $table->unsignedBigInteger('team_id'); 27 | $table->timestamps(); 28 | 29 | $table->string("email"); 30 | $table->string("token"); 31 | 32 | $table->foreign('team_id') 33 | ->references('id') 34 | ->on('governor_teams') 35 | ->onDelete('CASCADE') 36 | ->onUpdate('CASCADE'); 37 | }); 38 | } 39 | 40 | public function down(): void 41 | { 42 | Schema::drop('governor_team_invitations'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000010_create_governor_teamables_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_teamables', function (Blueprint $table): void { 20 | $table->unsignedBigInteger('team_id'); 21 | $table->unsignedBigInteger('teamable_id'); 22 | $table->string("teamable_type"); 23 | $table->timestamps(); 24 | 25 | $table->foreign('team_id') 26 | ->references('id') 27 | ->on('governor_teams') 28 | ->onDelete('CASCADE') 29 | ->onUpdate('CASCADE'); 30 | }); 31 | } 32 | 33 | public function down(): void 34 | { 35 | Schema::drop('governor_teamables'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/0001_01_02_000011_create_governor_permissions_table.php: -------------------------------------------------------------------------------- 1 | bound("Hyn\Tenancy\Environment")) { 13 | $this->connection = config("tenancy.db.tenant-connection-name"); 14 | } 15 | } 16 | 17 | public function up(): void 18 | { 19 | Schema::create('governor_permissions', function (Blueprint $table): void { 20 | $table->bigIncrements('id'); 21 | $table->string('role_name') 22 | ->nullable(); 23 | $table->string('entity_name'); 24 | $table->string('action_name'); 25 | $table->string('ownership_name'); 26 | $table->unsignedBigInteger("team_id") 27 | ->nullable(); 28 | $table->timestamps(); 29 | 30 | $table->unique(['role_name', 'entity_name', 'action_name', 'ownership_name'], 'governor_permissions_unique_key'); 31 | $table->foreign('role_name') 32 | ->references('name') 33 | ->on('governor_roles') 34 | ->onDelete('CASCADE') 35 | ->onUpdate('CASCADE'); 36 | $table->foreign('entity_name') 37 | ->references('name') 38 | ->on('governor_entities') 39 | ->onDelete('CASCADE') 40 | ->onUpdate('CASCADE'); 41 | $table->foreign('action_name') 42 | ->references('name') 43 | ->on('governor_actions') 44 | ->onDelete('CASCADE') 45 | ->onUpdate('CASCADE'); 46 | $table->foreign('ownership_name') 47 | ->references('name') 48 | ->on('governor_ownerships') 49 | ->onDelete('CASCADE') 50 | ->onUpdate('CASCADE'); 51 | $table->foreign('team_id') 52 | ->references('id') 53 | ->on('governor_teams') 54 | ->onDelete('SET NULL') 55 | ->onUpdate('CASCADE'); 56 | }); 57 | } 58 | 59 | public function down(): void 60 | { 61 | Schema::drop('governor_permissions'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorActionsTableSeeder.php: -------------------------------------------------------------------------------- 1 | firstOrCreate(['name' => 'create']); 14 | $action->firstOrCreate(['name' => 'delete']); 15 | $action->firstOrCreate(['name' => 'forceDelete']); 16 | $action->firstOrCreate(['name' => 'restore']); 17 | $action->firstOrCreate(['name' => 'update']); 18 | $action->firstOrCreate(['name' => 'view']); 19 | $action->firstOrCreate(['name' => 'viewAny']); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorAdminSeeder.php: -------------------------------------------------------------------------------- 1 | make(config('genealabs-laravel-governor.models.auth')); 12 | $roleClass = config("genealabs-laravel-governor.models.role"); 13 | $adminRole = (new $roleClass)->find("Admin"); 14 | $memberRole = (new $roleClass)->find("Member"); 15 | $admins = config('genealabs-laravel-governor.admins'); 16 | 17 | if (! $admins) { 18 | return; 19 | } 20 | 21 | $admins = json_decode($admins); 22 | 23 | if (! is_array($admins)) { 24 | return; 25 | } 26 | 27 | foreach ($admins as $admin) { 28 | if ($admin->email) { 29 | $adminUser = $users 30 | ->firstOrNew([ 31 | "email" => $admin->email, 32 | ]); 33 | 34 | if (!$adminUser->exists) { 35 | $adminUser->fill([ 36 | "name" => $admin->name, 37 | "password" => bcrypt($admin->password), 38 | ]); 39 | $adminUser->save(); 40 | } 41 | 42 | $adminUser->roles()->syncWithoutDetaching([$adminRole->name, $memberRole->name]); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorDatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call(LaravelGovernorEntitiesTableSeeder::class); 15 | $this->call(LaravelGovernorActionsTableSeeder::class); 16 | $this->call(LaravelGovernorOwnershipsTableSeeder::class); 17 | $this->call(LaravelGovernorRolesTableSeeder::class); 18 | $this->call(LaravelGovernorSuperAdminSeeder::class); 19 | $this->call(LaravelGovernorAdminSeeder::class); 20 | $this->call(OwnedBySeeder::class); 21 | 22 | Model::reguard(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorEntitiesTableSeeder.php: -------------------------------------------------------------------------------- 1 | parsePolicies(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorOwnershipsTableSeeder.php: -------------------------------------------------------------------------------- 1 | firstOrCreate(['name' => 'any']); 13 | (new $ownershipClass)->firstOrCreate(['name' => 'own']); 14 | (new $ownershipClass)->firstOrCreate(['name' => 'other']); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorRolesTableSeeder.php: -------------------------------------------------------------------------------- 1 | firstOrCreate([ 14 | 'name' => 'SuperAdmin', 15 | ]) 16 | ->fill([ 17 | 'description' => 'This role is for the main super-administrator of your site. They will be able to do absolutely everything. (This role cannot be edited.)', 18 | ]); 19 | (new $roleClass) 20 | ->firstOrCreate([ 21 | 'name' => 'Admin', 22 | ]) 23 | ->fill([ 24 | 'description' => 'This role is for the administrators of your site. You can configure what they have access to.', 25 | ]); 26 | (new $roleClass) 27 | ->firstOrCreate([ 28 | 'name' => 'Member', 29 | ]) 30 | ->fill([ 31 | 'description' => 'Represents the baseline registered user. Customize permissions as best suits your site.', 32 | ]); 33 | (new $roleClass) 34 | ->firstOrCreate([ 35 | 'name' => 'Guest', 36 | ]) 37 | ->fill([ 38 | 'description' => 'Represents the any user visiting the site that is not logged in.', 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorSuperAdminSeeder.php: -------------------------------------------------------------------------------- 1 | make(config('genealabs-laravel-governor.models.auth')); 12 | $roleClass = config("genealabs-laravel-governor.models.role"); 13 | $superAdminRole = (new $roleClass)->find("SuperAdmin"); 14 | $memberRole = (new $roleClass)->find("Member"); 15 | $superAdmins = config('genealabs-laravel-governor.superadmins'); 16 | 17 | if (! $superAdmins) { 18 | return; 19 | } 20 | 21 | $superAdmins = json_decode($superAdmins); 22 | 23 | if (! is_array($superAdmins)) { 24 | return; 25 | } 26 | 27 | foreach ($superAdmins as $superAdmin) { 28 | if ($superAdmin->email) { 29 | $superuser = $users 30 | ->firstOrNew([ 31 | "email" => $superAdmin->email, 32 | ]); 33 | 34 | if (!$superuser->exists) { 35 | $superuser->fill([ 36 | "name" => $superAdmin->name, 37 | "password" => bcrypt($superAdmin->password), 38 | ]); 39 | $superuser->save(); 40 | } 41 | $superuser->roles()->syncWithoutDetaching([$superAdminRole->name, $memberRole->name]); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorUpgradeTo0100.php: -------------------------------------------------------------------------------- 1 | table("entities") 17 | ->get() 18 | ->each(function ($entity) { 19 | (new Entity)->firstOrCreate([ 20 | "name" => $entity->name, 21 | ]); 22 | }); 23 | } 24 | 25 | if (Schema::hasTable('permissions')) { 26 | app("db") 27 | ->table("permissions") 28 | ->where("role_key", "NOT LIKE", "SuperAdmin") 29 | ->get() 30 | ->each(function ($permission) { 31 | (new Permission)->firstOrCreate([ 32 | "role_name" => $permission->role_key, 33 | "action_name" => $permission->action_key, 34 | "entity_name" => $permission->entity_key, 35 | "ownership_name" => $permission->ownership_key, 36 | ]); 37 | }); 38 | } 39 | 40 | if (Schema::hasTable('role_user')) { 41 | app("db") 42 | ->table("role_user") 43 | ->get() 44 | ->each(function ($roleUser) { 45 | (new Role) 46 | ->find($roleUser->role_key) 47 | ->users() 48 | ->syncWithoutDetaching($roleUser->user_id); 49 | }); 50 | } 51 | 52 | if (Schema::hasTable('role_user')) { 53 | Schema::drop('role_user'); 54 | } 55 | 56 | if (Schema::hasTable('permissions')) { 57 | Schema::drop('permissions'); 58 | } 59 | 60 | if (Schema::hasTable('roles')) { 61 | Schema::drop('roles'); 62 | } 63 | 64 | if (Schema::hasTable('actions')) { 65 | Schema::drop('actions'); 66 | } 67 | 68 | if (Schema::hasTable('entities')) { 69 | Schema::drop('entities'); 70 | } 71 | 72 | if (Schema::hasTable('ownerships')) { 73 | Schema::drop('ownerships'); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorUpgradeTo0110.php: -------------------------------------------------------------------------------- 1 | getTableNames() 15 | ->each(function ($tableName) { 16 | if (! Schema::hasColumn($tableName, 'governor_created_by')) { 17 | return; 18 | } 19 | 20 | if (Schema::hasColumn($tableName, "governor_owned_by")) { 21 | app("db") 22 | ->table($tableName) 23 | ->where("governor_created_by", ">", 0) 24 | ->update([ 25 | "governor_owned_by" => app("db")->raw("governor_created_by"), 26 | ]); 27 | Schema::table($tableName, function (Blueprint $table) { 28 | $table->dropColumn("governor_created_by"); 29 | }); 30 | 31 | return; 32 | } 33 | 34 | Schema::table($tableName, function (Blueprint $table) { 35 | $table->renameColumn("governor_created_by", "governor_owned_by"); 36 | }); 37 | }); 38 | } 39 | 40 | protected function getTableNames() : Collection 41 | { 42 | return collect(app("db") 43 | ->getDoctrineSchemaManager() 44 | ->listTableNames()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorUpgradeTo0115.php: -------------------------------------------------------------------------------- 1 | dropIndexIfExists("governor_permissions_role_name_entity_name_action_name_ownership_name_unique"); 15 | }); 16 | Schema::table("governor_permissions", function (Blueprint $table) { 17 | $table->string("role_name") 18 | ->nullable() 19 | ->change(); 20 | if (! Schema::hasColumn("governor_permissions", "team_id")) { 21 | $table->unsignedBigInteger("team_id") 22 | ->nullable(); 23 | $table->unique([ 24 | "team_id", 25 | 'role_name', 26 | 'entity_name', 27 | 'action_name', 28 | 'ownership_name', 29 | ]); 30 | } 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/seeders/LaravelGovernorUpgradeTo0120.php: -------------------------------------------------------------------------------- 1 | bigIncrements("id"); 15 | } 16 | 17 | $table->string('role_name') 18 | ->change(); 19 | $table->unsignedBigInteger('user_id') 20 | ->change(); 21 | 22 | if (! Schema::hasColumn("governor_role_user", "created_at")) { 23 | $table->timestamps(); 24 | } 25 | // $table->dropIndex("role_name"); 26 | $table->dropIndexIfExists("governor_role_user_role_name_index"); 27 | // $table->dropIndex("user_id"); 28 | $table->dropIndexIfExists("governor_role_user_user_id_index"); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/seeders/OwnedBySeeder.php: -------------------------------------------------------------------------------- 1 | getModels() 24 | ->each(function ($model) { 25 | $this->createGovernorOwnedByFields(new $model); 26 | }); 27 | } 28 | 29 | protected function getModels(): Collection 30 | { 31 | return collect(File::allFiles(app_path())) 32 | ->map(function ($item) { 33 | $path = $item->getRelativePathName(); 34 | $class = sprintf( 35 | '\%s%s', 36 | Container::getInstance()->getNamespace(), 37 | strtr(substr($path, 0, strrpos($path, '.')), '/', '\\'), 38 | ); 39 | 40 | return $class; 41 | }) 42 | ->filter(function ($class) { 43 | $valid = false; 44 | 45 | if (class_exists($class)) { 46 | $reflection = new \ReflectionClass($class); 47 | $valid = $reflection->isSubclassOf(Model::class) && 48 | !$reflection->isAbstract(); 49 | } 50 | 51 | return $valid; 52 | }) 53 | ->values(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /dist/css/tool.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/dist/css/tool.css -------------------------------------------------------------------------------- /dist/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/governor.js": "/js/governor.js?id=1ec7efb2658283980aa5", 3 | "/js/tool.js": "/js/tool.js?id=71a030179021e2a8397c", 4 | "/css/tool.css": "/css/tool.css?id=d41d8cd98f00b204e980", 5 | "/css/vue-multiselect.min.css": "/css/vue-multiselect.min.css?id=852f8d3449dafd2df542" 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "npm run development -- --watch", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "alpinejs": "^2.7.3", 14 | "cross-env": "^5.1", 15 | "laravel-mix": "4.*", 16 | "laravel-nova": "^1.0.9", 17 | "lodash": "^4.17.15", 18 | "resolve-url-loader": "2.3.1", 19 | "sass-loader": "7.*", 20 | "sass": "^1.15.2", 21 | "tailwindcss": "^2.0.1", 22 | "vue-multiselect": "*", 23 | "vue-template-compiler": "^2.5.21", 24 | "vue": "^2.5.17" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /phive: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/phive -------------------------------------------------------------------------------- /phive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | GeneaLabs coding standards. 4 | 5 | 6 | 7 | 8 | 9 | 0 10 | 11 | 12 | -------------------------------------------------------------------------------- /phpmd.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | Custom ruleset for Laravel. 11 | 12 | 13 | docker 14 | public 15 | node_modules 16 | vendor 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 50 33 | 34 | 35 | 36 | 37 | 38 | 39 | 2 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /resources/js/components/Assignments.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 97 | 98 | 111 | -------------------------------------------------------------------------------- /resources/js/components/PermissionDetailField.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /resources/js/components/PermissionFormField.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 48 | -------------------------------------------------------------------------------- /resources/js/components/PermissionIndexField.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /resources/js/components/RoleCreate.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 80 | 81 | 94 | -------------------------------------------------------------------------------- /resources/js/components/Roles.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 91 | 92 | 97 | -------------------------------------------------------------------------------- /resources/js/governor.js: -------------------------------------------------------------------------------- 1 | import "alpinejs"; 2 | -------------------------------------------------------------------------------- /resources/js/tool.js: -------------------------------------------------------------------------------- 1 | global._ = require('lodash'); 2 | 3 | Nova.booting((Vue, router) => { 4 | Vue.component('index-permission', require('./components/PermissionIndexField')) 5 | Vue.component('detail-permission', require('./components/PermissionDetailField')) 6 | Vue.component('form-permission', require('./components/PermissionFormField')) 7 | Vue.component("multiselect", require("vue-multiselect").default); 8 | Vue.component('permissions-tool', require('./components/PermissionsTool').default); 9 | Vue.component("permissions", require('./components/Permissions').default); 10 | router.addRoutes([ 11 | { 12 | name: 'laravel-nova-governor-roles', 13 | path: '/laravel-nova-governor/roles', 14 | component: require('./components/Roles').default, 15 | }, 16 | { 17 | name: 'laravel-nova-governor-role-create', 18 | path: '/laravel-nova-governor/roles/create', 19 | component: require('./components/RoleCreate').default, 20 | }, 21 | { 22 | name: 'laravel-nova-governor-permissions', 23 | path: '/laravel-nova-governor/permissions/:role', 24 | component: require('./components/Permissions').default, 25 | }, 26 | { 27 | name: 'laravel-nova-governor-groups', 28 | path: '/laravel-nova-governor/groups', 29 | component: require('./components/Groups').default, 30 | }, 31 | { 32 | name: 'laravel-nova-governor-groups-create', 33 | path: '/laravel-nova-governor/groups/create/', 34 | component: require('./components/GroupCreate').default, 35 | }, 36 | { 37 | name: 'laravel-nova-governor-groups-edit', 38 | path: '/laravel-nova-governor/groups/:group', 39 | component: require('./components/GroupCreate').default, 40 | }, 41 | { 42 | name: 'laravel-nova-governor-assignments', 43 | path: '/laravel-nova-governor/assignments', 44 | component: require('./components/Assignments').default, 45 | }, 46 | ]) 47 | }) 48 | -------------------------------------------------------------------------------- /resources/sass/tool.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/resources/sass/tool.scss -------------------------------------------------------------------------------- /resources/views/assignments/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends (config("genealabs-laravel-governor.layout-view")) 2 | 3 | @section ("content") 4 |
5 | 6 | 7 |
8 |
9 |

10 | Assignments 11 |

12 |
13 |
14 | 15 | 16 | 21 | @errors() 22 | 23 |
24 |
25 |
26 | 27 | 28 | 29 | 32 | 38 | 39 | 40 | 43 | 44 | @foreach ($roles as $role) 45 | 46 | 51 | 63 | 64 | @endforeach 65 | 66 | 67 |
30 | Role 31 | 36 | Assignees 37 |
49 | {{ $role->name }} 50 | 54 | 62 |
68 |
69 | 70 | 74 |
75 |
76 |
77 |
78 | @endsection 79 | -------------------------------------------------------------------------------- /resources/views/components/menu-bar-item.blade.php: -------------------------------------------------------------------------------- 1 | @if (\Str::startsWith(request()->url(), $url)) 2 | 7 | {{ $label }} 8 | 9 | @else 10 | 14 | {{ $label }} 15 | 16 | @endif 17 | -------------------------------------------------------------------------------- /resources/views/components/menu-bar.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 15 |
16 | 28 |
29 | -------------------------------------------------------------------------------- /resources/views/groups/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends (config("genealabs-laravel-governor.layout-view")) 2 | 3 | @section ("content") 4 |
5 | 6 | 7 |
8 |
9 |

10 | Create a new Group 11 |

12 |
13 |
14 | 15 | 16 | 17 | 22 | 27 |
30 |
31 |
32 |

General

33 |

34 | Name your group according to the purpose it will fulfill. 35 |

36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 48 |
49 |
50 |
51 |
52 |
53 |
54 | 59 |
60 |
61 |
62 |
63 |

Entities

64 |

65 | Select all entities that are part of this functional grouping. 66 |

67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 80 |
81 |
82 |
83 |
84 | 88 |
89 |
90 |
91 |
92 |
93 | @endsection 94 | -------------------------------------------------------------------------------- /resources/views/groups/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends (config("genealabs-laravel-governor.layout-view")) 2 | 3 | @section ("content") 4 |
5 | 6 | 7 |
8 |
9 |

10 | Edit "{{ $group->name }}" Group 11 |

12 |
13 |
14 | 19 | 23 | 24 |
25 |
26 | 27 | 28 | 29 | 35 | 40 |
43 |
44 |
45 |

General

46 |

47 | Name your group according to the purpose it will fulfill. 48 |

49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 61 |
62 |
63 |
64 |
65 |
66 |
67 | 72 |
73 |
74 |
75 |
76 |

Entities

77 |

78 | Select all entities that are part of this functional grouping. 79 |

80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | 94 |
95 |
96 |
97 |
98 | 102 |
103 |
104 |
105 |
106 |
107 | @endsection 108 | -------------------------------------------------------------------------------- /resources/views/groups/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends (config("genealabs-laravel-governor.layout-view")) 2 | 3 | @section ("content") 4 |
5 | 6 | 7 |
8 |
9 |

10 | Groups 11 |

12 |
13 | 21 |
22 | 23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 | 36 | 42 | 43 | 44 | 45 | 46 | @if ($groups->isEmpty()) 47 | 48 | 54 | 55 | @endif 56 | 57 | @foreach ($groups as $group) 58 | 59 | 68 | 83 | 84 | @endforeach 85 | 86 | 87 |
34 | Name 35 | 40 | Assigned Entities 41 |
52 | We don't seem to have any groups yet. Why not create one? 53 |
60 | 64 | {{ $group->name }} 65 | 66 | 67 | 69 | 73 | 74 | @foreach ($group->entities as $entity) 75 | 78 | {{ $entity->name }} 79 | 80 | @endforeach 81 | 82 |
88 |
89 |
90 |
91 |
92 |
93 | @endsection 94 | -------------------------------------------------------------------------------- /resources/views/navigation.blade.php: -------------------------------------------------------------------------------- 1 | @if (auth()->user()->can("viewAny", config("genealabs-laravel-governor.models.role")) 2 | || auth()->user()->can("viewAny", config("genealabs-laravel-governor.models.group")) 3 | || auth()->user()->can("viewAny", config("genealabs-laravel-governor.models.team")) 4 | || auth()->user()->can("viewAny", config("genealabs-laravel-governor.models.assignment")) 5 | ) 6 |

7 | 8 | {{ __("Permissions") }} 9 |

10 | 53 | @endif 54 | -------------------------------------------------------------------------------- /resources/views/roles/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends (config("genealabs-laravel-governor.layout-view")) 2 | 3 | @section ("content") 4 |
5 | 6 | 7 |
8 |
9 |

10 | Roles 11 |

12 |
13 | 21 |
22 | 23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | @foreach ($roles as $role) 41 | 42 | 59 | 60 | @endforeach 61 | 62 | 63 |
34 | Role 35 |
43 | 47 | 50 | {{ $role->name }} 51 | 52 | 55 | {{ $role->description }} 56 | 57 | 58 |
64 |
65 |
66 |
67 |
68 |
69 | @endsection 70 | -------------------------------------------------------------------------------- /resources/views/teams/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends (config("genealabs-laravel-governor.layout-view")) 2 | 3 | @section ("content") 4 |
5 | 6 | 7 |
8 |
9 |

10 | Teams 11 |

12 |
13 | 21 |
22 | 23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 | 36 | 42 | 43 | 44 | 45 | 46 | @if ($teams->isEmpty()) 47 | 48 | 54 | 55 | @endif 56 | 57 | @foreach ($teams as $team) 58 | 59 | 76 | 79 | 80 | @endforeach 81 | 82 | 83 |
34 | Team 35 | 40 | Owner 41 |
52 | There aren't any teams to be found anywhere. Do you want to set one up? 53 |
60 | 64 | 67 | {{ $team->name }} 68 | 69 | 72 | {{ $team->description }} 73 | 74 | 75 | 77 | {{ $team->ownerName }} 78 |
84 |
85 |
86 |
87 |
88 |
89 | @endsection 90 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | name("genealabs.laravel-governor.api.user-can.show"); 5 | Route::get('user-is/{role}', "UserIs@show") 6 | ->name("genealabs.laravel-governor.api.user-is.show"); 7 | -------------------------------------------------------------------------------- /routes/nova.php: -------------------------------------------------------------------------------- 1 | 'required|min:3|unique:governor_actions,name', 20 | ]; 21 | protected $fillable = [ 22 | 'name', 23 | ]; 24 | protected $table = "governor_actions"; 25 | 26 | public $incrementing = false; 27 | 28 | public function permissions(): HasMany 29 | { 30 | return $this->hasMany( 31 | config('genealabs-laravel-governor.models.permission'), 32 | 'action_name' 33 | ); 34 | } 35 | 36 | public function getEntityAttribute(): string 37 | { 38 | $entity = collect(explode("\\", $this->model_class)) 39 | ->last() 40 | ?? ""; 41 | 42 | return trim(ucwords(preg_replace('/[A-Z]/m', " $0", $entity))); 43 | } 44 | 45 | public function getModelClassAttribute(): string 46 | { 47 | if (! Str::contains($this->name, ":")) { 48 | return ""; 49 | } 50 | 51 | return collect(explode(":", $this->name)) 52 | ->shift(); 53 | } 54 | 55 | public function getActionAttribute(): string 56 | { 57 | if (! Str::contains($this->name, ":")) { 58 | return $this->name; 59 | } 60 | 61 | return collect(explode(":", $this->name)) 62 | ->pop(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Assignment.php: -------------------------------------------------------------------------------- 1 | users = app(config('genealabs-laravel-governor.models.auth')); 21 | $this->roles = app(config('genealabs-laravel-governor.models.role')); 22 | } 23 | 24 | public function role(): BelongsTo 25 | { 26 | return $this->belongsTo(config('genealabs-laravel-governor.models.role')); 27 | } 28 | 29 | public function user(): BelongsTo 30 | { 31 | return $this->belongsTo(config("genealabs-laravel-governor.models.auth")); 32 | } 33 | 34 | public function addAllUsersToMemberRole() 35 | { 36 | $this->users 37 | ->with('roles') 38 | ->get() 39 | ->each(function ($user) { 40 | if (! $user->roles->contains('Member')) { 41 | $user->roles()->attach('Member'); 42 | } 43 | }); 44 | } 45 | 46 | public function removeAllSuperAdminUsersFromOtherRoles($assignedUsers) 47 | { 48 | $role = 'SuperAdmin'; 49 | 50 | if (array_key_exists($role, $assignedUsers)) { 51 | $users = $assignedUsers[$role]; 52 | 53 | foreach ($users as $id) { 54 | $this->users 55 | ->with('roles') 56 | ->find($id) 57 | ->roles() 58 | ->detach(); 59 | } 60 | 61 | $this->roles 62 | ->with('users') 63 | ->find($role) 64 | ->users() 65 | ->attach($users); 66 | } 67 | } 68 | 69 | public function assignUsersToRoles($assignedUsers) 70 | { 71 | foreach ($assignedUsers as $role => $users) { 72 | if ($role === 'Member') { 73 | continue; 74 | } 75 | 76 | $this->roles 77 | ->with('users') 78 | ->find($role) 79 | ->users() 80 | ->attach($users); 81 | } 82 | } 83 | 84 | public function removeUsersFromRoles($assignedUsers) 85 | { 86 | $this->roles 87 | ->with("users") 88 | ->get() 89 | ->each(function ($role) use ($assignedUsers) { 90 | $role->users() 91 | ->whereIn("id", data_get($assignedUsers, $role->name, [])) 92 | ->detach(); 93 | }); 94 | } 95 | 96 | 97 | public function getAllUsersOfRole($role) 98 | { 99 | return $this->roles 100 | ->with('users') 101 | ->find($role) 102 | ->users; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Console/Commands/Publish.php: -------------------------------------------------------------------------------- 1 | option('config')) { 14 | $this->call('vendor:publish', [ 15 | '--provider' => LaravelGovernorService::class, 16 | '--tag' => ['config'], 17 | '--force' => true, 18 | ]); 19 | } 20 | 21 | if ($this->option('views')) { 22 | $this->call('vendor:publish', [ 23 | '--provider' => LaravelGovernorService::class, 24 | '--tag' => ['views'], 25 | '--force' => true, 26 | ]); 27 | } 28 | 29 | if ($this->option('migrations')) { 30 | $this->call('vendor:publish', [ 31 | '--provider' => LaravelGovernorService::class, 32 | '--tag' => ['migrations'], 33 | '--force' => true, 34 | ]); 35 | } 36 | 37 | if ($this->option('assets')) { 38 | $this->call('vendor:publish', [ 39 | '--provider' => LaravelGovernorService::class, 40 | '--tag' => ['assets'], 41 | '--force' => true, 42 | ]); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Entity.php: -------------------------------------------------------------------------------- 1 | 'required|min:3|unique:governor_entities,name', 17 | ]; 18 | protected $fillable = [ 19 | 'name', 20 | ]; 21 | protected $table = "governor_entities"; 22 | 23 | public $incrementing = false; 24 | 25 | public function group(): BelongsTo 26 | { 27 | return $this->belongsTo( 28 | config('genealabs-laravel-governor.models.group') 29 | ); 30 | } 31 | 32 | public function permissions(): HasMany 33 | { 34 | return $this->hasMany( 35 | config('genealabs-laravel-governor.models.permission'), 36 | 'entity_name' 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Group.php: -------------------------------------------------------------------------------- 1 | 'required|min:3|unique:governor_groups,name', 16 | ]; 17 | protected $fillable = [ 18 | 'name', 19 | ]; 20 | protected $table = "governor_groups"; 21 | 22 | public $incrementing = false; 23 | 24 | public function entities() : HasMany 25 | { 26 | return $this->hasMany( 27 | config('genealabs-laravel-governor.models.entity'), 28 | 'group_name' 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Controllers/Api/UserCan.php: -------------------------------------------------------------------------------- 1 | displayNameField = config('genealabs-laravel-governor.user-name-property'); 16 | } 17 | 18 | public function create(): View 19 | { 20 | $assignmentClass = config("genealabs-laravel-governor.models.assignment"); 21 | $assignment = new $assignmentClass; 22 | $this->authorize('view', $assignment); 23 | $displayNameField = $this->displayNameField; 24 | $userClass = app(config('genealabs-laravel-governor.models.auth')); 25 | $users = (new $userClass) 26 | ->orderBy("name") 27 | ->get(); 28 | $roleClass = config("genealabs-laravel-governor.models.role"); 29 | $roles = (new $roleClass) 30 | ->with("users") 31 | ->orderBy("name") 32 | ->get(); 33 | 34 | return view('genealabs-laravel-governor::assignments.create')->with( 35 | compact('users', 'roles', 'displayNameField', 'assignment') 36 | ); 37 | } 38 | 39 | public function store(CreateAssignmentRequest $request): RedirectResponse 40 | { 41 | $request->process(); 42 | 43 | return redirect() 44 | ->route('genealabs.laravel-governor.assignments.create'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Http/Controllers/GroupsController.php: -------------------------------------------------------------------------------- 1 | with("entities") 18 | ->orderBy("name") 19 | ->get(); 20 | 21 | return view("genealabs-laravel-governor::groups.index") 22 | ->with([ 23 | "groups" => $groups, 24 | ]); 25 | } 26 | 27 | public function create(): View 28 | { 29 | $entities = (new Entity) 30 | ->orderBy("name") 31 | ->get(); 32 | 33 | return view("genealabs-laravel-governor::groups.create") 34 | ->with([ 35 | "entities" => $entities, 36 | ]); 37 | } 38 | 39 | public function store(StoreGroupRequest $request): RedirectResponse 40 | { 41 | $request->process(); 42 | 43 | return redirect()->route("genealabs.laravel-governor.groups.index"); 44 | } 45 | 46 | public function edit(Group $group): View 47 | { 48 | $entities = (new Entity) 49 | ->orderBy("name") 50 | ->get(); 51 | 52 | return view("genealabs-laravel-governor::groups.edit") 53 | ->with([ 54 | "entities" => $entities, 55 | "group" => $group, 56 | ]); 57 | } 58 | 59 | public function destroy(GroupDeleteRequest $request, Group $group): RedirectResponse 60 | { 61 | $request->process(); 62 | 63 | return redirect()->route("genealabs.laravel-governor.groups.index"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Http/Controllers/InvitationController.php: -------------------------------------------------------------------------------- 1 | with("team") 10 | ->where("token", $token) 11 | ->first(); 12 | 13 | if (! $invitation) { 14 | abort(410, "Unfortunately this invitation is no longer valid."); 15 | } 16 | 17 | $invitation->team->members()->syncWithoutDetaching(auth()->user()); 18 | $invitation->delete(); 19 | $nova = config("nova.path"); 20 | 21 | return redirect("{$nova}/resources/teams/{$invitation->team_id}"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Http/Controllers/Nova/AssignmentController.php: -------------------------------------------------------------------------------- 1 | with("users") 14 | ->orderBy("name") 15 | ->get() 16 | ->where("name", $role) 17 | ->first() 18 | ->users() 19 | ->sync(request("user_ids")); 20 | 21 | return response(null, 204); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Http/Controllers/Nova/EntityController.php: -------------------------------------------------------------------------------- 1 | with("entities") 17 | ->orderBy("name") 18 | ->get(); 19 | } 20 | 21 | public function store(StoreGroupRequest $request) : Response 22 | { 23 | $request->process(); 24 | 25 | return response(null, 204); 26 | } 27 | 28 | public function show($id) : Group 29 | { 30 | $groupClass = app(config('genealabs-laravel-governor.models.group')); 31 | 32 | return (new $groupClass) 33 | ->with("entities") 34 | ->orderBy("name") 35 | ->get() 36 | ->find($id); 37 | } 38 | 39 | public function update(UpdateGroupRequest $request) : Response 40 | { 41 | $request->process(); 42 | 43 | return response(null, 204); 44 | } 45 | 46 | public function destroy($id) : Response 47 | { 48 | $groupClass = config("genealabs-laravel-governor.models.group"); 49 | (new $groupClass) 50 | ->destroy($id); 51 | 52 | return response(null, 204); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Http/Controllers/Nova/PermissionController.php: -------------------------------------------------------------------------------- 1 | with("permissions.action", "permissions.entity", "permissions.ownership") 21 | ->where(function ($query) { 22 | if (request("filter") === "team_id") { 23 | $query->where("id", request("value")); 24 | } 25 | 26 | if (request("filter") === "role_name") { 27 | $query->where("name", request("value")); 28 | } 29 | }) 30 | ->first(); 31 | 32 | if (request("owner") === "yes") { 33 | return $permissible 34 | ->ownedBy 35 | ->effectivePermissions 36 | ->toArray(); 37 | } 38 | 39 | $this->parsePolicies(); 40 | 41 | $entities = (new $entityClass) 42 | ->whereNotIn('name', ['Permission (Laravel Governor)', 'Entity (Laravel Governor)', "Action (Laravel Governor)", "Ownership (Laravel Governor)", "Team Invitation (Laravel Governor)"]) 43 | ->orderBy("group_name") 44 | ->orderBy("name") 45 | ->get(); 46 | $actionClass = app(config('genealabs-laravel-governor.models.action')); 47 | $actions = (new $actionClass) 48 | ->orderBy("name") 49 | ->get(); 50 | $permissionMatrix = []; 51 | 52 | foreach ($entities as $entity) { 53 | foreach ($actions as $action) { 54 | $selectedOwnership = 'no'; 55 | 56 | foreach ($permissible->permissions as $permissioncheck) { 57 | if (($permissioncheck->entity->name === $entity->name) 58 | && ($permissioncheck->action->name === $action->name)) { 59 | $selectedOwnership = $permissioncheck->ownership->name; 60 | } 61 | } 62 | 63 | $groupName = ucwords($entity->group_name) 64 | ?: "Ungrouped"; 65 | $permissionMatrix[$groupName][$entity->name][$action->name] = $selectedOwnership; 66 | } 67 | } 68 | 69 | return $permissionMatrix; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Http/Controllers/Nova/RoleController.php: -------------------------------------------------------------------------------- 1 | with("users") 18 | ->orderBy("name") 19 | ->get(); 20 | } 21 | 22 | public function store(StoreRoleRequest $request): Response 23 | { 24 | $request->process(); 25 | 26 | return response(null, 204); 27 | } 28 | 29 | public function show($id): Role 30 | { 31 | $roleClass = config("genealabs-laravel-governor.models.role"); 32 | 33 | return (new $roleClass) 34 | ->find($id); 35 | } 36 | 37 | public function update(UpdateRoleRequest $request): Response 38 | { 39 | $request->process(); 40 | 41 | return response(null, 204); 42 | } 43 | 44 | public function destroy($id): Response 45 | { 46 | $roleClass = config("genealabs-laravel-governor.models.role"); 47 | 48 | (new $roleClass) 49 | ->find($id) 50 | ->delete(); 51 | 52 | return response(null, 204); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Http/Controllers/Nova/TeamController.php: -------------------------------------------------------------------------------- 1 | process(); 12 | 13 | return response(null, 204); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Http/Controllers/Nova/UserController.php: -------------------------------------------------------------------------------- 1 | get(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Http/Controllers/TeamsController.php: -------------------------------------------------------------------------------- 1 | orderBy("name") 17 | ->get(); 18 | 19 | return view("genealabs-laravel-governor::teams.index") 20 | ->with([ 21 | "teams" => $teams, 22 | ]); 23 | } 24 | 25 | public function create(): View 26 | { 27 | $entityClass = config("genealabs-laravel-governor.models.entity"); 28 | $ownershipClass = config("genealabs-laravel-governor.models.ownership"); 29 | $teamClass = config("genealabs-laravel-governor.models.team"); 30 | 31 | $entities = (new $entityClass) 32 | ->whereNotIn('name', ['Permission (Laravel Governor)', 'Entity (Laravel Governor)', "Action (Laravel Governor)", "Ownership (Laravel Governor)", "Team Invitation (Laravel Governor)"]) 33 | ->orderBy("group_name") 34 | ->orderBy("name") 35 | ->get(); 36 | $ownerships = (new $ownershipClass) 37 | ->all() 38 | ->pluck('name', 'name'); 39 | $ownerships = collect(["not" => ""])->merge($ownerships); 40 | $team = new $teamClass; 41 | $permissionMatrix = $this->createPermissionMatrix($team, $entities); 42 | 43 | return view("genealabs-laravel-governor::teams.create") 44 | ->with([ 45 | "entities" => $entities, 46 | "ownerships" => $ownerships, 47 | "permissionMatrix" => $permissionMatrix, 48 | ]); 49 | } 50 | 51 | public function store(TeamStoreRequest $request): RedirectResponse 52 | { 53 | $request->process(); 54 | 55 | return redirect()->route('genealabs.laravel-governor.teams.index'); 56 | } 57 | 58 | public function edit(Team $team): View 59 | { 60 | $entityClass = config("genealabs-laravel-governor.models.entity"); 61 | $ownershipClass = config("genealabs-laravel-governor.models.ownership"); 62 | 63 | $entities = (new $entityClass) 64 | ->whereNotIn('name', ['Permission (Laravel Governor)', 'Entity (Laravel Governor)', "Action (Laravel Governor)", "Ownership (Laravel Governor)", "Team Invitation (Laravel Governor)"]) 65 | ->orderBy("group_name") 66 | ->orderBy("name") 67 | ->get(); 68 | $ownerships = (new $ownershipClass) 69 | ->all() 70 | ->pluck('name', 'name'); 71 | $ownerships = collect(["not" => ""])->merge($ownerships); 72 | $permissionMatrix = $this->createPermissionMatrix($team, $entities); 73 | 74 | return view("genealabs-laravel-governor::teams.edit") 75 | ->with([ 76 | "entities" => $entities, 77 | "ownerships" => $ownerships, 78 | "permissionMatrix" => $permissionMatrix, 79 | "team" => $team, 80 | ]); 81 | } 82 | 83 | protected function createPermissionMatrix(Team $team, Collection $entities): array 84 | { 85 | $permissionMatrix = []; 86 | 87 | $actionClass = app(config('genealabs-laravel-governor.models.action')); 88 | $actions = (new $actionClass) 89 | ->orderBy("name") 90 | ->get(); 91 | 92 | foreach ($entities as $entity) { 93 | foreach ($actions as $action) { 94 | $selectedOwnership = 'no'; 95 | 96 | foreach ($team->permissions as $permissioncheck) { 97 | if ( 98 | $permissioncheck->entity->name === $entity->name 99 | && $permissioncheck->action->name === $action->name 100 | ) { 101 | $selectedOwnership = $permissioncheck->ownership->name; 102 | } 103 | } 104 | 105 | $groupName = ucwords($entity->group_name) 106 | ?: "Ungrouped"; 107 | $permissionMatrix[$groupName][$entity->name][$action->name] = $selectedOwnership; 108 | } 109 | } 110 | 111 | return $permissionMatrix; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authorize.php: -------------------------------------------------------------------------------- 1 | authorize($request) 18 | ? $next($request) 19 | : abort(403); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Middleware/ParseCustomPolicyActions.php: -------------------------------------------------------------------------------- 1 | registerCustomPolicyActions(); 22 | 23 | return $next($request); 24 | } 25 | 26 | protected function registerCustomPolicyActions(): void 27 | { 28 | $this 29 | ->getPolicies() 30 | ->map(function (string $policyClass, string $modelClass): Collection { 31 | if (! collect(class_parents($policyClass))->contains(BasePolicy::class)) { 32 | return collect(); 33 | } 34 | 35 | return $this 36 | ->getCustomActionMethods($policyClass) 37 | ->map(function (string $method) use ($modelClass): Action { 38 | $action = app("governor-actions") 39 | ->where("name", "{$modelClass}:{$method}") 40 | ->first(); 41 | 42 | if (! $action) { 43 | $actionClass = app(config('genealabs-laravel-governor.models.action')); 44 | $action = (new $actionClass) 45 | ->firstOrCreate([ 46 | "name" => "{$modelClass}:{$method}", 47 | ]); 48 | } 49 | 50 | return $action; 51 | }); 52 | }) 53 | ->filter() 54 | ->toArray(); 55 | } 56 | 57 | protected function getCustomActionMethods(string $policyClass): Collection 58 | { 59 | return cache()->remember("genealabs:laravel-governor:custom-action-methods:policy-{$policyClass}", 300, function () use ($policyClass): Collection { 60 | $parentClass = new ReflectionClass(get_parent_class($policyClass)); 61 | $parentMethods = collect($parentClass->getMethods(ReflectionMethod::IS_PUBLIC)) 62 | ->pluck("name"); 63 | $class = new ReflectionClass($policyClass); 64 | $methods = collect($class->getMethods(ReflectionMethod::IS_PUBLIC)) 65 | ->pluck("name"); 66 | 67 | return $methods 68 | ->diff($parentMethods) 69 | ->sort(); 70 | }); 71 | } 72 | 73 | protected function getPolicies(): Collection 74 | { 75 | return cache()->remember("genealabs:laravel-governor:policies", 300, function (): Collection { 76 | $gate = app('Illuminate\Contracts\Auth\Access\Gate'); 77 | $reflectedGate = new ReflectionObject($gate); 78 | $policies = $reflectedGate->getProperty("policies"); 79 | $policies->setAccessible(true); 80 | 81 | return collect($policies->getValue($gate)); 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Http/Requests/CreateAssignmentRequest.php: -------------------------------------------------------------------------------- 1 | allows('create', new $assignmentClass); 13 | } 14 | 15 | public function rules(): array 16 | { 17 | return [ 18 | 'users' => 'required|array', 19 | ]; 20 | } 21 | 22 | public function process(): void 23 | { 24 | $assignmentClass = config("genealabs-laravel-governor.models.assignment"); 25 | $assignment = new $assignmentClass; 26 | $assignment->removeUsersFromRoles($this->users); 27 | $assignment->assignUsersToRoles($this->users); 28 | $assignment->addAllUsersToMemberRole(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Http/Requests/CreateRoleRequest.php: -------------------------------------------------------------------------------- 1 | allows('create', new $roleClass); 15 | } 16 | 17 | public function rules() : array 18 | { 19 | return [ 20 | 'name' => 'required', 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Http/Requests/GroupDeleteRequest.php: -------------------------------------------------------------------------------- 1 | check() 15 | && auth()->user()->can("delete", $this->group); 16 | } 17 | 18 | public function rules(): array 19 | { 20 | return [ 21 | // 22 | ]; 23 | } 24 | 25 | public function process(): Group 26 | { 27 | $this->group->delete(); 28 | 29 | return $this->group; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Requests/RoleUpdateRequest.php: -------------------------------------------------------------------------------- 1 | check() 14 | && ($this->role 15 | ? auth()->user()->can("update", $this->role) 16 | : auth()->user()->can("create", $roleClass)); 17 | } 18 | 19 | public function rules(): array 20 | { 21 | return [ 22 | 'name' => 'required|string', 23 | "description" => "string|nullable", 24 | "permissions" => "array", 25 | ]; 26 | } 27 | 28 | public function process(): void 29 | { 30 | $permissionClass = config("genealabs-laravel-governor.models.permission"); 31 | $this->role->fill($this->all()); 32 | 33 | if ($this->filled('permissions')) { 34 | $this->role->permissions()->delete(); 35 | 36 | foreach ($this->permissions as $group) { 37 | foreach ($group as $entity => $actions) { 38 | foreach ($actions as $action => $ownership) { 39 | if ('no' !== $ownership) { 40 | (new $permissionClass) 41 | ->updateOrCreate([ 42 | "action_name" => $action, 43 | "entity_name" => urldecode($entity), 44 | "role_name" => $this->role->name, 45 | ], [ 46 | "ownership_name" => $ownership, 47 | ]); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | $this->role->save(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Http/Requests/StoreGroupRequest.php: -------------------------------------------------------------------------------- 1 | check() 14 | && ($this->group 15 | ? auth()->user()->can("update", $this->group) 16 | : auth()->user()->can("create", $groupClass)); 17 | } 18 | 19 | public function rules(): array 20 | { 21 | return [ 22 | 'name' => 'required|string', 23 | "entity_names" => "required|array", 24 | ]; 25 | } 26 | 27 | public function process() 28 | { 29 | $entityClass = config("genealabs-laravel-governor.models.entity"); 30 | $groupClass = config("genealabs-laravel-governor.models.group"); 31 | 32 | (new $groupClass)->firstOrCreate([ 33 | "name" => $this->name, 34 | ]); 35 | 36 | foreach ($this->entity_names as $entityName) { 37 | (new $entityClass) 38 | ->where("name", $entityName) 39 | ->update([ 40 | "group_name" => $this->name, 41 | ]); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Http/Requests/StoreRoleRequest.php: -------------------------------------------------------------------------------- 1 | check() 12 | && auth()->user()->can("create", $roleClass); 13 | } 14 | 15 | public function rules() : array 16 | { 17 | return [ 18 | 'name' => 'required|string', 19 | "description" => "string|nullable", 20 | ]; 21 | } 22 | 23 | public function process() 24 | { 25 | $roleClass = config("genealabs-laravel-governor.models.role"); 26 | $role = new $roleClass; 27 | $role->fill($this->all()); 28 | $role->save(); 29 | 30 | return $role; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Http/Requests/TeamStoreRequest.php: -------------------------------------------------------------------------------- 1 | check() 15 | && ($this->team 16 | ? auth()->user()->can("update", $this->team) 17 | : auth()->user()->can("create", $teamClass)); 18 | } 19 | 20 | public function rules(): array 21 | { 22 | return [ 23 | "description" => "string|nullable", 24 | "name" => "required|string", 25 | "permissions" => "array", 26 | ]; 27 | } 28 | 29 | public function process(): Team 30 | { 31 | $permissionClass = config("genealabs-laravel-governor.models.permission"); 32 | $teamClass = config("genealabs-laravel-governor.models.team"); 33 | $team = $this->team 34 | ?? new $teamClass; 35 | $team->fill($this->all()); 36 | 37 | if ($this->filled('permissions')) { 38 | $team->permissions()->delete(); 39 | 40 | foreach ($this->permissions as $group) { 41 | foreach ($group as $entity => $actions) { 42 | foreach ($actions as $action => $ownership) { 43 | if ($ownership) { 44 | (new $permissionClass) 45 | ->updateOrCreate([ 46 | "action_name" => $action, 47 | "entity_name" => urldecode($entity), 48 | "team_id" => $team->getKey(), 49 | ], [ 50 | "ownership_name" => $ownership, 51 | ]); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | $team->save(); 59 | 60 | return $team; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Http/Requests/UpdateGroupRequest.php: -------------------------------------------------------------------------------- 1 | check() 15 | && ($this->group 16 | ? auth()->user()->can("update", $this->group) 17 | : auth()->user()->can("create", $groupClass)); 18 | } 19 | 20 | public function rules() : array 21 | { 22 | return [ 23 | 'name' => 'required|string', 24 | "description" => "string|nullable", 25 | "permissions" => "array", 26 | ]; 27 | } 28 | 29 | public function process() 30 | { 31 | $permissionClass = config("genealabs-laravel-governor.models.permission"); 32 | $roleClass = config("genealabs-laravel-governor.models.role"); 33 | $role = $this->id 34 | ? (new $roleClass)->find($this->id) 35 | : new $roleClass; 36 | $role->fill($this->all()); 37 | 38 | if ($this->filled('permissions')) { 39 | $actionClass = app(config('genealabs-laravel-governor.models.action')); 40 | $allActions = (new $actionClass) 41 | ->orderBy("name") 42 | ->get(); 43 | $ownershipClass = app(config('genealabs-laravel-governor.models.ownership')); 44 | $allOwnerships = (new $ownershipClass) 45 | ->orderBy("name") 46 | ->get(); 47 | $allEntities = app("governor-entities"); 48 | $role->permissions()->delete(); 49 | 50 | foreach ($this->permissions as $entity => $actions) { 51 | foreach ($actions as $action => $ownership) { 52 | if ('no' !== $ownership) { 53 | $currentAction = $allActions->find($action); 54 | $currentOwnership = $allOwnerships->find($ownership); 55 | $currentEntity = $allEntities->find($entity); 56 | $currentPermission = new $permissionClass; 57 | $currentPermission->ownership()->associate($currentOwnership); 58 | $currentPermission->action()->associate($currentAction); 59 | $currentPermission->role()->associate($role); 60 | $currentPermission->entity()->associate($currentEntity); 61 | $currentPermission->save(); 62 | } 63 | } 64 | } 65 | } 66 | 67 | $role->save(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Http/Requests/UpdateRoleRequest.php: -------------------------------------------------------------------------------- 1 | role)) { 14 | $this->role = (new $roleClass) 15 | ->where("name", $this->role) 16 | ->first(); 17 | } 18 | 19 | return auth()->check() 20 | && ($this->role 21 | ? auth()->user()->can("update", $this->role) 22 | : auth()->user()->can("create", $roleClass)); 23 | } 24 | 25 | public function rules(): array 26 | { 27 | return [ 28 | 'name' => 'required|string', 29 | "description" => "string|nullable", 30 | "permissions" => "array", 31 | ]; 32 | } 33 | 34 | public function process(): void 35 | { 36 | $roleClass = config("genealabs-laravel-governor.models.role"); 37 | 38 | if (is_string($this->role)) { 39 | $this->role = (new $roleClass) 40 | ->where("name", $this->role) 41 | ->first(); 42 | } 43 | 44 | $permissionClass = config("genealabs-laravel-governor.models.permission"); 45 | $this->role->fill($this->all()); 46 | 47 | if ($this->filled('permissions')) { 48 | $this->role->permissions()->delete(); 49 | 50 | foreach ($this->permissions as $group) { 51 | foreach ($group as $entity => $actions) { 52 | foreach ($actions as $action => $ownership) { 53 | if ('no' !== $ownership) { 54 | (new $permissionClass) 55 | ->updateOrCreate([ 56 | "action_name" => $action, 57 | "entity_name" => urldecode($entity), 58 | "role_name" => $this->role->name, 59 | ], [ 60 | "ownership_name" => $ownership, 61 | ]); 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | $this->role->save(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Http/Requests/UpdateTeamRequest.php: -------------------------------------------------------------------------------- 1 | team = (new $teamClass) 12 | ->get() 13 | ->find($this->team); 14 | 15 | return auth()->check() 16 | && ($this->team 17 | ? auth()->user()->can("update", $this->team) 18 | : auth()->user()->can("create", $teamClass)); 19 | } 20 | 21 | public function rules() : array 22 | { 23 | return [ 24 | "permissions" => "array", 25 | ]; 26 | } 27 | 28 | public function process() 29 | { 30 | $permissionClass = config("genealabs-laravel-governor.models.permission"); 31 | 32 | if ($this->filled('permissions')) { 33 | $this->team->permissions()->delete(); 34 | 35 | // TODO: optimize into a single insert command 36 | foreach ($this->permissions as $group) { 37 | foreach ($group as $entity => $actions) { 38 | foreach ($actions as $action => $ownership) { 39 | if ('no' !== $ownership) { 40 | $currentPermission = (new $permissionClass) 41 | ->firstOrNew([ 42 | "action_name" => $action, 43 | "entity_name" => $entity, 44 | "team_id" => $this->team->id, 45 | ]); 46 | $currentPermission->ownership_name = $ownership; 47 | $currentPermission->save(); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Http/Requests/UserCan.php: -------------------------------------------------------------------------------- 1 | ability; 10 | $model = $this->model; 11 | $primaryKey = request("primary-key"); 12 | 13 | if ($primaryKey) { 14 | $model = (new $model)->findOrFail($primaryKey); 15 | } 16 | 17 | return auth()->check() 18 | && auth()->user()->can($ability, $model); 19 | } 20 | 21 | public function rules() : array 22 | { 23 | return [ 24 | 'model' => 'required|string', 25 | 'primary-key' => 'integer', 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Http/Requests/UserIs.php: -------------------------------------------------------------------------------- 1 | check() 10 | && (auth()->user()->roles->contains($this->role) 11 | || auth()->user()->roles->contains("SuperAdmin")); 12 | } 13 | 14 | public function rules() : array 15 | { 16 | return [ 17 | // 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Listeners/CreatedInvitationListener.php: -------------------------------------------------------------------------------- 1 | email) 16 | ->notify(new TeamInvitation($invitation)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Listeners/CreatedListener.php: -------------------------------------------------------------------------------- 1 | filter(function ($model) { 21 | return $model instanceof Model 22 | && get_class($model) === config('genealabs-laravel-governor.models.auth'); 23 | }) 24 | ->each(function ($model) { 25 | try { 26 | $model->roles()->syncWithoutDetaching('Member'); 27 | } catch (Exception $exception) { 28 | $roleClass = config("genealabs-laravel-governor.models.role"); 29 | (new $roleClass)->firstOrCreate([ 30 | 'name' => 'Member', 31 | 'description' => 'Represents the baseline registered user. Customize permissions as best suits your site.', 32 | ]); 33 | $model->roles()->attach('Member'); 34 | } 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Listeners/CreatedTeamListener.php: -------------------------------------------------------------------------------- 1 | check()) { 8 | $team->members()->syncWithoutDetaching([auth()->user()->id]); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Listeners/CreatingInvitationListener.php: -------------------------------------------------------------------------------- 1 | token = Uuid::uuid4(); 13 | $model->ownedBy()->associate(auth()->user()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Listeners/CreatingListener.php: -------------------------------------------------------------------------------- 1 | filter(function ($model) { 29 | return $model instanceof Model 30 | && in_array( 31 | "GeneaLabs\\LaravelGovernor\\Traits\\Governable", 32 | class_uses_recursive($model) 33 | ); 34 | }) 35 | ->filter() 36 | ->each(function ($model) { 37 | $model->getEntityFromModel(get_class($model)); 38 | 39 | if ( 40 | ! $model->governor_owned_by 41 | && auth()->check() 42 | ) { 43 | $model->governor_owned_by = auth()->user()->id; 44 | } 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Notifications/TeamInvitation.php: -------------------------------------------------------------------------------- 1 | invitation = $invitation; 18 | } 19 | 20 | public function via($notifiable) : array 21 | { 22 | return ['mail']; 23 | } 24 | 25 | public function toMail($notifiable) : MailMessage 26 | { 27 | $appUrl = config("app.url"); 28 | $message = [ 29 | "You have been invited by {$this->invitation->ownedBy->name}", 30 | "to join team '{$this->invitation->team->name}' on {$appUrl}." 31 | ]; 32 | $route = route( 33 | "genealabs.laravel-governor.invitations.update", 34 | $this->invitation->token 35 | ); 36 | 37 | return (new MailMessage) 38 | ->greeting("Hello!") 39 | ->line(implode(" ", $message)) 40 | ->action('Accept Invitation', url($route)); 41 | } 42 | 43 | public function toArray($notifiable) : array 44 | { 45 | return [ 46 | // 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Nova/GovernorAction.php: -------------------------------------------------------------------------------- 1 | sortable(), 18 | // HasMany::make("Permissions"), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Nova/GovernorAssignment.php: -------------------------------------------------------------------------------- 1 | sortable(), 18 | BelongsTo::make("User", "user", GovernorUser::class), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Nova/GovernorEntity.php: -------------------------------------------------------------------------------- 1 | sortable(), 18 | // HasMany::make("Permissions"), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Nova/GovernorGroup.php: -------------------------------------------------------------------------------- 1 | sortable(), 21 | HasMany::make("Entities", "entities", "GeneaLabs\LaravelGovernor\Nova\GovernorEntity"), 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Nova/GovernorOwnership.php: -------------------------------------------------------------------------------- 1 | sortable(), 18 | // HasMany::make("Permissions"), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Nova/GovernorPermission.php: -------------------------------------------------------------------------------- 1 | onlyOnIndex() 20 | ->sortable(), 21 | Text::make("Action", "action_name", "GeneaLabs\LaravelGovernor\Nova\GovernorAction") 22 | ->resolveUsing(function ($actionName) { 23 | return "can {$actionName}"; 24 | }) 25 | ->onlyOnIndex() 26 | ->sortable(), 27 | Text::make("Ownership", "ownership_name", "GeneaLabs\LaravelGovernor\Nova\GovernorOwnership") 28 | ->onlyOnIndex() 29 | ->sortable(), 30 | Text::make("Entity", "entity_name", "GeneaLabs\LaravelGovernor\Nova\GovernorEntity") 31 | ->onlyOnIndex() 32 | ->sortable(), 33 | BelongsTo::make("Role", "role", "GeneaLabs\LaravelGovernor\Nova\GovernorRole") 34 | ->hideFromIndex(), 35 | BelongsTo::make("Action", "action", "GeneaLabs\LaravelGovernor\Nova\GovernorAction") 36 | ->hideFromIndex(), 37 | BelongsTo::make("Ownership", "ownership", "GeneaLabs\LaravelGovernor\Nova\GovernorOwnership") 38 | ->hideFromIndex(), 39 | BelongsTo::make("Entity", "entity", "GeneaLabs\LaravelGovernor\Nova\GovernorEntity") 40 | ->hideFromIndex(), 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Nova/GovernorRole.php: -------------------------------------------------------------------------------- 1 | sortable(), 21 | Text::make("description"), 22 | HasMany::make("Permissions", "permissions", "GeneaLabs\LaravelGovernor\Nova\GovernorPermission"), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Nova/GovernorTeam.php: -------------------------------------------------------------------------------- 1 | sortable(), 27 | Text::make("Description"), 28 | BelongsTo::make("Owner", "ownedBy", "GeneaLabs\LaravelGovernor\Nova\GovernorUser") 29 | ->withMeta([ 30 | "belongsToId" => $this->governor_owned_by 31 | ?: auth()->user()->id, 32 | ]) 33 | ->searchable() 34 | ->prepopulate() 35 | ->hideFromIndex(), 36 | Text::make("Owner", "governor_owned_by") 37 | ->resolveUsing(function () { 38 | if (! $this->ownedBy) { 39 | return ""; 40 | } 41 | 42 | if (! auth()->user()->can("view", $this->ownedBy)) { 43 | return $this->ownedBy->name 44 | ?: ""; 45 | } 46 | 47 | return "" . $this->ownedBy->name . ''; 48 | }) 49 | ->asHtml() 50 | ->onlyOnIndex() 51 | ->sortable(), 52 | 53 | BelongsToMany::make("Members", "members", "GeneaLabs\LaravelGovernor\Nova\GovernorUser"), 54 | HasMany::make("Invitations", "invitations", "GeneaLabs\LaravelGovernor\Nova\GovernorTeamInvitation"), 55 | PermissionsTool::make() 56 | ->canSee(function () { 57 | return $this->governor_owned_by === auth()->user()->id 58 | || auth()->user()->hasRole("SuperAdmin"); 59 | }), 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Nova/GovernorTeamInvitation.php: -------------------------------------------------------------------------------- 1 | sortable(), 22 | BelongsTo::make("Team", "team", "GeneaLabs\LaravelGovernor\Nova\GovernorTeam"), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Nova/GovernorUser.php: -------------------------------------------------------------------------------- 1 | sortable() 20 | ->rules('required', 'max:255'), 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Nova/Resource.php: -------------------------------------------------------------------------------- 1 | viaResource) { 69 | return "/resources/{$request->viaResource}/{$request->viaResourceId}"; 70 | } 71 | 72 | return parent::redirectAfterCreate($request, $resource); 73 | } 74 | 75 | public static function redirectAfterUpdate(NovaRequest $request, $resource) 76 | { 77 | if ($request->viaResource) { 78 | return "/resources/{$request->viaResource}/{$request->viaResourceId}"; 79 | } 80 | 81 | return parent::redirectAfterUpdate($request, $resource); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Nova/Tools/Governor.php: -------------------------------------------------------------------------------- 1 | 'required|min:3|unique:governor_ownerships,name', 18 | ]; 19 | protected $fillable = [ 20 | 'name', 21 | ]; 22 | protected $table = "governor_ownerships"; 23 | 24 | public function permissions() : HasMany 25 | { 26 | return $this->hasMany( 27 | config('genealabs-laravel-governor.models.permission'), 28 | 'ownership_name' 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Permission.php: -------------------------------------------------------------------------------- 1 | 'required', 15 | 'action_name' => 'required', 16 | 'ownership_name' => 'required', 17 | ]; 18 | protected $fillable = [ 19 | 'role_name', 20 | 'entity_name', 21 | 'action_name', 22 | 'ownership_name', 23 | "team_id", 24 | ]; 25 | protected $table = "governor_permissions"; 26 | 27 | protected static function syncPermissionsSingleton(): void 28 | { 29 | $permissionClass = config("genealabs-laravel-governor.models.permission"); 30 | $permissions = (new $permissionClass) 31 | ->with("role", "team") 32 | ->toBase() 33 | ->get(); 34 | app()->instance("governor-permissions", $permissions); 35 | } 36 | 37 | public static function boot(): void 38 | { 39 | parent::boot(); 40 | 41 | static::saved(function () { 42 | self::syncPermissionsSingleton(); 43 | }); 44 | static::deleted(function () { 45 | self::syncPermissionsSingleton(); 46 | }); 47 | } 48 | 49 | public function entity(): BelongsTo 50 | { 51 | return $this->belongsTo( 52 | config('genealabs-laravel-governor.models.entity'), 53 | 'entity_name' 54 | ); 55 | } 56 | 57 | public function action(): BelongsTo 58 | { 59 | return $this->belongsTo( 60 | config('genealabs-laravel-governor.models.action'), 61 | 'action_name' 62 | ); 63 | } 64 | 65 | public function ownership(): BelongsTo 66 | { 67 | return $this->belongsTo( 68 | config('genealabs-laravel-governor.models.ownership'), 69 | 'ownership_name' 70 | ); 71 | } 72 | 73 | public function role(): BelongsTo 74 | { 75 | return $this->belongsTo( 76 | config('genealabs-laravel-governor.models.role'), 77 | 'role_name' 78 | ); 79 | } 80 | 81 | public function team(): BelongsTo 82 | { 83 | return $this->belongsTo( 84 | config('genealabs-laravel-governor.models.team') 85 | ); 86 | } 87 | 88 | public function getFilteredBy(string $filter = null, string $value = null): Collection 89 | { 90 | return $this 91 | ->where(function ($query) use ($filter, $value) { 92 | if ($filter) { 93 | $query->where($filter, $value); 94 | } 95 | }) 96 | ->get(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/PermissionField.php: -------------------------------------------------------------------------------- 1 | entity = $this->getEntity(get_class($this)); 23 | $this->permissions = $this->getPermissions(); 24 | } 25 | 26 | protected function getPermissions(): Collection 27 | { 28 | return app("governor-permissions"); 29 | } 30 | 31 | public function create(?Model $user): bool 32 | { 33 | return $this->validatePermissions( 34 | $user, 35 | 'create', 36 | $this->entity 37 | ); 38 | } 39 | 40 | public function update(?Model $user, Model $model): bool 41 | { 42 | return $this->validatePermissions( 43 | $user, 44 | 'update', 45 | $this->entity, 46 | $model 47 | ); 48 | } 49 | 50 | public function viewAny(?Model $user): bool 51 | { 52 | return $this->validatePermissions( 53 | $user, 54 | 'viewAny', 55 | $this->entity 56 | ); 57 | } 58 | 59 | public function view(?Model $user, Model $model): bool 60 | { 61 | return $this->validatePermissions( 62 | $user, 63 | 'view', 64 | $this->entity, 65 | $model 66 | ); 67 | } 68 | 69 | public function delete(?Model $user, Model $model): bool 70 | { 71 | return $this->validatePermissions( 72 | $user, 73 | 'delete', 74 | $this->entity, 75 | $model 76 | ); 77 | } 78 | 79 | public function restore(?Model $user, Model $model): bool 80 | { 81 | return $this->validatePermissions( 82 | $user, 83 | 'restore', 84 | $this->entity, 85 | $model 86 | ); 87 | } 88 | 89 | public function forceDelete(?Model $user, Model $model): bool 90 | { 91 | return $this->validatePermissions( 92 | $user, 93 | 'forceDelete', 94 | $this->entity, 95 | $model 96 | ); 97 | } 98 | 99 | protected function authorizeCustomAction(?Model $user, Model $model): bool 100 | { 101 | $action = debug_backtrace( 102 | ! DEBUG_BACKTRACE_PROVIDE_OBJECT 103 | | DEBUG_BACKTRACE_IGNORE_ARGS, 104 | 2, 105 | )[1]["function"]; 106 | 107 | return $this->validatePermissions( 108 | $user, 109 | get_class($model) . ":" . $action, 110 | $this->entity, 111 | $model, 112 | ); 113 | } 114 | 115 | protected function validatePermissions( 116 | ?Model $user, 117 | string $action, 118 | string $entity, 119 | ?Model $model = null 120 | ): bool { 121 | if (! $user) { 122 | $user = $this->createGuestUser(); 123 | } 124 | 125 | $user->loadMissing("roles", "teams.permissions"); 126 | 127 | if ($user->hasRole("SuperAdmin")) { 128 | return true; 129 | } 130 | 131 | if ( 132 | $user->roles->isEmpty() 133 | && $user->teams->isEmpty() 134 | ) { 135 | return false; 136 | } 137 | 138 | $ownership = 'other'; 139 | 140 | if ( 141 | $model 142 | && $user->getKey() == $model->governor_owned_by 143 | ) { 144 | $ownership = 'own'; 145 | } 146 | 147 | $filteredPermissions = $this->filterPermissions($action, $entity, $ownership); 148 | 149 | foreach ($filteredPermissions as $permission) { 150 | if ( 151 | $user->roles->pluck("name")->contains($permission->role_name) 152 | || $user->teams->pluck("id")->contains($permission->team_id) 153 | ) { 154 | return true; 155 | } 156 | } 157 | 158 | return false; 159 | } 160 | 161 | protected function filterPermissions($action, $entity, $ownership) 162 | { 163 | $filteredPermissions = $this 164 | ->permissions 165 | ->filter(function ($permission) use ($action, $entity, $ownership) { 166 | return ($permission->action_name === $action 167 | && $permission->entity_name === $entity 168 | && in_array($permission->ownership_name, [$ownership, 'any'])); 169 | }); 170 | 171 | return $filteredPermissions; 172 | } 173 | 174 | protected function createGuestUser() 175 | { 176 | if (! auth()->user()) { 177 | $roleClass = config("genealabs-laravel-governor.models.role"); 178 | $userClass = config("genealabs-laravel-governor.models.auth"); 179 | $user = new $userClass; 180 | $guest = (new $roleClass)->find("Guest"); 181 | 182 | if ($guest) { 183 | $user->roles = collect([$guest]); 184 | } 185 | 186 | auth()->setUser($user); 187 | 188 | return $user; 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/Policies/Entity.php: -------------------------------------------------------------------------------- 1 | name === "SuperAdmin") { 11 | return false; 12 | } 13 | 14 | return parent::delete($user, $model); 15 | } 16 | 17 | public function forceDelete(?Model $user, Model $model) : bool 18 | { 19 | if ($model->name === "SuperAdmin") { 20 | return false; 21 | } 22 | 23 | return parent::forceDelete($user, $model); 24 | } 25 | 26 | public function restore(?Model $user, Model $model) : bool 27 | { 28 | if ($model->name === "SuperAdmin") { 29 | return false; 30 | } 31 | 32 | return parent::restore($user, $model); 33 | } 34 | 35 | public function update(?Model $user, Model $model) : bool 36 | { 37 | if ($model->name === "SuperAdmin") { 38 | return false; 39 | } 40 | 41 | return parent::update($user, $model); 42 | } 43 | 44 | public function view(?Model $user, Model $model) : bool 45 | { 46 | if ($model->name === "SuperAdmin") { 47 | return false; 48 | } 49 | 50 | return parent::view($user, $model); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Policies/Team.php: -------------------------------------------------------------------------------- 1 | policies = [ 21 | $actionClass => 'GeneaLabs\LaravelGovernor\Policies\Action', 22 | $assignmentClass => 'GeneaLabs\LaravelGovernor\Policies\Assignment', 23 | $entityClass => 'GeneaLabs\LaravelGovernor\Policies\Entity', 24 | $groupClass => 'GeneaLabs\LaravelGovernor\Policies\Group', 25 | $ownershiplass => 'GeneaLabs\LaravelGovernor\Policies\Ownership', 26 | $permissionClass => 'GeneaLabs\LaravelGovernor\Policies\Permission', 27 | $roleClass => 'GeneaLabs\LaravelGovernor\Policies\Role', 28 | $teamClass => 'GeneaLabs\LaravelGovernor\Policies\Team', 29 | $invitationClass => 'GeneaLabs\LaravelGovernor\Policies\TeamInvitation', 30 | ]; 31 | $this->registerPolicies(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Providers/Nova.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../../resources/views', 'genealabs-laravel-governor'); 15 | 16 | // $this->app->booted(function () { 17 | // $this->routes(); 18 | // }); 19 | 20 | // if (class_exists("Laravel\Nova\Resource")) { 21 | // \GeneaLabs\LaravelGovernor\Nova\GovernorAction::$model = config("genealabs-laravel-governor.models.action"); 22 | // \GeneaLabs\LaravelGovernor\Nova\GovernorAssignment::$model = config("genealabs-laravel-governor.models.assignment"); 23 | // \GeneaLabs\LaravelGovernor\Nova\GovernorEntity::$model = config("genealabs-laravel-governor.models.entity"); 24 | // \GeneaLabs\LaravelGovernor\Nova\GovernorGroup::$model = config("genealabs-laravel-governor.models.group"); 25 | // \GeneaLabs\LaravelGovernor\Nova\GovernorOwnership::$model = config("genealabs-laravel-governor.models.ownership"); 26 | // \GeneaLabs\LaravelGovernor\Nova\GovernorPermission::$model = config("genealabs-laravel-governor.models.permission"); 27 | // \GeneaLabs\LaravelGovernor\Nova\GovernorRole::$model = config("genealabs-laravel-governor.models.role"); 28 | // \GeneaLabs\LaravelGovernor\Nova\GovernorTeam::$model = config("genealabs-laravel-governor.models.team"); 29 | // \GeneaLabs\LaravelGovernor\Nova\GovernorTeamInvitation::$model = config("genealabs-laravel-governor.models.invitation"); 30 | // \GeneaLabs\LaravelGovernor\Nova\GovernorUser::$model = config("genealabs-laravel-governor.models.auth"); 31 | // \Laravel\Nova\Nova::serving(function (ServingNova $event) { 32 | // \Laravel\Nova\Nova::script('genealabs-laravel-governor', __DIR__ . '/../../dist/js/tool.js'); 33 | // \Laravel\Nova\Nova::style('genealabs-laravel-governor', __DIR__ . '/../../dist/css/tool.css'); 34 | // \Laravel\Nova\Nova::resources([ 35 | // \GeneaLabs\LaravelGovernor\Nova\GovernorAction::class, 36 | // \GeneaLabs\LaravelGovernor\Nova\GovernorAssignment::class, 37 | // \GeneaLabs\LaravelGovernor\Nova\GovernorEntity::class, 38 | // \GeneaLabs\LaravelGovernor\Nova\GovernorGroup::class, 39 | // \GeneaLabs\LaravelGovernor\Nova\GovernorOwnership::class, 40 | // \GeneaLabs\LaravelGovernor\Nova\GovernorPermission::class, 41 | // \GeneaLabs\LaravelGovernor\Nova\GovernorRole::class, 42 | // \GeneaLabs\LaravelGovernor\Nova\GovernorUser::class, 43 | // \GeneaLabs\LaravelGovernor\Nova\GovernorTeam::class, 44 | // \GeneaLabs\LaravelGovernor\Nova\GovernorTeamInvitation::class, 45 | // ]); 46 | // }); 47 | // } 48 | // } 49 | 50 | // protected function routes() 51 | // { 52 | // if ($this->app->routesAreCached()) { 53 | // return; 54 | // } 55 | 56 | // Route::middleware(['nova', Authorize::class]) 57 | // ->prefix("genealabs/laravel-governor/nova") 58 | // ->namespace($this->namespace . "\Nova") 59 | // ->group(__DIR__ . '/../../routes/nova.php'); 60 | // } 61 | 62 | // public function register() 63 | // { 64 | // // 65 | // } 66 | } 67 | -------------------------------------------------------------------------------- /src/Providers/Route.php: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 12 | $this->mapWebRoutes(); 13 | } 14 | 15 | protected function mapWebRoutes() 16 | { 17 | app("router") 18 | ->prefix(config('genealabs-laravel-governor.url-prefix')) 19 | ->middleware('web') 20 | ->as('genealabs.laravel-governor.') 21 | ->group(__DIR__ . '/../../routes/web.php'); 22 | } 23 | 24 | protected function mapApiRoutes() 25 | { 26 | app("router") 27 | ->prefix('api' . config('genealabs-laravel-governor.url-prefix')) 28 | ->middleware([ 29 | "auth:api", 30 | "bindings", 31 | ]) 32 | ->namespace($this->namespace . "\Api") 33 | ->group(__DIR__ . '/../../routes/api.php'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Providers/Service.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . '/../../config/config.php', 'genealabs-laravel-governor'); 28 | $this->commands(Publish::class); 29 | } 30 | 31 | public function boot(): void 32 | { 33 | $this->app 34 | ->singleton('governor-actions', function () { 35 | $actionClass = app(config('genealabs-laravel-governor.models.action')); 36 | 37 | return (new $actionClass) 38 | ->orderBy("name") 39 | ->get(); 40 | }); 41 | $this->app 42 | ->singleton('governor-entities', function () { 43 | $entityClass = app(config('genealabs-laravel-governor.models.entity')); 44 | 45 | return (new $entityClass) 46 | ->select("name") 47 | ->with("group:name") 48 | ->orderBy("name") 49 | ->toBase() 50 | ->get(); 51 | }); 52 | $this->app 53 | ->singleton("governor-permissions", function () { 54 | $permissionClass = config("genealabs-laravel-governor.models.permission"); 55 | 56 | return (new $permissionClass) 57 | ->with("role", "team") 58 | ->toBase() 59 | ->get(); 60 | }); 61 | $this->app 62 | ->singleton("governor-roles", function () { 63 | $roleClass = config("genealabs-laravel-governor.models.role"); 64 | 65 | return (new $roleClass) 66 | ->select('name') 67 | ->toBase() 68 | ->get(); 69 | }); 70 | 71 | $teamClass = config("genealabs-laravel-governor.models.team"); 72 | $invitationClass = config("genealabs-laravel-governor.models.invitation"); 73 | app('events')->listen('eloquent.created: *', CreatedListener::class); 74 | app('events')->listen('eloquent.creating: *', CreatingListener::class); 75 | app('events')->listen('eloquent.saving: *', CreatingListener::class); 76 | app('events')->listen("eloquent.created: {$invitationClass}", CreatedInvitationListener::class); 77 | app('events')->listen("eloquent.created: {$teamClass}", CreatedTeamListener::class); 78 | app('events')->listen("eloquent.creating: {$invitationClass}", CreatingInvitationListener::class); 79 | $this->publishes([ 80 | __DIR__ . '/../../config/config.php' => config_path('genealabs-laravel-governor.php') 81 | ], 'config'); 82 | $this->publishes([ 83 | __DIR__ . '/../../dist' => public_path('vendor/genealabs/laravel-governor') 84 | ], 'assets'); 85 | $this->publishes([ 86 | __DIR__ . '/../../resources/views' => base_path('resources/views/vendor/genealabs-laravel-governor') 87 | ], 'views'); 88 | $this->publishes([ 89 | __DIR__ . '/../../database/migrations' => base_path('database/migrations') 90 | ], 'migrations'); 91 | $this->loadViewsFrom(__DIR__ . '/../../resources/views', 'genealabs-laravel-governor'); 92 | $this->loadViewComponentsAs('governor', [ 93 | MenuBar::class, 94 | ]); 95 | $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations'); 96 | 97 | $this->app 98 | ->make(Kernel::class) 99 | ->pushMiddleware(ParseCustomPolicyActions::class); 100 | } 101 | 102 | public function provides(): array 103 | { 104 | return []; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Role.php: -------------------------------------------------------------------------------- 1 | 'required|min:3|unique:governor_roles,name', 17 | 'description' => 'required|min:25', 18 | ]; 19 | protected $fillable = [ 20 | 'name', 21 | 'description', 22 | ]; 23 | protected $table = "governor_roles"; 24 | 25 | public $incrementing = false; 26 | 27 | public function entities(): HasMany 28 | { 29 | return $this->hasMany( 30 | config('genealabs-laravel-governor.models.entity'), 31 | "entity_name" 32 | ); 33 | } 34 | 35 | public function permissions(): HasMany 36 | { 37 | return $this->hasMany( 38 | config('genealabs-laravel-governor.models.permission'), 39 | 'role_name' 40 | ); 41 | } 42 | 43 | public function users(): BelongsToMany 44 | { 45 | return $this->belongsToMany( 46 | config('genealabs-laravel-governor.models.auth'), 47 | 'governor_role_user', 48 | 'role_name', 49 | 'user_id' 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Team.php: -------------------------------------------------------------------------------- 1 | 'required|min:3', 19 | 'description' => 'string', 20 | ]; 21 | protected $fillable = [ 22 | 'name', 23 | 'description', 24 | ]; 25 | protected $table = "governor_teams"; 26 | 27 | public function invitations(): HasMany 28 | { 29 | return $this->hasMany( 30 | config('genealabs-laravel-governor.models.invitation'), 31 | "team_id" 32 | ); 33 | } 34 | 35 | public function owner(): BelongsTo 36 | { 37 | $authClass = config("genealabs-laravel-governor.models.auth"); 38 | 39 | return $this->belongsTo($authClass, "governor_owned_by", "id"); 40 | } 41 | 42 | public function members(): BelongsToMany 43 | { 44 | return $this->belongsToMany( 45 | config('genealabs-laravel-governor.models.auth'), 46 | "governor_team_user", 47 | "team_id", 48 | "user_id" 49 | ); 50 | } 51 | 52 | public function permissions(): HasMany 53 | { 54 | return $this->hasMany( 55 | config('genealabs-laravel-governor.models.permission') 56 | ); 57 | } 58 | 59 | public function getOwnerNameAttribute(): string 60 | { 61 | return $this->owner->name 62 | ?? ""; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/TeamInvitation.php: -------------------------------------------------------------------------------- 1 | belongsTo( 25 | config('genealabs-laravel-governor.models.team') 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Traits/EntityManagement.php: -------------------------------------------------------------------------------- 1 | last(); 19 | $entityName = str_replace('Policy', '', $policyClass); 20 | $entityName = trim(implode(" ", preg_split('/(?=[A-Z])/', $entityName))); 21 | 22 | if (! Str::contains($policyClassName, "App")) { 23 | $nameSpaceParts->shift(); 24 | $packageName = $nameSpaceParts->shift(); 25 | $packageName = trim(implode(" ", preg_split('/(?=[A-Z])/', $packageName))); 26 | $entityName .= " ({$packageName})"; 27 | } 28 | 29 | $entity = app("governor-entities") 30 | ->where("name", ucwords($entityName)) 31 | ->first(); 32 | 33 | if (! $entity) { 34 | $entity = (new $entityClass)->firstOrCreate([ 35 | "name" => ucwords($entityName), 36 | ]); 37 | } 38 | 39 | return $entity->name; 40 | } 41 | 42 | public function getEntityFromModel(string $modelClass): string 43 | { 44 | $policy = app(Gate::class) 45 | ->getPolicyFor($modelClass); 46 | 47 | if (! $policy) { 48 | return ""; 49 | } 50 | 51 | return $this->getEntity(get_class($policy)); 52 | } 53 | 54 | protected function getPolicies(): Collection 55 | { 56 | $gate = app('Illuminate\Contracts\Auth\Access\Gate'); 57 | $reflectedGate = new ReflectionObject($gate); 58 | $policies = $reflectedGate->getProperty("policies"); 59 | $policies->setAccessible(true); 60 | 61 | return collect($policies->getValue($gate)); 62 | } 63 | 64 | public function parsePolicies(): void 65 | { 66 | $this->getPolicies() 67 | ->each(function ($policyClassName) { 68 | $this->getEntity($policyClassName); 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Traits/Governable.php: -------------------------------------------------------------------------------- 1 | getEntityFromModel(get_class($this)); 19 | $ownerships = $this->getOwnershipsForEntity($entityName, $ability); 20 | 21 | return $this->filterQuery($query, $ownerships->pluck("ownership_name")); 22 | } 23 | 24 | protected function filterQuery(Builder $query, Collection $ownerships): Builder 25 | { 26 | if ( 27 | $ownerships->contains("any") 28 | || auth()->user()?->hasRole("SuperAdmin") 29 | ) { 30 | return $query; 31 | } 32 | 33 | if ($ownerships->contains("own")) { 34 | $authModel = config("genealabs-laravel-governor.models.auth"); 35 | $authTable = (new $authModel)->getTable(); 36 | 37 | if (method_exists($query->getModel(), "teams")) { 38 | if ($query->getModel()->getTable() === $authTable) { 39 | return $query 40 | ->whereHas("teams", function ($query) { 41 | $query->whereIn("governor_team_user.user_id", auth()->user()->teams->pluck("id")); 42 | }) 43 | ->orWhere($query->getModel()->getKeyName(), auth()->user()->getKey()); 44 | } 45 | 46 | return $query 47 | ->whereHas("teams", function ($query) { 48 | $query->whereIn("governor_teamables.team_id", auth()->user()->teams->pluck("id")); 49 | }) 50 | ->orWhere( 51 | "{$query->getModel()->getTable()}.governor_owned_by", 52 | auth()->user()->getKey() 53 | ); 54 | } 55 | 56 | if ($query->getModel()->getTable() === $authTable) { 57 | return $query->where( 58 | $query->getModel()->getKeyName(), 59 | auth()->user()->getKey(), 60 | ); 61 | } 62 | 63 | return $query->where( 64 | "{$query->getModel()->getTable()}.governor_owned_by", 65 | auth()->user()->getKey(), 66 | ); 67 | } 68 | 69 | return $query->whereRaw("1 = 2"); 70 | } 71 | 72 | protected function getOwnershipsForEntity( 73 | string $entityName, 74 | string $ability, 75 | ): Collection { 76 | if (! $entityName) { 77 | return collect(); 78 | } 79 | 80 | return app("governor-permissions") 81 | ->where("action_name", $ability) 82 | ->where("entity_name", $entityName); 83 | } 84 | 85 | public function ownedBy(): BelongsTo 86 | { 87 | return $this->belongsTo( 88 | config("genealabs-laravel-governor.models.auth"), 89 | "governor_owned_by" 90 | ); 91 | } 92 | 93 | public function teams(): MorphToMany 94 | { 95 | return $this->MorphToMany( 96 | config("genealabs-laravel-governor.models.team"), 97 | "teamable", 98 | "governor_teamables" 99 | ); 100 | } 101 | 102 | public function scopeDeletable(Builder $query): Builder 103 | { 104 | return $this->applyPermissionToQuery($query, "delete"); 105 | } 106 | 107 | public function scopeForceDeletable(Builder $query): Builder 108 | { 109 | return $this->applyPermissionToQuery($query, "forceDelete"); 110 | } 111 | 112 | public function scopeRestorable(Builder $query): Builder 113 | { 114 | return $this->applyPermissionToQuery($query, "restore"); 115 | } 116 | 117 | public function scopeUpdatable(Builder $query): Builder 118 | { 119 | return $this->applyPermissionToQuery($query, "update"); 120 | } 121 | 122 | public function scopeViewable(Builder $query): Builder 123 | { 124 | return $this->applyPermissionToQuery($query, "view"); 125 | } 126 | 127 | public function scopeViewAnyable(Builder $query): Builder 128 | { 129 | return $this->applyPermissionToQuery($query, "viewAny"); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Traits/Governing.php: -------------------------------------------------------------------------------- 1 | where("name", $name) 19 | ->first(); 20 | 21 | if (! $role) { 22 | return false; 23 | } 24 | 25 | $this->loadMissing("roles"); 26 | 27 | return $this->roles->contains($role->name) 28 | || $this->roles->contains("SuperAdmin"); 29 | } 30 | 31 | public function roles(): BelongsToMany 32 | { 33 | $roleClass = config("genealabs-laravel-governor.models.role"); 34 | 35 | return $this->belongsToMany($roleClass, 'governor_role_user', 'user_id', 'role_name'); 36 | } 37 | 38 | public function ownedTeams(): HasMany 39 | { 40 | return $this->hasMany( 41 | config("genealabs-laravel-governor.models.team"), 42 | "governor_owned_by" 43 | ); 44 | } 45 | 46 | public function teams(): BelongsToMany 47 | { 48 | return $this->belongsToMany( 49 | config('genealabs-laravel-governor.models.team'), 50 | "governor_team_user", 51 | "user_id", 52 | "team_id" 53 | ); 54 | } 55 | 56 | public function getPermissionsAttribute(): Collection 57 | { 58 | $roleNames = $this->roles->pluck('name'); 59 | 60 | return app("governor-permissions") 61 | ->whereIn('role_name', $roleNames); 62 | } 63 | 64 | public function getEffectivePermissionsAttribute(): Collection 65 | { 66 | $results = collect(); 67 | $groupedPermissions = $this->permissions 68 | ->groupBy(function ($permission) { 69 | return $permission->entity_name . "|" . $permission->action_name; 70 | }); 71 | 72 | foreach ($groupedPermissions as $entityAction => $permissions) { 73 | $permission = $permissions->first(); 74 | $permission->role_name = null; 75 | $permission->team_name = null; 76 | 77 | if ($permissions->pluck("ownership_name")->contains("any")) { 78 | $permission->ownership_name = "any"; 79 | $results = $results->push($permission); 80 | } 81 | 82 | if ($permissions->pluck("ownership_name")->contains("own")) { 83 | $permission->ownership_name = "own"; 84 | $results = $results->push($permission); 85 | } 86 | } 87 | 88 | return $results; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Traits/GovernorOwnedByField.php: -------------------------------------------------------------------------------- 1 | getProperty('policies'); 20 | $property->setAccessible(true); 21 | $protectedClass = collect($property->getValue($gate)) 22 | ->flip() 23 | ->get(get_class($policy)); 24 | 25 | if (! $protectedClass) { 26 | return false; 27 | } 28 | 29 | $model = new $protectedClass; 30 | 31 | return $this->createGovernorOwnedByFields($model); 32 | } 33 | 34 | protected function createGovernorOwnedByFields(Model $model): bool 35 | { 36 | $class = new ReflectionClass($model); 37 | 38 | if ( 39 | (! in_array("GeneaLabs\\LaravelGovernor\\Traits\\Governable", class_uses_recursive($model)) 40 | && ! in_array("GeneaLabs\\LaravelGovernor\\Traits\\Governing", class_uses_recursive($model))) 41 | || $class->isAbstract() 42 | ) { 43 | return false; 44 | } 45 | 46 | $connection = $model 47 | ->getConnection() 48 | ->getName(); 49 | 50 | $governorOwnedByExists = Schema::connection($connection) 51 | ->hasColumn($model->getTable(), 'governor_owned_by'); 52 | 53 | if ($governorOwnedByExists) { 54 | return false; 55 | } 56 | 57 | Schema::connection($connection)->table($model->getTable(), function (Blueprint $table) { 58 | $authModelPrimaryKeyType = config("genealabs-laravel-governor.auth-model-primary-key-type", "bigInteger"); 59 | $fieldType = "unsigned"; 60 | 61 | switch (strtolower($authModelPrimaryKeyType)) { 62 | case "integer": 63 | $fieldType .= "Integer"; 64 | break; 65 | default: 66 | $fieldType .= "BigInteger"; 67 | break; 68 | } 69 | 70 | $table->{$fieldType}('governor_owned_by') 71 | ->nullable(); 72 | }); 73 | 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/User.php: -------------------------------------------------------------------------------- 1 | menuBarItems = json_decode(json_encode([ 14 | [ 15 | "label" => "Roles", 16 | "url" => route("genealabs.laravel-governor.roles.index"), 17 | ], 18 | [ 19 | "label" => "Groups", 20 | "url" => route("genealabs.laravel-governor.groups.index"), 21 | ], 22 | [ 23 | "label" => "Teams", 24 | "url" => route("genealabs.laravel-governor.teams.index"), 25 | ], 26 | [ 27 | "label" => "Assignments", 28 | "url" => route("genealabs.laravel-governor.assignments.create"), 29 | ], 30 | ])); 31 | } 32 | 33 | public function render() 34 | { 35 | return view('genealabs-laravel-governor::components.menu-bar'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tools/phpcs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/tools/phpcs -------------------------------------------------------------------------------- /tools/phpmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/tools/phpmd -------------------------------------------------------------------------------- /tools/phpstan: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/tools/phpstan -------------------------------------------------------------------------------- /tools/phpunit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-governor/311891c1fccb770273f7c6a27713429b820b6121/tools/phpunit -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | mix 4 | .setPublicPath('dist') 5 | .js('resources/js/tool.js', 'js') 6 | .js('resources/js/governor.js', 'js') 7 | .sass('resources/sass/tool.scss', 'css') 8 | .copy('./node_modules/vue-multiselect/dist/vue-multiselect.min.css', 'css') 9 | .version() 10 | ; 11 | --------------------------------------------------------------------------------