diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 0000000..3c99342
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,17 @@
+name: Laravel.php ACL
+
+on: push
+
+jobs:
+ cd:
+ runs-on: ubuntu-latest
+ steps:
+ - name: cd
+ uses: tripteki/cd-package@1.0.0
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ repotoken: ${{ secrets.REPOSITORY_TOKEN }}
+ repouser: tripteki
+ repository: https://packagist.org
+ language: php
+ artifact: composer.json
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..061b3a5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Trip Teknologi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d65cae5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,141 @@
+
ACL
+
+This package provides implementation of Access Control List (ACL) Roles-Permissions in repository pattern for Lumen and Laravel besides REST API starterpack of admin management with no intervention to codebase and keep clean.
+
+Getting Started
+---
+
+Installation :
+
+```
+$ composer require tripteki/laravelphp-acl
+```
+
+How to use it :
+
+- Read detail optional instruction here [Lumen](https://spatie.be/docs/laravel-permission/installation-lumen) or [Laravel](https://spatie.be/docs/laravel-permission/installation-laravel).
+
+- Put to any of your model ruleable.
+
+```php
+
+/**
+ * @var array
+ */
+protected $dispatchesEvents = [
+
+ "created" => \Tripteki\ACL\Events\Created::class,
+ "deleted" => \Tripteki\ACL\Events\Deleted::class,
+ "restored" => \Tripteki\ACL\Events\Created::class,
+ "forceDeleted" => \Tripteki\ACL\Events\Deleted::class,
+];
+```
+
+- Put `Tripteki\ACL\Providers\ACLServiceProvider` to service provider configuration list.
+
+- Put `Tripteki\ACL\Providers\ACLServiceProvider::ignoreConfig()` into `register` provider, then publish config file into your project's directory with running :
+
+```
+php artisan vendor:publish --tag=tripteki-laravelphp-acl
+```
+
+- Put `Tripteki\ACL\Providers\ACLServiceProvider::ignoreMigrations()` into `register` provider, then publish migrations file into your project's directory with running (optionally) :
+
+```
+php artisan vendor:publish --tag=tripteki-laravelphp-acl-migrations
+```
+
+- Migrate.
+
+```
+$ php artisan migrate
+```
+
+- Emit Event-Listener.
+
+```
+php artisan queue:work
+```
+
+- Publish tests file into your project's directory with running (optionally) :
+
+```
+php artisan vendor:publish --tag=tripteki-laravelphp-acl-tests
+```
+
+- Sample :
+
+```php
+use Tripteki\ACL\Contracts\Repository\Admin\IACLRoleRepository;
+use Tripteki\ACL\Contracts\Repository\Admin\IACLPermissionRepository;
+use Tripteki\ACL\Contracts\Repository\IACLRepository;
+
+$roleRepository = app(IACLRoleRepository::class);
+$permissionRepository = app(IACLPermissionRepository::class);
+
+/*
+ * As `{resource}`.`{action}`.`{target}` is representing :
+ *
+ * - {resource} : 'posts' = 'posts.*' = 'posts.*.*'
+ * - {action} : 'viewAny', 'view', 'create', 'update', 'delete'
+ * - {target} : '[identifier]'
+ */
+
+// $permissionRepository->rule("posts.update.*"); //
+// $permissionRepository->unrule("posts.update.*"); //
+// $permissionRepository->get("posts.update.*"); //
+// $permissionRepository->all(); //
+
+// $roleRepository->rule("admin"); //
+// $roleRepository->rule("user"); //
+// $roleRepository->unrule("admin"); //
+// $roleRepository->unrule("user"); //
+// $roleRepository->get("admin"); //
+// $roleRepository->get("user"); //
+// $roleRepository->all(); //
+
+// $roleRepository->forRole("admin"); //
+// $roleRepository->grant("posts.update.*"); //
+// $roleRepository->revoke("posts.update.*"); //
+// $roleRepository->ability("posts.update.*"); //
+// $roleRepository->permissions(); //
+
+$repository = app(IACLRepository::class);
+// $repository->setUser(...); //
+// $repository->getUser(); //
+
+// $repository->grantAs("admin"); //
+// $repository->revokeAs("admin"); //
+// $repository->is("admin"); //
+// $repository->permissions(); //
+// $repository->grant("posts.update.5"); //
+// $repository->revoke("posts.update.5"); //
+// $repository->owns(); //
+// $repository->can(iacl(\App\Models\Post::class, "update", 5)); //
+// $repository->can("posts.update.5"); //
+// auth()->user()->can("posts.update.5"); //
+// auth()->user()->canAny([ "posts.update.5", ]); //
+// auth()->user()->cant("posts.update.5"); //
+// auth()->user()->cantAny([ "posts.update.5", ]); //
+```
+
+- Generate swagger files into your project's directory with putting this into your annotation configuration (optionally) :
+
+```
+base_path("app/Http/Controllers/ACL")
+```
+
+```
+base_path("app/Http/Controllers/Admin/ACL")
+```
+
+Usage
+---
+
+`php artisan adminer:install:acl`
+
+Author
+---
+
+- Trip Teknologi ([@tripteki](https://linkedin.com/company/tripteki))
+- Hasby Maulana ([@hsbmaulana](https://linkedin.com/in/hsbmaulana))
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..98a89fd
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,63 @@
+{
+ "name": "tripteki/laravelphp-acl",
+ "version": "1.0.0",
+ "description": "Trip Teknologi's Laravel.php ACLs",
+
+ "readme": "README.md",
+ "license": "MIT",
+ "authors": [ { "name": "Trip Teknologi", "email": "tripteki.company@gmail.com" } ],
+ "homepage": "https://github.com/tripteki/laravelphp-acl",
+ "support": { "issues": "https://github.com/tripteki/laravelphp-acl/issues" },
+
+ "require": {
+
+ "php": "^8.0.2",
+
+ "tripteki/laravelphp-repository": "^1.0.0",
+ "tripteki/laravelphp-helpers": "^1.0.0",
+ "tripteki/laravelphp-adminer": "^1.0.0",
+ "tripteki/laravelphp-import-export": "^1.0.0",
+ "tripteki/laravelphp-request-response-query": "^1.0.0",
+
+ "spatie/laravel-permission": "^5.8.0"
+ },
+
+ "require-dev": {},
+
+ "suggest": {
+
+ "laravel/lumen-framework": "Required when using lumen framework (^9.0).",
+ "laravel/framework": "Required when using laravel framework (^9.0)."
+ },
+
+ "autoload": {
+
+ "files": [
+
+ "src/Helpers/IACL.php",
+ "src/Helpers/Accesses.php"
+ ],
+
+ "psr-4": {
+
+ "Tripteki\\ACL\\": "src/"
+ }
+ },
+
+ "autoload-dev": {},
+
+ "extra": {
+
+ "laravel": {
+
+ "dont-discover": [],
+
+ "providers": [
+
+ "Tripteki\\ACL\\Providers\\ACLServiceProvider"
+ ],
+
+ "aliases": []
+ }
+ }
+}
diff --git a/config/acl.php b/config/acl.php
new file mode 100644
index 0000000..88de699
--- /dev/null
+++ b/config/acl.php
@@ -0,0 +1,74 @@
+ [
+
+ "permission" => Tripteki\ACL\Models\Admin\Permission::class,
+ "role" => Tripteki\ACL\Models\Admin\Role::class,
+ ],
+
+ "table_names" => [
+
+ "permissions" => "acl_permissions",
+ "roles" => "acl_roles",
+
+ "model_has_permissions" => "acl_user_has_permissions",
+ "model_has_roles" => "acl_user_has_roles",
+ "role_has_permissions" => "acl_role_has_permissions",
+ ],
+
+ /**
+ * Owns :
+ *
+ * "index" => "viewAny"
+ * "show" => "view"
+ * "create" => "create"
+ * "store" => "create"
+ * "edit" => "update"
+ * "update" => "update"
+ * "destroy" => "delete"
+ */
+ "own_resources" => [
+
+ "show",
+ "update",
+ "destroy",
+ ],
+
+
+
+ "column_names" => [
+
+ "role_pivot_key" => null,
+
+ "permission_pivot_key" => null,
+
+ "model_morph_key" => "model_id",
+
+ "team_foreign_key" => "team_id",
+ ],
+
+ "teams" => false,
+
+
+
+ "enable_wildcard_permission" => true,
+
+ "register_permission_check_method" => true,
+
+ "display_permission_in_exception" => false,
+
+ "display_role_in_exception" => false,
+
+ "cache" => [
+
+ "expiration_time" => \DateInterval::createFromDateString("24 hours"),
+
+ "key" => Str::slug(env("APP_NAME"), "_")."_acl",
+
+ "store" => "default",
+ ],
+];
diff --git a/database/migrations/2023_01_20_000000_create_roles_permissions_table.php b/database/migrations/2023_01_20_000000_create_roles_permissions_table.php
new file mode 100644
index 0000000..7f0a823
--- /dev/null
+++ b/database/migrations/2023_01_20_000000_create_roles_permissions_table.php
@@ -0,0 +1,184 @@
+keytype = app(AuthModelContract::class)->getKeyType();
+ $this->role = app(IACLRoleRepository::class);
+ $this->permission = app(IACLPermissionRepository::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function up()
+ {
+ $keytype = $this->keytype;
+ $tableNames = config("permission.table_names");
+ $columnNames = config("permission.column_names");
+ $teams = config("permission.teams");
+
+ if (empty($tableNames)) {
+
+ throw new \Exception("Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.");
+ }
+
+ if ($teams && empty($columnNames["team_foreign_key"] ?? null)) {
+
+ throw new \Exception("Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.");
+ }
+
+ Schema::create($tableNames["permissions"], function (Blueprint $table) {
+
+ $table->uuid("id");
+ $table->string("name");
+ $table->string("guard_name");
+ $table->timestamps();
+ $table->unique([ "name", "guard_name", ]);
+
+ $table->primary("id");
+ });
+
+ Schema::create($tableNames["roles"], function (Blueprint $table) use ($teams, $columnNames) {
+
+ $table->uuid("id");
+
+ if ($teams || config("permission.testing")) {
+
+ $table->unsignedBigInteger($columnNames["team_foreign_key"])->nullable();
+ $table->index($columnNames["team_foreign_key"], "roles_team_foreign_key_index");
+ }
+
+ $table->string("name");
+ $table->string("guard_name");
+ $table->timestamps();
+
+ if ($teams || config("permission.testing")) {
+
+ $table->unique([ $columnNames["team_foreign_key"], "name", "guard_name", ]);
+
+ } else {
+
+ $table->unique([ "name", "guard_name", ]);
+ }
+
+ $table->primary("id");
+ });
+
+ Schema::create($tableNames["model_has_permissions"], function (Blueprint $table) use ($keytype, $tableNames, $columnNames, $teams) {
+
+ $table->string("model_type");
+
+ if ($keytype == "int") $table->unsignedBigInteger($columnNames["model_morph_key"]);
+ else if ($keytype == "string") $table->uuid($columnNames["model_morph_key"]);
+
+ $table->index([ $columnNames["model_morph_key"], "model_type", ], "model_has_permissions_model_id_model_type_index");
+ if ($keytype == "int") $table->foreignId(PermissionRegistrar::$pivotPermission)->constrained($tableNames["permissions"])->onUpdate("cascade")->onDelete("cascade");
+ else if ($keytype == "string") $table->foreignUuid(PermissionRegistrar::$pivotPermission)->references("id")->on($tableNames["permissions"])->onUpdate("cascade")->onDelete("cascade");
+
+ if ($teams) {
+
+ $table->unsignedBigInteger($columnNames["team_foreign_key"]);
+ $table->index($columnNames["team_foreign_key"], "model_has_permissions_team_foreign_key_index");
+ $table->primary([ $columnNames["team_foreign_key"], PermissionRegistrar::$pivotPermission, $columnNames["model_morph_key"], "model_type", ], "model_has_permissions_permission_model_type_primary");
+
+ } else {
+
+ $table->primary([ PermissionRegistrar::$pivotPermission, $columnNames["model_morph_key"], "model_type", ], "model_has_permissions_permission_model_type_primary");
+ }
+ });
+
+ Schema::create($tableNames["model_has_roles"], function (Blueprint $table) use ($keytype, $tableNames, $columnNames, $teams) {
+
+ $table->string("model_type");
+
+ if ($keytype == "int") $table->unsignedBigInteger($columnNames["model_morph_key"]);
+ else if ($keytype == "string") $table->uuid($columnNames["model_morph_key"]);
+
+ $table->index([ $columnNames["model_morph_key"], "model_type", ], "model_has_roles_model_id_model_type_index");
+ if ($keytype == "int") $table->foreignId(PermissionRegistrar::$pivotRole)->constrained($tableNames["roles"])->onUpdate("cascade")->onDelete("cascade");
+ else if ($keytype == "string") $table->foreignUuid(PermissionRegistrar::$pivotRole)->references("id")->on($tableNames["roles"])->onUpdate("cascade")->onDelete("cascade");
+
+ if ($teams) {
+
+ $table->unsignedBigInteger($columnNames["team_foreign_key"]);
+ $table->index($columnNames["team_foreign_key"], "model_has_roles_team_foreign_key_index");
+ $table->primary([ $columnNames["team_foreign_key"], PermissionRegistrar::$pivotRole, $columnNames["model_morph_key"], "model_type", ], "model_has_roles_role_model_type_primary");
+
+ } else {
+
+ $table->primary([ PermissionRegistrar::$pivotRole, $columnNames["model_morph_key"], "model_type", ], "model_has_roles_role_model_type_primary");
+ }
+ });
+
+ Schema::create($tableNames["role_has_permissions"], function (Blueprint $table) use ($keytype, $tableNames) {
+
+ if ($keytype == "int") {
+
+ $table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
+ $table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
+
+ } else if ($keytype == "string") {
+
+ $table->uuid(PermissionRegistrar::$pivotPermission);
+ $table->uuid(PermissionRegistrar::$pivotRole);
+ }
+
+ $table->foreign(PermissionRegistrar::$pivotPermission, "acl_role_has_permissions_permission_id_foreign")->references("id")->on($tableNames["permissions"])->onUpdate("cascade")->onDelete("cascade");
+ $table->foreign(PermissionRegistrar::$pivotRole, "acl_role_has_permissions_role_id_foreign")->references("id")->on($tableNames["roles"])->onUpdate("cascade")->onDelete("cascade");
+
+ $table->primary([ PermissionRegistrar::$pivotPermission, PermissionRegistrar::$pivotRole, ], "role_has_permissions_permission_id_role_id_primary");
+ });
+
+ app("cache")->store(config("permission.cache.store") != "default" ? config("permission.cache.store") : null)->forget(config("permission.cache.key"));
+
+ $this->role->rule(ACLServiceProvider::SUPERUSER);
+ }
+
+ /**
+ * @return void
+ */
+ public function down()
+ {
+ $tableNames = config("permission.table_names");
+
+ if (empty($tableNames)) {
+
+ throw new \Exception("Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.");
+ }
+
+ Schema::drop($tableNames["role_has_permissions"]);
+ Schema::drop($tableNames["model_has_roles"]);
+ Schema::drop($tableNames["model_has_permissions"]);
+ Schema::drop($tableNames["roles"]);
+ Schema::drop($tableNames["permissions"]);
+ }
+};
diff --git a/database/migrations/2023_01_20_000001_add_teams_fields.php.stub b/database/migrations/2023_01_20_000001_add_teams_fields.php.stub
new file mode 100644
index 0000000..4d6bf57
--- /dev/null
+++ b/database/migrations/2023_01_20_000001_add_teams_fields.php.stub
@@ -0,0 +1,100 @@
+unsignedBigInteger($columnNames["team_foreign_key"])->nullable()->after("id");
+ $table->index($columnNames["team_foreign_key"], "roles_team_foreign_key_index");
+ $table->dropUnique("roles_name_guard_name_unique");
+ $table->unique([ $columnNames["team_foreign_key"], "name", "guard_name", ]);
+ });
+ }
+
+ if (! Schema::hasColumn($tableNames["model_has_permissions"], $columnNames["team_foreign_key"])) {
+
+ Schema::table($tableNames["model_has_permissions"], function (Blueprint $table) use ($tableNames, $columnNames) {
+
+ $table->unsignedBigInteger($columnNames["team_foreign_key"])->default("1");
+ $table->index($columnNames["team_foreign_key"], "model_has_permissions_team_foreign_key_index");
+
+ if (DB::getDriverName() !== "sqlite") {
+
+ $table->dropForeign([ PermissionRegistrar::$pivotPermission, ]);
+ }
+
+ $table->dropPrimary();
+ $table->primary([ $columnNames["team_foreign_key"], PermissionRegistrar::$pivotPermission, $columnNames["model_morph_key"], "model_type", ], "model_has_permissions_permission_model_type_primary");
+
+ if (DB::getDriverName() !== "sqlite") {
+
+ $table->foreignUuid(PermissionRegistrar::$pivotPermission)->references("id")->on($tableNames["permissions"])->onUpdate("cascade")->onDelete("cascade");
+ }
+ });
+ }
+
+ if (! Schema::hasColumn($tableNames["model_has_roles"], $columnNames["team_foreign_key"])) {
+
+ Schema::table($tableNames["model_has_roles"], function (Blueprint $table) use ($tableNames, $columnNames) {
+
+ $table->unsignedBigInteger($columnNames["team_foreign_key"])->default("1");;
+ $table->index($columnNames["team_foreign_key"], "model_has_roles_team_foreign_key_index");
+
+ if (DB::getDriverName() !== "sqlite") {
+
+ $table->dropForeign([ PermissionRegistrar::$pivotRole, ]);
+ }
+
+ $table->dropPrimary();
+ $table->primary([ $columnNames["team_foreign_key"], PermissionRegistrar::$pivotRole, $columnNames["model_morph_key"], "model_type", ], "model_has_roles_role_model_type_primary");
+
+ if (DB::getDriverName() !== "sqlite") {
+
+ $table->foreignUuid(PermissionRegistrar::$pivotRole)->references("id")->on($tableNames["roles"])->onDelete("cascade");
+ }
+ });
+ }
+
+ app("cache")->store(config("permission.cache.store") != "default" ? config("permission.cache.store") : null)->forget(config("permission.cache.key"));
+ }
+
+ /**
+ * @return void
+ */
+ public function down()
+ {
+ //
+ }
+};
diff --git a/src/Console/Commands/InstallCommand.php b/src/Console/Commands/InstallCommand.php
new file mode 100644
index 0000000..5fe7a03
--- /dev/null
+++ b/src/Console/Commands/InstallCommand.php
@@ -0,0 +1,81 @@
+helper = $helper;
+ }
+
+ /**
+ * @return int
+ */
+ public function handle()
+ {
+ $this->installStack();
+
+ return 0;
+ }
+
+ /**
+ * @return int|null
+ */
+ protected function installStack()
+ {
+ (new Filesystem)->ensureDirectoryExists(base_path("routes/user"));
+ (new Filesystem)->ensureDirectoryExists(base_path("routes/admin"));
+ (new Filesystem)->copy(__DIR__."/../../../stubs/routes/user/acl.php", base_path("routes/user/acl.php"));
+ (new Filesystem)->copy(__DIR__."/../../../stubs/routes/admin/acl.php", base_path("routes/admin/acl.php"));
+ $this->helper->putRoute("api.php", "user/acl.php");
+ $this->helper->putRoute("api.php", "admin/acl.php");
+
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Controllers/ACL"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Controllers/ACL", app_path("Http/Controllers/ACL"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Requests/ACLs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Requests/ACLs", app_path("Http/Requests/ACLs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Controllers/Admin/ACL"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Controllers/Admin/ACL", app_path("Http/Controllers/Admin/ACL"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Imports/ACLs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Imports/ACLs", app_path("Imports/ACLs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Exports/ACLs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Exports/ACLs", app_path("Exports/ACLs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Requests/Admin/ACLs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Requests/Admin/ACLs", app_path("Http/Requests/Admin/ACLs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Responses"));
+
+ $this->helper->putTrait($this->helper->classToFile(get_class(app(AuthModelContract::class))), \Tripteki\ACL\Traits\RolePermissionTrait::class);
+ $this->helper->putMiddleware(null, "role", \Tripteki\ACL\Http\Middleware\RoleMiddleware::class);
+ $this->helper->putMiddleware(null, "permission", \Tripteki\ACL\Http\Middleware\PermissionMiddleware::class);
+ $this->helper->putMiddleware(null, "role_or_permission", \Tripteki\ACL\Http\Middleware\RoleOrPermissionMiddleware::class);
+
+ $this->info("Adminer ACL scaffolding installed successfully.");
+ }
+};
diff --git a/src/Contracts/Repository/Admin/IACLPermissionRepository.php b/src/Contracts/Repository/Admin/IACLPermissionRepository.php
new file mode 100644
index 0000000..b7ca69b
--- /dev/null
+++ b/src/Contracts/Repository/Admin/IACLPermissionRepository.php
@@ -0,0 +1,13 @@
+user = Auth::check() ? Auth::user() : null;
+ $this->model = $model;
+ }
+};
diff --git a/src/Events/Deleted.php b/src/Events/Deleted.php
new file mode 100644
index 0000000..3547679
--- /dev/null
+++ b/src/Events/Deleted.php
@@ -0,0 +1,32 @@
+user = Auth::check() ? Auth::user() : null;
+ $this->model = $model;
+ }
+};
diff --git a/src/Events/Granted.php b/src/Events/Granted.php
new file mode 100644
index 0000000..ad96cde
--- /dev/null
+++ b/src/Events/Granted.php
@@ -0,0 +1,24 @@
+data = $data;
+ }
+};
diff --git a/src/Events/Granting.php b/src/Events/Granting.php
new file mode 100644
index 0000000..4147a4b
--- /dev/null
+++ b/src/Events/Granting.php
@@ -0,0 +1,24 @@
+data = $data;
+ }
+};
diff --git a/src/Events/Revoked.php b/src/Events/Revoked.php
new file mode 100644
index 0000000..a91a8bb
--- /dev/null
+++ b/src/Events/Revoked.php
@@ -0,0 +1,24 @@
+data = $data;
+ }
+};
diff --git a/src/Events/Revoking.php b/src/Events/Revoking.php
new file mode 100644
index 0000000..cd4968b
--- /dev/null
+++ b/src/Events/Revoking.php
@@ -0,0 +1,24 @@
+data = $data;
+ }
+};
diff --git a/src/Helpers/Accesses.php b/src/Helpers/Accesses.php
new file mode 100644
index 0000000..9a6f84b
--- /dev/null
+++ b/src/Helpers/Accesses.php
@@ -0,0 +1,41 @@
+setUser($user);
+
+ } else {
+
+ if (Auth::check()) {
+
+ $repository->setUser(Auth::user());
+ }
+ }
+
+ if ($repository->getUser() instanceof $class && in_array(RolePermissionTrait::class, class_uses($class))) {
+
+ $accesses = array_merge($repository->permissions()->toArray(), $repository->owns()->toArray());
+ }
+
+ return AccessResource::collection($accesses)->resolve();
+ };
+}
diff --git a/src/Helpers/IACL.php b/src/Helpers/IACL.php
new file mode 100644
index 0000000..109d9a9
--- /dev/null
+++ b/src/Helpers/IACL.php
@@ -0,0 +1,18 @@
+resource["name"]);
+ $resources = explode(",", $accesses[0]);
+ $actions = explode(",", $accesses[1]);
+ $targets = explode(",", $accesses[2]);
+
+ $ables = [];
+
+ foreach ($resources as $resource) {
+
+ foreach ($actions as $action) {
+
+ foreach ($targets as $target) {
+
+ $ables[$resource][$target][$this->resource["id"]][] = $action;
+ }
+ }
+ }
+
+ return $ables;
+ }
+};
diff --git a/src/Listeners/RevokeUnruleListener.php b/src/Listeners/RevokeUnruleListener.php
new file mode 100644
index 0000000..2019734
--- /dev/null
+++ b/src/Listeners/RevokeUnruleListener.php
@@ -0,0 +1,75 @@
+role = $role;
+ $this->permission = $permission;
+ $this->acl = $acl;
+
+ $this->own = $own;
+ }
+
+ /**
+ * @param \Tripteki\ACL\Events\Deleted $event
+ * @return void
+ */
+ public function handle(Deleted $event)
+ {
+ $this->acl->setUser($event->user);
+
+ if ($this->acl->getUser()) {
+
+ foreach ($this->own->scope($event->model) as $able) {
+
+ $this->acl->revoke($able);
+ $this->permission->unrule($able);
+ }
+ }
+ }
+};
diff --git a/src/Listeners/RuleGrantListener.php b/src/Listeners/RuleGrantListener.php
new file mode 100644
index 0000000..3190775
--- /dev/null
+++ b/src/Listeners/RuleGrantListener.php
@@ -0,0 +1,75 @@
+role = $role;
+ $this->permission = $permission;
+ $this->acl = $acl;
+
+ $this->own = $own;
+ }
+
+ /**
+ * @param \Tripteki\ACL\Events\Created $event
+ * @return void
+ */
+ public function handle(Created $event)
+ {
+ $this->acl->setUser($event->user);
+
+ if ($this->acl->getUser()) {
+
+ foreach ($this->own->scope($event->model) as $able) {
+
+ $this->permission->rule($able);
+ $this->acl->grant($able);
+ }
+ }
+ }
+};
diff --git a/src/Models/Admin/Permission.php b/src/Models/Admin/Permission.php
new file mode 100644
index 0000000..a57eeec
--- /dev/null
+++ b/src/Models/Admin/Permission.php
@@ -0,0 +1,11 @@
+ \Tripteki\ACL\Repositories\Eloquent\ACLRepository::class,
+ \Tripteki\ACL\Contracts\Repository\Admin\IACLRoleRepository::class => \Tripteki\ACL\Repositories\Eloquent\Admin\ACLRoleRepository::class,
+ \Tripteki\ACL\Contracts\Repository\Admin\IACLPermissionRepository::class => \Tripteki\ACL\Repositories\Eloquent\Admin\ACLPermissionRepository::class,
+ ];
+
+ /**
+ * @var string
+ */
+ public const SUPERUSER = "superuser";
+
+ /**
+ * @var bool
+ */
+ public static $loadConfig = true;
+
+ /**
+ * @var bool
+ */
+ public static $runsMigrations = true;
+
+ /**
+ * @return bool
+ */
+ public static function shouldLoadConfig()
+ {
+ return static::$loadConfig;
+ }
+
+ /**
+ * @return bool
+ */
+ public static function shouldRunMigrations()
+ {
+ return static::$runsMigrations;
+ }
+
+ /**
+ * @return void
+ */
+ public static function ignoreConfig()
+ {
+ static::$loadConfig = false;
+ }
+
+ /**
+ * @return void
+ */
+ public static function ignoreMigrations()
+ {
+ static::$runsMigrations = false;
+ }
+
+ /**
+ * @return void
+ */
+ public function boot()
+ {
+ parent::boot();
+
+ $this->dataEventListener();
+
+ $this->registerSuperuser();
+ $this->registerPublishers();
+ $this->registerConfigs();
+ $this->registerCommands();
+ $this->registerMigrations();
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerSuperuser()
+ {
+ Gate::before(function ($user, $ability) {
+
+ return $user->hasRole(ACLServiceProvider::SUPERUSER) ? true : null;
+ });
+
+ // Gate::after(function ($user, $ability) { //
+
+ // return $user->hasRole(ACLServiceProvider::SUPERUSER); //
+ // }); //
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerConfigs()
+ {
+ if (static::shouldLoadConfig()) {
+
+ $this->app["config"]->set("permission", []);
+ $this->mergeConfigFrom(__DIR__."/../../config/acl.php", "permission");
+ }
+
+ $this->app->bind(PermissionContract::class, function ($app) {
+
+ $config = $app->config["permission.models"];
+
+ return $app->make($config["permission"]);
+ });
+
+ $this->app->bind(RoleContract::class, function ($app) {
+
+ $config = $app->config["permission.models"];
+
+ return $app->make($config["role"]);
+ });
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerCommands()
+ {
+ if (! $this->app->isProduction() && $this->app->runningInConsole()) {
+
+ $this->commands(
+ [
+ InstallCommand::class,
+ ]);
+ }
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerMigrations()
+ {
+ if ($this->app->runningInConsole() && static::shouldRunMigrations()) {
+
+ $this->loadMigrationsFrom(__DIR__."/../../database/migrations");
+ }
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerPublishers()
+ {
+ $this->publishes(
+ [
+ __DIR__."/../../config/acl.php" => config_path("permission.php"),
+ ],
+
+ "tripteki-laravelphp-acl");
+
+ if (! static::shouldRunMigrations()) {
+
+ $this->publishes(
+ [
+ __DIR__."/../../database/migrations" => database_path("migrations"),
+ ],
+
+ "tripteki-laravelphp-acl-migrations");
+ }
+
+ $this->publishes(
+ [
+ __DIR__."/../../stubs/tests/Feature/ACL/ACLTest.stub" => base_path("tests/Feature/ACL/ACLTest.php"),
+ ],
+
+ "tripteki-laravelphp-acl-tests");
+ }
+
+ /**
+ * @return void
+ */
+ public function dataEventListener()
+ {
+ Permission::observe(UniqueIdObserver::class);
+ Role::observe(UniqueIdObserver::class);
+
+ Event::listen(Created::class, [ RuleGrantListener::class, "handle", ]);
+ Event::listen(Deleted::class, [ RevokeUnruleListener::class, "handle", ]);
+ }
+};
diff --git a/src/Repositories/Eloquent/ACLRepository.php b/src/Repositories/Eloquent/ACLRepository.php
new file mode 100644
index 0000000..c5c4ce2
--- /dev/null
+++ b/src/Repositories/Eloquent/ACLRepository.php
@@ -0,0 +1,203 @@
+role = $role;
+ $this->permission = $permission;
+ }
+
+ /**
+ * @param array $querystring|[]
+ * @return mixed
+ */
+ public function all($querystring = [])
+ {
+ $querystringed =
+ [
+ "limit" => $querystring["limit"] ?? request()->query("limit", 10),
+ "current_page" => $querystring["current_page"] ?? request()->query("current_page", 1),
+ ];
+ extract($querystringed);
+
+ $content = $this->user;
+ $content = $content->setRelation("roles",
+ QueryBuilder::for($content->roles())->
+ defaultSort("name")->
+ allowedSorts([ "name", "guard_name", ])->
+ allowedFilters([ "name", "guard_name", ])->
+ paginate($limit, [ "*", ], "current_page", $current_page)->appends(empty($querystring) ? request()->query() : $querystringed));
+ $content = $content->loadCount("roles");
+
+ return collect($content)->only([ "roles_count", "roles", ]);
+ }
+
+ /**
+ * @param string|int $role
+ * @return mixed
+ */
+ public function grantAs($role)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = $this->user->assignRole($this->role->get($role));
+
+ DB::commit();
+
+ event(new Granted($content));
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string|int $role
+ * @return mixed
+ */
+ public function revokeAs($role)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = $this->user->removeRole($this->role->get($role));
+
+ DB::commit();
+
+ event(new Revoked($content));
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string $role
+ * @return bool
+ */
+ public function is($role)
+ {
+ return $this->user->hasRole($role);
+ }
+
+ /**
+ * @param string|int $permission
+ * @return mixed
+ */
+ public function grant($permission)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = $this->user->givePermissionTo($this->permission->get($permission));
+
+ DB::commit();
+
+ event(new Granted($content));
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string|int $permission
+ * @return mixed
+ */
+ public function revoke($permission)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = $this->user->revokePermissionTo($this->permission->get($permission));
+
+ DB::commit();
+
+ event(new Revoked($content));
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string $permission
+ * @return bool
+ */
+ public function can($permission)
+ {
+ return $this->user->can($permission);
+ }
+
+ /**
+ * @return \Illuminate\Support\Collection
+ */
+ public function permissions()
+ {
+ return $this->user->getPermissionsViaRoles();
+ }
+
+ /**
+ * @return \Illuminate\Support\Collection
+ */
+ public function owns()
+ {
+ return $this->user->getDirectPermissions();
+ }
+};
diff --git a/src/Repositories/Eloquent/Admin/ACLPermissionRepository.php b/src/Repositories/Eloquent/Admin/ACLPermissionRepository.php
new file mode 100644
index 0000000..c894368
--- /dev/null
+++ b/src/Repositories/Eloquent/Admin/ACLPermissionRepository.php
@@ -0,0 +1,127 @@
+ $querystring["limit"] ?? request()->query("limit", 10),
+ "current_page" => $querystring["current_page"] ?? request()->query("current_page", 1),
+ ];
+ extract($querystringed);
+
+ $content = QueryBuilder::for(app(PermissionModel::class)->query())->
+ defaultSort("name")->
+ allowedSorts([ "name", "guard_name", ])->
+ allowedFilters([ "name", "guard_name", ])->
+ paginate($limit, [ "*", ], "current_page", $current_page)->appends(empty($querystring) ? request()->query() : $querystringed);
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @param array $querystring|[]
+ * @return mixed
+ */
+ public function get($identifier, $querystring = [])
+ {
+ $querystringed =
+ [
+ "limit" => $querystring["limit"] ?? request()->query("limit", 10),
+ "current_page" => $querystring["current_page"] ?? request()->query("current_page", 1),
+ ];
+ extract($querystringed);
+
+ $content = app(PermissionModel::class)->findByName($identifier);
+ $content = $content->setRelation("roles",
+ QueryBuilder::for($content->roles())->
+ defaultSort("name")->
+ allowedSorts([ "name", "guard_name", ])->
+ allowedFilters([ "name", "guard_name", ])->
+ paginate($limit, [ "*", ], "current_page", $current_page)->appends(empty($querystring) ? request()->query() : $querystringed));
+ $content = $content->loadCount("roles");
+
+ return $content;
+ }
+
+ /**
+ * @param array $data
+ * @return mixed
+ */
+ public function create($data)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = app(PermissionModel::class)->create($data);
+
+ DB::commit();
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @return mixed
+ */
+ public function delete($identifier)
+ {
+ $content = app(PermissionModel::class)->findByName($identifier);
+
+ DB::beginTransaction();
+
+ try {
+
+ $content->delete();
+
+ DB::commit();
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string $permission
+ * @return mixed
+ */
+ public function rule($permission)
+ {
+ return $this->create([ "name" => $permission, ]);
+ }
+
+ /**
+ * @param string $permission
+ * @return mixed
+ */
+ public function unrule($permission)
+ {
+ return $this->delete($permission);
+ }
+};
diff --git a/src/Repositories/Eloquent/Admin/ACLRoleRepository.php b/src/Repositories/Eloquent/Admin/ACLRoleRepository.php
new file mode 100644
index 0000000..c57e94e
--- /dev/null
+++ b/src/Repositories/Eloquent/Admin/ACLRoleRepository.php
@@ -0,0 +1,233 @@
+setRole(app(RoleModel::class)->findByName($role));
+
+ return $this->getRole();
+ }
+
+ /**
+ * @param \Illuminate\Database\Eloquent\Model $role
+ * @return void
+ */
+ protected function setRole(\Illuminate\Database\Eloquent\Model $role)
+ {
+ $this->role = $role;
+ }
+
+ /**
+ * @return \Illuminate\Database\Eloquent\Model
+ */
+ protected function getRole()
+ {
+ return $this->role;
+ }
+
+ /**
+ * @param array $querystring|[]
+ * @return mixed
+ */
+ public function all($querystring = [])
+ {
+ $querystringed =
+ [
+ "limit" => $querystring["limit"] ?? request()->query("limit", 10),
+ "current_page" => $querystring["current_page"] ?? request()->query("current_page", 1),
+ ];
+ extract($querystringed);
+
+ $content = QueryBuilder::for(app(RoleModel::class)->query())->
+ defaultSort("name")->
+ allowedSorts([ "name", "guard_name", ])->
+ allowedFilters([ "name", "guard_name", ])->
+ paginate($limit, [ "*", ], "current_page", $current_page)->appends(empty($querystring) ? request()->query() : $querystringed);
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @param array $querystring|[]
+ * @return mixed
+ */
+ public function get($identifier, $querystring = [])
+ {
+ $querystringed =
+ [
+ "limit" => $querystring["limit"] ?? request()->query("limit", 10),
+ "current_page" => $querystring["current_page"] ?? request()->query("current_page", 1),
+ ];
+ extract($querystringed);
+
+ $content = app(RoleModel::class)->findByName($identifier);
+ $content = $content->setRelation("permissions",
+ QueryBuilder::for($content->permissions())->
+ defaultSort("name")->
+ allowedSorts([ "name", "guard_name", ])->
+ allowedFilters([ "name", "guard_name", ])->
+ paginate($limit, [ "*", ], "current_page", $current_page)->appends(empty($querystring) ? request()->query() : $querystringed));
+ $content = $content->loadCount("permissions");
+
+ return $content;
+ }
+
+ /**
+ * @param array $data
+ * @return mixed
+ */
+ public function create($data)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = app(RoleModel::class)->create($data);
+
+ DB::commit();
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @return mixed
+ */
+ public function delete($identifier)
+ {
+ $content = app(RoleModel::class)->findByName($identifier);
+
+ DB::beginTransaction();
+
+ try {
+
+ $content->delete();
+
+ DB::commit();
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string $role
+ * @return mixed
+ */
+ public function rule($role)
+ {
+ return $this->create([ "name" => $role, ]);
+ }
+
+ /**
+ * @param string $role
+ * @return mixed
+ */
+ public function unrule($role)
+ {
+ return $this->delete($role);
+ }
+
+ /**
+ * @param string|int $permission
+ * @return mixed
+ */
+ public function grant($permission)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = $this->role->givePermissionTo($permission);
+
+ DB::commit();
+
+ event(new Granted($content));
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string|int $permission
+ * @return mixed
+ */
+ public function revoke($permission)
+ {
+ $content = null;
+
+ DB::beginTransaction();
+
+ try {
+
+ $content = $this->role->revokePermissionTo($permission);
+
+ DB::commit();
+
+ event(new Revoked($content));
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string $permission
+ * @return bool
+ */
+ public function ability($permission)
+ {
+ return $this->role->hasPermissionTo($permission);
+ }
+
+ /**
+ * @return \Illuminate\Support\Collection
+ */
+ public function permissions()
+ {
+ return $this->role->permissions()->get();
+ }
+};
diff --git a/src/Repositories/QueryBuilder/.gitkeep b/src/Repositories/QueryBuilder/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/Scopes/OwnScope.php b/src/Scopes/OwnScope.php
new file mode 100644
index 0000000..d6871b8
--- /dev/null
+++ b/src/Scopes/OwnScope.php
@@ -0,0 +1,49 @@
+resourceAbilityMap())->only(config("permission.own_resources"));
+ $target = $model->{$model->getKeyName()};
+
+ if (config("permission.enable_wildcard_permission")) {
+
+ $able = $action->implode(",");
+
+ return [
+
+ $resource.".".$able.".".$target,
+ ];
+
+ } else {
+
+ return $action->map(function ($able) use ($resource, $target) {
+
+ return $resource.".".$able.".".$target;
+
+ })->toArray();
+ }
+ }
+};
diff --git a/src/Traits/RolePermissionTrait.php b/src/Traits/RolePermissionTrait.php
new file mode 100644
index 0000000..1d18f89
--- /dev/null
+++ b/src/Traits/RolePermissionTrait.php
@@ -0,0 +1,10 @@
+user());
+
+ return iresponse($data, $statecode);
+ }
+};
diff --git a/stubs/app/Http/Controllers/Admin/ACL/ACLAdminController.php b/stubs/app/Http/Controllers/Admin/ACL/ACLAdminController.php
new file mode 100644
index 0000000..e4877f8
--- /dev/null
+++ b/stubs/app/Http/Controllers/Admin/ACL/ACLAdminController.php
@@ -0,0 +1,134 @@
+aclRoleAdminRepository = $aclRoleAdminRepository;
+ $this->aclUserAdminRepository = $aclUserAdminRepository;
+ }
+
+ /**
+ * @OA\Put(
+ * path="/admin/acls/{context}/{object}",
+ * tags={"Admin ACL Rule"},
+ * summary="rule",
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="context",
+ * schema={"type": "string", "enum": {"grant_permissions_to_role", "revoke_permissions_from_role", "grant_roles_to_user", "revoke_roles_from_user"}},
+ * description="ACL's Context."
+ * ),
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="object",
+ * description="ACL's Object."
+ * ),
+ * @OA\RequestBody(
+ * @OA\MediaType(
+ * mediaType="application/x-www-form-urlencoded",
+ * @OA\Schema(
+ * @OA\Property(
+ * property="rules[]",
+ * type="array",
+ * collectionFormat="multi",
+ * @OA\Items(type="string"),
+ * description="ACL's Rules."
+ * )
+ * )
+ * )
+ * ),
+ * @OA\Response(
+ * response=201,
+ * description="Created."
+ * ),
+ * @OA\Response(
+ * response=422,
+ * description="Unprocessable Entity."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\ACLValidation $request
+ * @param string $context
+ * @param string $object
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function rule(ACLValidation $request, $context, $object)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 202;
+
+ if ($context == ACLValidation::GRANT_PERMISSIONS_TO_ROLE || $context == ACLValidation::REVOKE_PERMISSIONS_FROM_ROLE) {
+
+ $this->aclRoleAdminRepository->forRole($object);
+
+ foreach ($form["rules"] as $rule) {
+
+ if ($context == ACLValidation::GRANT_PERMISSIONS_TO_ROLE) {
+
+ $data[] = $this->aclRoleAdminRepository->grant($rule);
+
+ } else if ($context == ACLValidation::REVOKE_PERMISSIONS_FROM_ROLE) {
+
+ $data[] = $this->aclRoleAdminRepository->revoke($rule);
+ }
+ }
+
+ } else if ($context == ACLValidation::GRANT_ROLES_TO_USER || $context == ACLValidation::REVOKE_ROLES_FROM_USER) {
+
+ $this->aclUserAdminRepository->setUser(app(AuthModelContract::class)->findOrFail($object));
+
+ foreach ($form["rules"] as $rule) {
+
+ if ($context == ACLValidation::GRANT_ROLES_TO_USER) {
+
+ $data[] = $this->aclUserAdminRepository->grantAs($rule);
+
+ } else if ($context == ACLValidation::REVOKE_ROLES_FROM_USER) {
+
+ $data[] = $this->aclUserAdminRepository->revokeAs($rule);
+ }
+ }
+ }
+
+ if ($data) {
+
+ $statecode = 201;
+ }
+
+ return iresponse($data, $statecode);
+ }
+};
diff --git a/stubs/app/Http/Controllers/Admin/ACL/PermissionAdminController.php b/stubs/app/Http/Controllers/Admin/ACL/PermissionAdminController.php
new file mode 100644
index 0000000..c2b8df3
--- /dev/null
+++ b/stubs/app/Http/Controllers/Admin/ACL/PermissionAdminController.php
@@ -0,0 +1,204 @@
+permissionAdminRepository = $permissionAdminRepository;
+ }
+
+ /**
+ * @OA\Get(
+ * path="/admin/acls/permissions",
+ * tags={"Admin ACL Permission"},
+ * summary="Index",
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="limit",
+ * description="ACL Permission's Pagination Limit."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="current_page",
+ * description="ACL Permission's Pagination Current Page."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="order",
+ * description="ACL Permission's Pagination Order."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="filter[]",
+ * description="ACL Permission's Pagination Filter."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * )
+ * )
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function index(Request $request)
+ {
+ $data = [];
+ $statecode = 200;
+
+ $data = $this->permissionAdminRepository->all();
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Get(
+ * path="/admin/acls/permissions/{permission}",
+ * tags={"Admin ACL Permission"},
+ * summary="Show",
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="permission",
+ * description="ACL Permission's Permission."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\Permissions\PermissionShowValidation $request
+ * @param string $permission
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function show(PermissionShowValidation $request, $permission)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 200;
+
+ $data = $this->permissionAdminRepository->get($permission);
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Post(
+ * path="/admin/acls/permissions",
+ * tags={"Admin ACL Permission"},
+ * summary="Store",
+ * @OA\RequestBody(
+ * @OA\MediaType(
+ * mediaType="application/x-www-form-urlencoded",
+ * @OA\Schema(
+ * @OA\Property(
+ * property="permission",
+ * type="string",
+ * description="ACL Permission's Permission."
+ * )
+ * )
+ * )
+ * ),
+ * @OA\Response(
+ * response=201,
+ * description="Created."
+ * ),
+ * @OA\Response(
+ * response=422,
+ * description="Unprocessable Entity."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\Permissions\PermissionStoreValidation $request
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function store(PermissionStoreValidation $request)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 202;
+
+ $data = $this->permissionAdminRepository->rule($form["permission"]);
+
+ if ($data) {
+
+ $statecode = 201;
+ }
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Delete(
+ * path="/admin/acls/permissions/{permission}",
+ * tags={"Admin ACL Permission"},
+ * summary="Destroy",
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="permission",
+ * description="ACL Permission's Permission."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * ),
+ * @OA\Response(
+ * response=422,
+ * description="Unprocessable Entity."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\Permissions\PermissionDestroyValidation $request
+ * @param string $permission
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function destroy(PermissionDestroyValidation $request, $permission)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 202;
+
+ $data = $this->permissionAdminRepository->unrule($permission);
+
+ if ($data) {
+
+ $statecode = 200;
+ }
+
+ return iresponse($data, $statecode);
+ }
+};
diff --git a/stubs/app/Http/Controllers/Admin/ACL/RoleAdminController.php b/stubs/app/Http/Controllers/Admin/ACL/RoleAdminController.php
new file mode 100644
index 0000000..324013d
--- /dev/null
+++ b/stubs/app/Http/Controllers/Admin/ACL/RoleAdminController.php
@@ -0,0 +1,204 @@
+roleAdminRepository = $roleAdminRepository;
+ }
+
+ /**
+ * @OA\Get(
+ * path="/admin/acls/roles",
+ * tags={"Admin ACL Role"},
+ * summary="Index",
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="limit",
+ * description="ACL Role's Pagination Limit."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="current_page",
+ * description="ACL Role's Pagination Current Page."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="order",
+ * description="ACL Role's Pagination Order."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="filter[]",
+ * description="ACL Role's Pagination Filter."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * )
+ * )
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function index(Request $request)
+ {
+ $data = [];
+ $statecode = 200;
+
+ $data = $this->roleAdminRepository->all();
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Get(
+ * path="/admin/acls/roles/{role}",
+ * tags={"Admin ACL Role"},
+ * summary="Show",
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="role",
+ * description="ACL Role's Role."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\Roles\RoleShowValidation $request
+ * @param string $role
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function show(RoleShowValidation $request, $role)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 200;
+
+ $data = $this->roleAdminRepository->get($role);
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Post(
+ * path="/admin/acls/roles",
+ * tags={"Admin ACL Role"},
+ * summary="Store",
+ * @OA\RequestBody(
+ * @OA\MediaType(
+ * mediaType="application/x-www-form-urlencoded",
+ * @OA\Schema(
+ * @OA\Property(
+ * property="role",
+ * type="string",
+ * description="ACL Role's Role."
+ * )
+ * )
+ * )
+ * ),
+ * @OA\Response(
+ * response=201,
+ * description="Created."
+ * ),
+ * @OA\Response(
+ * response=422,
+ * description="Unprocessable Entity."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\Roles\RoleStoreValidation $request
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function store(RoleStoreValidation $request)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 202;
+
+ $data = $this->roleAdminRepository->rule($form["role"]);
+
+ if ($data) {
+
+ $statecode = 201;
+ }
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Delete(
+ * path="/admin/acls/roles/{role}",
+ * tags={"Admin ACL Role"},
+ * summary="Destroy",
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="role",
+ * description="ACL Role's Role."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * ),
+ * @OA\Response(
+ * response=422,
+ * description="Unprocessable Entity."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\Roles\RoleDestroyValidation $request
+ * @param string $role
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function destroy(RoleDestroyValidation $request, $role)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 202;
+
+ $data = $this->roleAdminRepository->unrule($role);
+
+ if ($data) {
+
+ $statecode = 200;
+ }
+
+ return iresponse($data, $statecode);
+ }
+};
diff --git a/stubs/app/Http/Controllers/Admin/ACL/UserAdminController.php b/stubs/app/Http/Controllers/Admin/ACL/UserAdminController.php
new file mode 100644
index 0000000..a032730
--- /dev/null
+++ b/stubs/app/Http/Controllers/Admin/ACL/UserAdminController.php
@@ -0,0 +1,68 @@
+userAdminRepository = $userAdminRepository;
+ }
+
+ /**
+ * @OA\Get(
+ * path="/admin/acls/users/{user}",
+ * tags={"Admin ACL User"},
+ * summary="Show",
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="user",
+ * description="ACL User's User."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\ACLs\Users\UserShowValidation $request
+ * @param string $user
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function show(UserShowValidation $request, $user)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 200;
+
+ $user = app(AuthModelContract::class)->findOrFail($user);
+
+ $this->userAdminRepository->setUser($user);
+
+ $data = $this->userAdminRepository->all();
+
+ return iresponse($data, $statecode);
+ }
+};
diff --git a/stubs/app/Http/Requests/ACLs/.gitkeep b/stubs/app/Http/Requests/ACLs/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/app/Http/Requests/Admin/ACLs/ACLValidation.php b/stubs/app/Http/Requests/Admin/ACLs/ACLValidation.php
new file mode 100644
index 0000000..1fd8897
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/ACLValidation.php
@@ -0,0 +1,87 @@
+ $this->route("context"),
+ "object" => $this->route("object"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ $provider = app(AuthModelContract::class);
+
+ $validator = [
+
+ "context" => "required|string|in:".self::GRANT_PERMISSIONS_TO_ROLE.",".self::REVOKE_PERMISSIONS_FROM_ROLE.",".self::GRANT_ROLES_TO_USER.",".self::REVOKE_ROLES_FROM_USER,
+ "rules" => "required|array",
+ "rules.*" => [],
+ "object" => [ "required", "string", ],
+ ];
+
+ if ($this->route("context") == self::GRANT_PERMISSIONS_TO_ROLE || $this->route("context") == self::REVOKE_PERMISSIONS_FROM_ROLE) {
+
+ $validator["rules.*"] = [
+
+ Rule::exists(config("permission.models.permission"), "name"),
+ ];
+
+ $validator["object"][] = "exists:".config("permission.models.role").",name";
+
+ } else if ($this->route("context") == self::GRANT_ROLES_TO_USER || $this->route("context") == self::REVOKE_ROLES_FROM_USER) {
+
+ $validator["rules.*"] = [
+
+ Rule::exists(config("permission.models.role"), "name"),
+ ];
+
+ $validator["object"][] = "exists:".get_class($provider).",".keyName($provider);
+ }
+
+ return $validator;
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionDestroyValidation.php b/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionDestroyValidation.php
new file mode 100644
index 0000000..7fd3c50
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionDestroyValidation.php
@@ -0,0 +1,38 @@
+ $this->route("permission"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+
+ "permission" => "required|string|exists:".config("permission.models.permission").",name",
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionShowValidation.php b/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionShowValidation.php
new file mode 100644
index 0000000..a89bd73
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionShowValidation.php
@@ -0,0 +1,38 @@
+ $this->route("permission"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+
+ "permission" => "required|string|exists:".config("permission.models.permission").",name",
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionStoreValidation.php b/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionStoreValidation.php
new file mode 100644
index 0000000..fbd4149
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/Permissions/PermissionStoreValidation.php
@@ -0,0 +1,27 @@
+
+ */
+ public function rules()
+ {
+ return [
+
+ "permission" => "required|string|max:127|regex:/^[a-zA-Z\._]+$/|unique:".config("permission.models.permission").",name",
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleDestroyValidation.php b/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleDestroyValidation.php
new file mode 100644
index 0000000..5e497f5
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleDestroyValidation.php
@@ -0,0 +1,38 @@
+ $this->route("role"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+
+ "role" => "required|string|exists:".config("permission.models.role").",name",
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleShowValidation.php b/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleShowValidation.php
new file mode 100644
index 0000000..799ff5a
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleShowValidation.php
@@ -0,0 +1,38 @@
+ $this->route("role"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+
+ "role" => "required|string|exists:".config("permission.models.role").",name",
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleStoreValidation.php b/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleStoreValidation.php
new file mode 100644
index 0000000..f6ad5fa
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/Roles/RoleStoreValidation.php
@@ -0,0 +1,27 @@
+
+ */
+ public function rules()
+ {
+ return [
+
+ "role" => "required|string|max:127|regex:/^[a-zA-Z\._]+$/|unique:".config("permission.models.role").",name",
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/ACLs/Users/UserShowValidation.php b/stubs/app/Http/Requests/Admin/ACLs/Users/UserShowValidation.php
new file mode 100644
index 0000000..eee292c
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/ACLs/Users/UserShowValidation.php
@@ -0,0 +1,41 @@
+ $this->route("user"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ $provider = app(AuthModelContract::class);
+
+ return [
+
+ "user" => "required|string|exists:".get_class($provider).",".keyName($provider),
+ ];
+ }
+};
diff --git a/stubs/app/Http/Responses/.gitkeep b/stubs/app/Http/Responses/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/app/Imports/ACLs/.gitkeep b/stubs/app/Imports/ACLs/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/routes/admin/acl.php b/stubs/routes/admin/acl.php
new file mode 100644
index 0000000..fb5f051
--- /dev/null
+++ b/stubs/routes/admin/acl.php
@@ -0,0 +1,22 @@
+middleware(config("adminer.middleware.admin"))->group(function () {
+
+ /**
+ * ACLs.
+ */
+ Route::prefix("acls")->group(function () {
+
+ Route::apiResource("users", UserAdminController::class)->only("show")->parameters([ "users" => "user", ]);
+ Route::apiResource("roles", RoleAdminController::class)->except("update")->parameters([ "roles" => "role", ]);
+ Route::apiResource("permissions", PermissionAdminController::class)->except("update")->parameters([ "permissions" => "permission", ]);
+
+ Route::put("/{context}/{object}", [ ACLAdminController::class, "rule", ]);
+ });
+});
diff --git a/stubs/routes/user/acl.php b/stubs/routes/user/acl.php
new file mode 100644
index 0000000..649e5f4
--- /dev/null
+++ b/stubs/routes/user/acl.php
@@ -0,0 +1,12 @@
+middleware(config("adminer.middleware.user"))->group(function () {
+
+ /**
+ * ACLs.
+ */
+ Route::get("acls", [ ACLController::class, "index", ]);
+});
diff --git a/stubs/tests/Feature/ACL/ACLTest.stub b/stubs/tests/Feature/ACL/ACLTest.stub
new file mode 100644
index 0000000..35fea51
--- /dev/null
+++ b/stubs/tests/Feature/ACL/ACLTest.stub
@@ -0,0 +1,24 @@
+user();
+ $this->actingAs($user);
+
+ $data = accesses();
+ $this->assertIsArray($data);
+ }
+};