From b3501be6ecc1c492749de735980f031f81535300 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Wed, 6 Nov 2024 09:09:10 +0100 Subject: [PATCH] Refactor api key permissions (#361) * use RESOURCE_NAME for requests * use RESOURCE_NAME for transformers * add permissions field to api key * add migration for new permissions field * update tests * remove debug log * set column type to "json" * remove default attribute to fix tests * fix default value for permissions * fix after merge * fix after merge * allow to "register" custom permissions * add "role" to default resource names * fix after merge * fix phpstan * fix migrations --- .../ApiKeyResource/Pages/CreateApiKey.php | 19 +- .../Api/StoreApplicationApiKeyRequest.php | 16 +- .../Allocations/DeleteAllocationRequest.php | 3 +- .../Allocations/GetAllocationsRequest.php | 3 +- .../Allocations/StoreAllocationRequest.php | 3 +- .../DeleteDatabaseHostRequest.php | 3 +- .../DatabaseHosts/GetDatabaseHostRequest.php | 3 +- .../StoreDatabaseHostRequest.php | 2 +- .../Api/Application/Eggs/GetEggRequest.php | 3 +- .../Api/Application/Eggs/GetEggsRequest.php | 3 +- .../Application/Mounts/DeleteMountRequest.php | 3 +- .../Application/Mounts/GetMountRequest.php | 3 +- .../Application/Mounts/StoreMountRequest.php | 3 +- .../Application/Nodes/DeleteNodeRequest.php | 3 +- .../Api/Application/Nodes/GetNodesRequest.php | 3 +- .../Application/Nodes/StoreNodeRequest.php | 2 +- .../Application/Roles/DeleteRoleRequest.php | 3 +- .../Api/Application/Roles/GetRoleRequest.php | 3 +- .../Application/Roles/StoreRoleRequest.php | 3 +- .../Databases/GetServerDatabaseRequest.php | 3 +- .../Databases/GetServerDatabasesRequest.php | 3 +- .../Databases/StoreServerDatabaseRequest.php | 3 +- .../Servers/GetExternalServerRequest.php | 3 +- .../Application/Servers/GetServerRequest.php | 3 +- .../Servers/ServerWriteRequest.php | 3 +- .../Servers/StoreServerRequest.php | 2 +- .../Servers/UpdateServerStartupRequest.php | 2 +- .../Application/Users/DeleteUserRequest.php | 3 +- .../Users/GetExternalUserRequest.php | 3 +- .../Api/Application/Users/GetUsersRequest.php | 3 +- .../Application/Users/StoreUserRequest.php | 2 +- app/Models/Allocation.php | 2 +- app/Models/ApiKey.php | 77 +++-- app/Models/Database.php | 2 +- app/Models/DatabaseHost.php | 2 +- app/Models/Egg.php | 2 +- app/Models/Node.php | 2 +- app/Models/Server.php | 2 +- app/Models/User.php | 2 +- app/Services/Acl/Api/AdminAcl.php | 40 +-- app/Services/Api/KeyCreationService.php | 2 +- app/Services/Nodes/NodeAutoDeployService.php | 5 +- .../Api/Application/AllocationTransformer.php | 5 +- .../Application/DatabaseHostTransformer.php | 5 +- .../Api/Application/EggTransformer.php | 5 +- .../Api/Application/MountTransformer.php | 10 +- .../Api/Application/NodeTransformer.php | 7 +- .../Application/ServerDatabaseTransformer.php | 3 +- .../Api/Application/ServerTransformer.php | 20 +- .../Application/ServerVariableTransformer.php | 4 +- .../Api/Application/SubuserTransformer.php | 7 +- .../Api/Application/UserTransformer.php | 6 +- database/Factories/ApiKeyFactory.php | 1 + ...095213_fix_missing_sqlite_foreign_keys.php | 302 +++++++++--------- ..._04_185326_revamp_api_keys_permissions.php | 94 ++++++ .../ApplicationApiIntegrationTestCase.php | 30 +- .../Api/Application/EggControllerTest.php | 3 +- .../Users/ExternalUserControllerTest.php | 3 +- .../Application/Users/UserControllerTest.php | 7 +- tests/Unit/Services/Acl/Api/AdminAclTest.php | 7 +- 60 files changed, 453 insertions(+), 321 deletions(-) create mode 100644 database/migrations/2024_11_04_185326_revamp_api_keys_permissions.php diff --git a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php index 11e1fae03..db6cac3b8 100644 --- a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php +++ b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php @@ -11,6 +11,7 @@ use Filament\Forms\Components\Textarea; use Filament\Forms\Components\ToggleButtons; use Filament\Forms\Form; use Filament\Resources\Pages\CreateRecord; +use Illuminate\Database\Eloquent\Model; class CreateApiKey extends CreateRecord { @@ -41,7 +42,7 @@ class CreateApiKey extends CreateRecord 'md' => 2, ]) ->schema( - collect(ApiKey::RESOURCES)->map(fn ($resource) => ToggleButtons::make("r_$resource") + collect(ApiKey::getPermissionList())->map(fn ($resource) => ToggleButtons::make('permissions_' . $resource) ->label(str($resource)->replace('_', ' ')->title())->inline() ->options([ 0 => 'None', @@ -87,4 +88,20 @@ class CreateApiKey extends CreateRecord ->columnSpanFull(), ]); } + + protected function handleRecordCreation(array $data): Model + { + $permissions = []; + + foreach (ApiKey::getPermissionList() as $permission) { + if (isset($data['permissions_' . $permission])) { + $permissions[$permission] = intval($data['permissions_' . $permission]); + unset($data['permissions_' . $permission]); + } + } + + $data['permissions'] = $permissions; + + return parent::handleRecordCreation($data); + } } diff --git a/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php b/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php index 83f80f267..13af92a0e 100644 --- a/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php +++ b/app/Http/Requests/Admin/Api/StoreApplicationApiKeyRequest.php @@ -3,7 +3,6 @@ namespace App\Http\Requests\Admin\Api; use App\Models\ApiKey; -use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Admin\AdminFormRequest; class StoreApplicationApiKeyRequest extends AdminFormRequest @@ -16,9 +15,12 @@ class StoreApplicationApiKeyRequest extends AdminFormRequest { $modelRules = ApiKey::getRules(); - return collect(AdminAcl::getResourceList())->mapWithKeys(function ($resource) use ($modelRules) { - return [AdminAcl::COLUMN_IDENTIFIER . $resource => $modelRules['r_' . $resource]]; - })->merge(['memo' => $modelRules['memo']])->toArray(); + $rules = [ + 'memo' => $modelRules['memo'], + 'permissions' => $modelRules['permissions'], + ]; + + return $rules; } public function attributes(): array @@ -30,8 +32,8 @@ class StoreApplicationApiKeyRequest extends AdminFormRequest public function getKeyPermissions(): array { - return collect($this->validated())->filter(function ($value, $key) { - return substr($key, 0, strlen(AdminAcl::COLUMN_IDENTIFIER)) === AdminAcl::COLUMN_IDENTIFIER; - })->toArray(); + $data = $this->validated(); + + return array_keys($data['permissions']); } } diff --git a/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php b/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php index 74077965e..f1d0104ee 100644 --- a/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php +++ b/app/Http/Requests/Api/Application/Allocations/DeleteAllocationRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Allocations; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Allocation; class DeleteAllocationRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_ALLOCATIONS; + protected ?string $resource = Allocation::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php b/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php index c59a66374..ee0695e28 100644 --- a/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php +++ b/app/Http/Requests/Api/Application/Allocations/GetAllocationsRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Allocations; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Allocation; class GetAllocationsRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_ALLOCATIONS; + protected ?string $resource = Allocation::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php b/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php index 397f72093..03b221e1f 100644 --- a/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php +++ b/app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Allocations; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Allocation; class StoreAllocationRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_ALLOCATIONS; + protected ?string $resource = Allocation::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Http/Requests/Api/Application/DatabaseHosts/DeleteDatabaseHostRequest.php b/app/Http/Requests/Api/Application/DatabaseHosts/DeleteDatabaseHostRequest.php index b92079067..275f5d451 100644 --- a/app/Http/Requests/Api/Application/DatabaseHosts/DeleteDatabaseHostRequest.php +++ b/app/Http/Requests/Api/Application/DatabaseHosts/DeleteDatabaseHostRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\DatabaseHosts; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\DatabaseHost; class DeleteDatabaseHostRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_DATABASE_HOSTS; + protected ?string $resource = DatabaseHost::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/DatabaseHosts/GetDatabaseHostRequest.php b/app/Http/Requests/Api/Application/DatabaseHosts/GetDatabaseHostRequest.php index 8cd876fc1..00831bcca 100644 --- a/app/Http/Requests/Api/Application/DatabaseHosts/GetDatabaseHostRequest.php +++ b/app/Http/Requests/Api/Application/DatabaseHosts/GetDatabaseHostRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\DatabaseHosts; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\DatabaseHost; class GetDatabaseHostRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_DATABASE_HOSTS; + protected ?string $resource = DatabaseHost::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php b/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php index 435a6d86b..b7c26f0e8 100644 --- a/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php +++ b/app/Http/Requests/Api/Application/DatabaseHosts/StoreDatabaseHostRequest.php @@ -8,7 +8,7 @@ use App\Http\Requests\Api\Application\ApplicationApiRequest; class StoreDatabaseHostRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_DATABASE_HOSTS; + protected ?string $resource = DatabaseHost::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Http/Requests/Api/Application/Eggs/GetEggRequest.php b/app/Http/Requests/Api/Application/Eggs/GetEggRequest.php index c5249b2ce..86a384ce7 100644 --- a/app/Http/Requests/Api/Application/Eggs/GetEggRequest.php +++ b/app/Http/Requests/Api/Application/Eggs/GetEggRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Eggs; use App\Http\Requests\Api\Application\ApplicationApiRequest; use App\Services\Acl\Api\AdminAcl; +use App\Models\Egg; class GetEggRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_EGGS; + protected ?string $resource = Egg::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Eggs/GetEggsRequest.php b/app/Http/Requests/Api/Application/Eggs/GetEggsRequest.php index 1d4ebe361..f8397103e 100644 --- a/app/Http/Requests/Api/Application/Eggs/GetEggsRequest.php +++ b/app/Http/Requests/Api/Application/Eggs/GetEggsRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Eggs; use App\Http\Requests\Api\Application\ApplicationApiRequest; use App\Services\Acl\Api\AdminAcl; +use App\Models\Egg; class GetEggsRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_EGGS; + protected ?string $resource = Egg::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php b/app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php index 927db27b5..5554cb4f5 100644 --- a/app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php +++ b/app/Http/Requests/Api/Application/Mounts/DeleteMountRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Mounts; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Mount; class DeleteMountRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_MOUNTS; + protected ?string $resource = Mount::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Mounts/GetMountRequest.php b/app/Http/Requests/Api/Application/Mounts/GetMountRequest.php index 4c5824245..a549afefb 100644 --- a/app/Http/Requests/Api/Application/Mounts/GetMountRequest.php +++ b/app/Http/Requests/Api/Application/Mounts/GetMountRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Mounts; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Mount; class GetMountRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_MOUNTS; + protected ?string $resource = Mount::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php b/app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php index fabc66f10..a8902970b 100644 --- a/app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php +++ b/app/Http/Requests/Api/Application/Mounts/StoreMountRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Mounts; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Mount; class StoreMountRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_MOUNTS; + protected ?string $resource = Mount::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php index 5a00f5831..e8bf5812b 100644 --- a/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/DeleteNodeRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Nodes; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Node; class DeleteNodeRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_NODES; + protected ?string $resource = Node::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php b/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php index 240902310..a68c0b457 100644 --- a/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/GetNodesRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Nodes; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Node; class GetNodesRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_NODES; + protected ?string $resource = Node::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php index eae324233..7e8404b0d 100644 --- a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php @@ -8,7 +8,7 @@ use App\Http\Requests\Api\Application\ApplicationApiRequest; class StoreNodeRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_NODES; + protected ?string $resource = Node::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php b/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php index 43c005fe8..e56cb741f 100644 --- a/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php +++ b/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Roles; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Role; class DeleteRoleRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_ROLES; + protected ?string $resource = Role::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Roles/GetRoleRequest.php b/app/Http/Requests/Api/Application/Roles/GetRoleRequest.php index 825ec9206..3c8136974 100644 --- a/app/Http/Requests/Api/Application/Roles/GetRoleRequest.php +++ b/app/Http/Requests/Api/Application/Roles/GetRoleRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Roles; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Role; class GetRoleRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_ROLES; + protected ?string $resource = Role::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php b/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php index 4cf01400e..0b2afedcb 100644 --- a/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php +++ b/app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Roles; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Role; class StoreRoleRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_ROLES; + protected ?string $resource = Role::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php index 1b1c42aaa..3576473b3 100644 --- a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php +++ b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabaseRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Servers\Databases; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Database; class GetServerDatabaseRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVER_DATABASES; + protected ?string $resource = Database::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php index 9296ea3d4..87b23c746 100644 --- a/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php +++ b/app/Http/Requests/Api/Application/Servers/Databases/GetServerDatabasesRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Servers\Databases; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Database; class GetServerDatabasesRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVER_DATABASES; + protected ?string $resource = Database::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php b/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php index 558834011..16ec4d716 100644 --- a/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php +++ b/app/Http/Requests/Api/Application/Servers/Databases/StoreServerDatabaseRequest.php @@ -9,10 +9,11 @@ use Illuminate\Database\Query\Builder; use App\Services\Acl\Api\AdminAcl; use App\Services\Databases\DatabaseManagementService; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Database; class StoreServerDatabaseRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVER_DATABASES; + protected ?string $resource = Database::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php b/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php index f49672c5b..e79ac3a7c 100644 --- a/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/GetExternalServerRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Servers; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Server; class GetExternalServerRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVERS; + protected ?string $resource = Server::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Servers/GetServerRequest.php b/app/Http/Requests/Api/Application/Servers/GetServerRequest.php index 4060ced80..0b97ff15d 100644 --- a/app/Http/Requests/Api/Application/Servers/GetServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/GetServerRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Servers; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Server; class GetServerRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVERS; + protected ?string $resource = Server::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php index 7f083364e..32a276862 100644 --- a/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php +++ b/app/Http/Requests/Api/Application/Servers/ServerWriteRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Servers; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\Server; class ServerWriteRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVERS; + protected ?string $resource = Server::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php index 86ac941fd..fba1573bb 100644 --- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php @@ -11,7 +11,7 @@ use App\Http\Requests\Api\Application\ApplicationApiRequest; class StoreServerRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVERS; + protected ?string $resource = Server::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php index 47b74f886..255908b48 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php @@ -8,7 +8,7 @@ use App\Http\Requests\Api\Application\ApplicationApiRequest; class UpdateServerStartupRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_SERVERS; + protected ?string $resource = Server::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php b/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php index 85ddcc955..ed63a2032 100644 --- a/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php +++ b/app/Http/Requests/Api/Application/Users/DeleteUserRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Users; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\User; class DeleteUserRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_USERS; + protected ?string $resource = User::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; } diff --git a/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php b/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php index eeba4819a..026a88214 100644 --- a/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php +++ b/app/Http/Requests/Api/Application/Users/GetExternalUserRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Users; use App\Services\Acl\Api\AdminAcl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\User; class GetExternalUserRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_USERS; + protected ?string $resource = User::RESOURCE_NAME; protected int $permission = AdminAcl::READ; } diff --git a/app/Http/Requests/Api/Application/Users/GetUsersRequest.php b/app/Http/Requests/Api/Application/Users/GetUsersRequest.php index be590e24b..fda19257c 100644 --- a/app/Http/Requests/Api/Application/Users/GetUsersRequest.php +++ b/app/Http/Requests/Api/Application/Users/GetUsersRequest.php @@ -4,10 +4,11 @@ namespace App\Http\Requests\Api\Application\Users; use App\Services\Acl\Api\AdminAcl as Acl; use App\Http\Requests\Api\Application\ApplicationApiRequest; +use App\Models\User; class GetUsersRequest extends ApplicationApiRequest { - protected ?string $resource = Acl::RESOURCE_USERS; + protected ?string $resource = User::RESOURCE_NAME; protected int $permission = Acl::READ; } diff --git a/app/Http/Requests/Api/Application/Users/StoreUserRequest.php b/app/Http/Requests/Api/Application/Users/StoreUserRequest.php index cf1b6c244..17f338243 100644 --- a/app/Http/Requests/Api/Application/Users/StoreUserRequest.php +++ b/app/Http/Requests/Api/Application/Users/StoreUserRequest.php @@ -8,7 +8,7 @@ use App\Http\Requests\Api\Application\ApplicationApiRequest; class StoreUserRequest extends ApplicationApiRequest { - protected ?string $resource = AdminAcl::RESOURCE_USERS; + protected ?string $resource = User::RESOURCE_NAME; protected int $permission = AdminAcl::WRITE; diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index abd60bfba..dfb56f32f 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -43,7 +43,7 @@ class Allocation extends Model { /** * The resource name for this model when it is transformed into an - * API representation using fractal. + * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'allocation'; diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 09914483e..9397fd96a 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -2,9 +2,9 @@ namespace App\Models; +use App\Services\Acl\Api\AdminAcl; use Illuminate\Support\Str; use Webmozart\Assert\Assert; -use App\Services\Acl\Api\AdminAcl; use Illuminate\Database\Eloquent\Relations\BelongsTo; /** @@ -15,20 +15,13 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @property int $key_type * @property string $identifier * @property string $token + * @property array $permissions * @property array $allowed_ips * @property string|null $memo * @property \Illuminate\Support\Carbon|null $last_used_at * @property \Illuminate\Support\Carbon|null $expires_at * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at - * @property int $r_servers - * @property int $r_nodes - * @property int $r_allocations - * @property int $r_users - * @property int $r_eggs - * @property int $r_database_hosts - * @property int $r_server_databases - * @property int $r_mounts * @property \App\Models\User $tokenable * @property \App\Models\User $user * @@ -84,8 +77,6 @@ class ApiKey extends Model */ public const KEY_LENGTH = 32; - public const RESOURCES = ['servers', 'nodes', 'allocations', 'users', 'eggs', 'database_hosts', 'server_databases', 'mounts']; - /** * The table associated with the model. */ @@ -99,18 +90,11 @@ class ApiKey extends Model 'key_type', 'identifier', 'token', + 'permissions', 'allowed_ips', 'memo', 'last_used_at', 'expires_at', - 'r_' . AdminAcl::RESOURCE_USERS, - 'r_' . AdminAcl::RESOURCE_ALLOCATIONS, - 'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS, - 'r_' . AdminAcl::RESOURCE_SERVER_DATABASES, - 'r_' . AdminAcl::RESOURCE_EGGS, - 'r_' . AdminAcl::RESOURCE_NODES, - 'r_' . AdminAcl::RESOURCE_SERVERS, - 'r_' . AdminAcl::RESOURCE_MOUNTS, ]; /** @@ -118,6 +102,7 @@ class ApiKey extends Model */ protected $attributes = [ 'allowed_ips' => '[]', + 'permissions' => '[]', ]; /** @@ -134,24 +119,19 @@ class ApiKey extends Model 'key_type' => 'present|integer|min:0|max:2', 'identifier' => 'required|string|size:16|unique:api_keys,identifier', 'token' => 'required|string', + 'permissions' => 'array', + 'permissions.*' => 'integer|min:0|max:3', 'memo' => 'required|nullable|string|max:500', 'allowed_ips' => 'array', 'allowed_ips.*' => 'string', 'last_used_at' => 'nullable|date', 'expires_at' => 'nullable|date', - 'r_' . AdminAcl::RESOURCE_USERS => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_SERVER_DATABASES => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_EGGS => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_NODES => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_SERVERS => 'integer|min:0|max:3', - 'r_' . AdminAcl::RESOURCE_MOUNTS => 'integer|min:0|max:3', ]; protected function casts(): array { return [ + 'permissions' => 'array', 'allowed_ips' => 'array', 'user_id' => 'int', 'last_used_at' => 'datetime', @@ -159,14 +139,6 @@ class ApiKey extends Model 'token' => 'encrypted', self::CREATED_AT => 'datetime', self::UPDATED_AT => 'datetime', - 'r_' . AdminAcl::RESOURCE_USERS => 'int', - 'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'int', - 'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'int', - 'r_' . AdminAcl::RESOURCE_SERVER_DATABASES => 'int', - 'r_' . AdminAcl::RESOURCE_EGGS => 'int', - 'r_' . AdminAcl::RESOURCE_NODES => 'int', - 'r_' . AdminAcl::RESOURCE_SERVERS => 'int', - 'r_' . AdminAcl::RESOURCE_MOUNTS => 'int', ]; } @@ -188,6 +160,41 @@ class ApiKey extends Model return $this->user(); } + /** + * Returns the permission for the given resource. + */ + public function getPermission(string $resource): int + { + return $this->permissions[$resource] ?? AdminAcl::NONE; + } + + public const DEFAULT_RESOURCE_NAMES = [ + Server::RESOURCE_NAME, + Node::RESOURCE_NAME, + Allocation::RESOURCE_NAME, + User::RESOURCE_NAME, + Egg::RESOURCE_NAME, + DatabaseHost::RESOURCE_NAME, + Database::RESOURCE_NAME, + Mount::RESOURCE_NAME, + Role::RESOURCE_NAME, + ]; + + private static array $customResourceNames = []; + + public static function registerCustomResourceName(string $resourceName): void + { + $customResourceNames[] = $resourceName; + } + + /** + * Returns a list of all possible permission keys. + */ + public static function getPermissionList(): array + { + return array_unique(array_merge(self::DEFAULT_RESOURCE_NAMES, self::$customResourceNames)); + } + /** * Finds the model matching the provided token. */ diff --git a/app/Models/Database.php b/app/Models/Database.php index bd6bc6de3..24e91ce96 100644 --- a/app/Models/Database.php +++ b/app/Models/Database.php @@ -23,7 +23,7 @@ class Database extends Model { /** * The resource name for this model when it is transformed into an - * API representation using fractal. + * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'server_database'; diff --git a/app/Models/DatabaseHost.php b/app/Models/DatabaseHost.php index 278cee9b4..66e37c23e 100644 --- a/app/Models/DatabaseHost.php +++ b/app/Models/DatabaseHost.php @@ -21,7 +21,7 @@ class DatabaseHost extends Model { /** * The resource name for this model when it is transformed into an - * API representation using fractal. + * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'database_host'; diff --git a/app/Models/Egg.php b/app/Models/Egg.php index 1ca0da55b..8f6013dc5 100644 --- a/app/Models/Egg.php +++ b/app/Models/Egg.php @@ -51,7 +51,7 @@ class Egg extends Model { /** * The resource name for this model when it is transformed into an - * API representation using fractal. + * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'egg'; diff --git a/app/Models/Node.php b/app/Models/Node.php index 64f44e3af..ea31ba2fd 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -49,7 +49,7 @@ class Node extends Model /** * The resource name for this model when it is transformed into an - * API representation using fractal. + * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'node'; diff --git a/app/Models/Server.php b/app/Models/Server.php index 67b719032..5136a9402 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -124,7 +124,7 @@ class Server extends Model /** * The resource name for this model when it is transformed into an - * API representation using fractal. + * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'server'; diff --git a/app/Models/User.php b/app/Models/User.php index 45545db34..9fc04d082 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -104,7 +104,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac /** * The resource name for this model when it is transformed into an - * API representation using fractal. + * API representation using fractal. Also used as name for api key permissions. */ public const RESOURCE_NAME = 'user'; diff --git a/app/Services/Acl/Api/AdminAcl.php b/app/Services/Acl/Api/AdminAcl.php index 90ec6480a..75a93aa86 100644 --- a/app/Services/Acl/Api/AdminAcl.php +++ b/app/Services/Acl/Api/AdminAcl.php @@ -6,12 +6,6 @@ use App\Models\ApiKey; class AdminAcl { - /** - * Resource permission columns in the api_keys table begin - * with this identifier. - */ - public const COLUMN_IDENTIFIER = 'r_'; - /** * The different types of permissions available for API keys. This * implements a read/write/none permissions scheme for all endpoints. @@ -22,28 +16,6 @@ class AdminAcl public const WRITE = 2; - /** - * Resources that are available on the API and can contain a permissions - * set for each key. These are stored in the database as r_{resource}. - */ - public const RESOURCE_SERVERS = 'servers'; - - public const RESOURCE_NODES = 'nodes'; - - public const RESOURCE_ALLOCATIONS = 'allocations'; - - public const RESOURCE_USERS = 'users'; - - public const RESOURCE_EGGS = 'eggs'; - - public const RESOURCE_DATABASE_HOSTS = 'database_hosts'; - - public const RESOURCE_SERVER_DATABASES = 'server_databases'; - - public const RESOURCE_MOUNTS = 'mounts'; - - public const RESOURCE_ROLES = 'roles'; - /** * Determine if an API key has permission to perform a specific read/write operation. */ @@ -62,20 +34,14 @@ class AdminAcl */ public static function check(ApiKey $key, string $resource, int $action = self::READ): bool { - return self::can(data_get($key, self::COLUMN_IDENTIFIER . $resource, self::NONE), $action); + return self::can($key->getPermission($resource), $action); } /** - * Return a list of all resource constants defined in this ACL. - * - * @throws \ReflectionException + * Returns a list of all possible permissions. */ public static function getResourceList(): array { - $reflect = new \ReflectionClass(__CLASS__); - - return collect($reflect->getConstants())->filter(function ($value, $key) { - return substr($key, 0, 9) === 'RESOURCE_'; - })->values()->toArray(); + return ApiKey::getPermissionList(); } } diff --git a/app/Services/Api/KeyCreationService.php b/app/Services/Api/KeyCreationService.php index 7fde94ba2..66151d28a 100644 --- a/app/Services/Api/KeyCreationService.php +++ b/app/Services/Api/KeyCreationService.php @@ -35,7 +35,7 @@ class KeyCreationService ]); if ($this->keyType === ApiKey::TYPE_APPLICATION) { - $data = array_merge($data, $permissions); + $data['permissions'] = array_merge($data['permissions'], $permissions); } return ApiKey::query()->forceCreate($data); diff --git a/app/Services/Nodes/NodeAutoDeployService.php b/app/Services/Nodes/NodeAutoDeployService.php index 5e9c55b22..ace7fd199 100644 --- a/app/Services/Nodes/NodeAutoDeployService.php +++ b/app/Services/Nodes/NodeAutoDeployService.php @@ -4,6 +4,7 @@ namespace App\Services\Nodes; use App\Models\ApiKey; use App\Models\Node; +use App\Services\Acl\Api\AdminAcl; use App\Services\Api\KeyCreationService; use Illuminate\Http\Request; @@ -28,7 +29,7 @@ class NodeAutoDeployService /** @var ApiKey|null $key */ $key = ApiKey::query() ->where('key_type', ApiKey::TYPE_APPLICATION) - ->where('r_nodes', true) + ->whereJsonContains('permissions->' . Node::RESOURCE_NAME, AdminAcl::READ) ->first(); // We couldn't find a key that exists for this user with only permission for @@ -37,7 +38,7 @@ class NodeAutoDeployService $key = $this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([ 'memo' => 'Automatically generated node deployment key.', 'user_id' => $request->user()->id, - ], ['r_nodes' => true]); + ], ['permissions' => [Node::RESOURCE_NAME => AdminAcl::READ]]); } $token = $key->identifier . $key->token; diff --git a/app/Transformers/Api/Application/AllocationTransformer.php b/app/Transformers/Api/Application/AllocationTransformer.php index 7f5bf8827..c44fc7b87 100644 --- a/app/Transformers/Api/Application/AllocationTransformer.php +++ b/app/Transformers/Api/Application/AllocationTransformer.php @@ -7,7 +7,6 @@ use App\Models\Server; use League\Fractal\Resource\Item; use App\Models\Allocation; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class AllocationTransformer extends BaseTransformer { @@ -46,7 +45,7 @@ class AllocationTransformer extends BaseTransformer */ public function includeNode(Allocation $allocation): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + if (!$this->authorize(Node::RESOURCE_NAME)) { return $this->null(); } @@ -64,7 +63,7 @@ class AllocationTransformer extends BaseTransformer */ public function includeServer(Allocation $allocation): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVERS) || !$allocation->server) { + if (!$this->authorize(Server::RESOURCE_NAME) || !$allocation->server) { return $this->null(); } diff --git a/app/Transformers/Api/Application/DatabaseHostTransformer.php b/app/Transformers/Api/Application/DatabaseHostTransformer.php index 1a47b905a..73d216ece 100644 --- a/app/Transformers/Api/Application/DatabaseHostTransformer.php +++ b/app/Transformers/Api/Application/DatabaseHostTransformer.php @@ -8,7 +8,6 @@ use App\Models\DatabaseHost; use League\Fractal\Resource\Item; use League\Fractal\Resource\Collection; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class DatabaseHostTransformer extends BaseTransformer { @@ -49,7 +48,7 @@ class DatabaseHostTransformer extends BaseTransformer */ public function includeDatabases(DatabaseHost $model): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { + if (!$this->authorize(Database::RESOURCE_NAME)) { return $this->null(); } @@ -65,7 +64,7 @@ class DatabaseHostTransformer extends BaseTransformer */ public function includeNode(DatabaseHost $model): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + if (!$this->authorize(Node::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/EggTransformer.php b/app/Transformers/Api/Application/EggTransformer.php index 982482b8c..6f3c15d25 100644 --- a/app/Transformers/Api/Application/EggTransformer.php +++ b/app/Transformers/Api/Application/EggTransformer.php @@ -8,7 +8,6 @@ use App\Models\Server; use App\Models\EggVariable; use League\Fractal\Resource\Collection; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class EggTransformer extends BaseTransformer { @@ -83,7 +82,7 @@ class EggTransformer extends BaseTransformer */ public function includeServers(Egg $model): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(Server::RESOURCE_NAME)) { return $this->null(); } @@ -99,7 +98,7 @@ class EggTransformer extends BaseTransformer */ public function includeVariables(Egg $model): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + if (!$this->authorize(Egg::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/MountTransformer.php b/app/Transformers/Api/Application/MountTransformer.php index 18dbe36dc..933985aed 100644 --- a/app/Transformers/Api/Application/MountTransformer.php +++ b/app/Transformers/Api/Application/MountTransformer.php @@ -2,10 +2,12 @@ namespace App\Transformers\Api\Application; +use App\Models\Egg; use App\Models\Mount; +use App\Models\Node; +use App\Models\Server; use League\Fractal\Resource\Collection; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class MountTransformer extends BaseTransformer { @@ -34,7 +36,7 @@ class MountTransformer extends BaseTransformer */ public function includeEggs(Mount $mount): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + if (!$this->authorize(Egg::RESOURCE_NAME)) { return $this->null(); } @@ -54,7 +56,7 @@ class MountTransformer extends BaseTransformer */ public function includeNodes(Mount $mount): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + if (!$this->authorize(Node::RESOURCE_NAME)) { return $this->null(); } @@ -74,7 +76,7 @@ class MountTransformer extends BaseTransformer */ public function includeServers(Mount $mount): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(Server::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/NodeTransformer.php b/app/Transformers/Api/Application/NodeTransformer.php index 670e4bcac..288c734d0 100644 --- a/app/Transformers/Api/Application/NodeTransformer.php +++ b/app/Transformers/Api/Application/NodeTransformer.php @@ -3,9 +3,10 @@ namespace App\Transformers\Api\Application; use App\Models\Node; +use App\Models\Server; +use App\Models\Allocation; use League\Fractal\Resource\Collection; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class NodeTransformer extends BaseTransformer { @@ -52,7 +53,7 @@ class NodeTransformer extends BaseTransformer */ public function includeAllocations(Node $node): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { + if (!$this->authorize(Allocation::RESOURCE_NAME)) { return $this->null(); } @@ -72,7 +73,7 @@ class NodeTransformer extends BaseTransformer */ public function includeServers(Node $node): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(Server::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/ServerDatabaseTransformer.php b/app/Transformers/Api/Application/ServerDatabaseTransformer.php index e74cd2586..64ae12011 100644 --- a/app/Transformers/Api/Application/ServerDatabaseTransformer.php +++ b/app/Transformers/Api/Application/ServerDatabaseTransformer.php @@ -6,7 +6,6 @@ use App\Models\Database; use League\Fractal\Resource\Item; use App\Models\DatabaseHost; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class ServerDatabaseTransformer extends BaseTransformer { @@ -57,7 +56,7 @@ class ServerDatabaseTransformer extends BaseTransformer */ public function includeHost(Database $model): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_DATABASE_HOSTS)) { + if (!$this->authorize(DatabaseHost::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/ServerTransformer.php b/app/Transformers/Api/Application/ServerTransformer.php index b81baf279..237881530 100644 --- a/app/Transformers/Api/Application/ServerTransformer.php +++ b/app/Transformers/Api/Application/ServerTransformer.php @@ -3,10 +3,14 @@ namespace App\Transformers\Api\Application; use App\Models\Server; +use App\Models\Node; +use App\Models\User; +use App\Models\Egg; +use App\Models\Database; +use App\Models\Allocation; use League\Fractal\Resource\Item; use League\Fractal\Resource\Collection; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; use App\Services\Servers\EnvironmentService; class ServerTransformer extends BaseTransformer @@ -97,7 +101,7 @@ class ServerTransformer extends BaseTransformer */ public function includeAllocations(Server $server): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_ALLOCATIONS)) { + if (!$this->authorize(Allocation::RESOURCE_NAME)) { return $this->null(); } @@ -113,7 +117,7 @@ class ServerTransformer extends BaseTransformer */ public function includeSubusers(Server $server): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { + if (!$this->authorize(User::RESOURCE_NAME)) { return $this->null(); } @@ -129,7 +133,7 @@ class ServerTransformer extends BaseTransformer */ public function includeUser(Server $server): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { + if (!$this->authorize(User::RESOURCE_NAME)) { return $this->null(); } @@ -145,7 +149,7 @@ class ServerTransformer extends BaseTransformer */ public function includeEgg(Server $server): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + if (!$this->authorize(Egg::RESOURCE_NAME)) { return $this->null(); } @@ -161,7 +165,7 @@ class ServerTransformer extends BaseTransformer */ public function includeVariables(Server $server): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(Server::RESOURCE_NAME)) { return $this->null(); } @@ -177,7 +181,7 @@ class ServerTransformer extends BaseTransformer */ public function includeNode(Server $server): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_NODES)) { + if (!$this->authorize(Node::RESOURCE_NAME)) { return $this->null(); } @@ -193,7 +197,7 @@ class ServerTransformer extends BaseTransformer */ public function includeDatabases(Server $server): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVER_DATABASES)) { + if (!$this->authorize(Database::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/ServerVariableTransformer.php b/app/Transformers/Api/Application/ServerVariableTransformer.php index 555fed228..479f5f554 100644 --- a/app/Transformers/Api/Application/ServerVariableTransformer.php +++ b/app/Transformers/Api/Application/ServerVariableTransformer.php @@ -4,8 +4,8 @@ namespace App\Transformers\Api\Application; use League\Fractal\Resource\Item; use App\Models\EggVariable; +use App\Models\Egg; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class ServerVariableTransformer extends BaseTransformer { @@ -37,7 +37,7 @@ class ServerVariableTransformer extends BaseTransformer */ public function includeParent(EggVariable $variable): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_EGGS)) { + if (!$this->authorize(Egg::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/SubuserTransformer.php b/app/Transformers/Api/Application/SubuserTransformer.php index 5cec29b33..c46f33fa9 100644 --- a/app/Transformers/Api/Application/SubuserTransformer.php +++ b/app/Transformers/Api/Application/SubuserTransformer.php @@ -3,9 +3,10 @@ namespace App\Transformers\Api\Application; use App\Models\Subuser; +use App\Models\User; +use App\Models\Server; use League\Fractal\Resource\Item; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class SubuserTransformer extends BaseTransformer { @@ -44,7 +45,7 @@ class SubuserTransformer extends BaseTransformer */ public function includeUser(Subuser $subuser): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_USERS)) { + if (!$this->authorize(User::RESOURCE_NAME)) { return $this->null(); } @@ -60,7 +61,7 @@ class SubuserTransformer extends BaseTransformer */ public function includeServer(Subuser $subuser): Item|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(Server::RESOURCE_NAME)) { return $this->null(); } diff --git a/app/Transformers/Api/Application/UserTransformer.php b/app/Transformers/Api/Application/UserTransformer.php index 5cf800c08..1215f2fe7 100644 --- a/app/Transformers/Api/Application/UserTransformer.php +++ b/app/Transformers/Api/Application/UserTransformer.php @@ -4,9 +4,9 @@ namespace App\Transformers\Api\Application; use App\Models\Role; use App\Models\User; +use App\Models\Server; use League\Fractal\Resource\Collection; use League\Fractal\Resource\NullResource; -use App\Services\Acl\Api\AdminAcl; class UserTransformer extends BaseTransformer { @@ -55,7 +55,7 @@ class UserTransformer extends BaseTransformer */ public function includeServers(User $user): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) { + if (!$this->authorize(Server::RESOURCE_NAME)) { return $this->null(); } @@ -71,7 +71,7 @@ class UserTransformer extends BaseTransformer */ public function includeRoles(User $user): Collection|NullResource { - if (!$this->authorize(AdminAcl::RESOURCE_ROLES)) { + if (!$this->authorize(Role::RESOURCE_NAME)) { return $this->null(); } diff --git a/database/Factories/ApiKeyFactory.php b/database/Factories/ApiKeyFactory.php index 1b258767b..f63e89af3 100644 --- a/database/Factories/ApiKeyFactory.php +++ b/database/Factories/ApiKeyFactory.php @@ -28,6 +28,7 @@ class ApiKeyFactory extends Factory 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION), 'token' => $token ?: $token = Str::random(ApiKey::KEY_LENGTH), 'allowed_ips' => [], + 'permissions' => [], 'memo' => 'Test Function Key', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), diff --git a/database/migrations/2024_07_12_095213_fix_missing_sqlite_foreign_keys.php b/database/migrations/2024_07_12_095213_fix_missing_sqlite_foreign_keys.php index 47c78a09a..35a41381e 100644 --- a/database/migrations/2024_07_12_095213_fix_missing_sqlite_foreign_keys.php +++ b/database/migrations/2024_07_12_095213_fix_missing_sqlite_foreign_keys.php @@ -24,26 +24,26 @@ return new class extends Migration DB::transaction(function () { // api_keys_user_id_foreign DB::statement('ALTER TABLE api_keys RENAME TO _api_keys_old'); - DB::statement('CREATE TABLE api_keys - ("id" integer primary key autoincrement not null, - "token" text not null, - "allowed_ips" text not null, - "created_at" datetime, - "updated_at" datetime, - "user_id" integer not null, - "memo" text, - "r_servers" integer not null default \'0\', - "r_nodes" integer not null default \'0\', - "r_allocations" integer not null default \'0\', - "r_users" integer not null default \'0\', - "r_eggs" integer not null default \'0\', - "r_database_hosts" integer not null default \'0\', - "r_server_databases" integer not null default \'0\', - "identifier" varchar, - "key_type" integer not null default \'0\', - "last_used_at" datetime, + DB::statement('CREATE TABLE api_keys + ("id" integer primary key autoincrement not null, + "token" text not null, + "allowed_ips" text not null, + "created_at" datetime, + "updated_at" datetime, + "user_id" integer not null, + "memo" text, + "r_servers" integer not null default \'0\', + "r_nodes" integer not null default \'0\', + "r_allocations" integer not null default \'0\', + "r_users" integer not null default \'0\', + "r_eggs" integer not null default \'0\', + "r_database_hosts" integer not null default \'0\', + "r_server_databases" integer not null default \'0\', + "identifier" varchar, + "key_type" integer not null default \'0\', + "last_used_at" datetime, "expires_at" datetime, - "r_mounts" integer not null default \'0\', + "r_mounts" integer not null default \'0\', foreign key("user_id") references "users"("id") on delete cascade)'); DB::statement('INSERT INTO api_keys SELECT * FROM _api_keys_old'); DB::statement('DROP TABLE _api_keys_old'); @@ -51,17 +51,17 @@ return new class extends Migration // database_hosts_node_id_foreign DB::statement('ALTER TABLE database_hosts RENAME TO _database_hosts_old'); - DB::statement('CREATE TABLE database_hosts - ("id" integer primary key autoincrement not null, - "name" varchar not null, - "host" varchar not null, - "port" integer not null, - "username" varchar not null, - "password" text not null, - "max_databases" integer, - "node_id" integer, - "created_at" datetime, - "updated_at" datetime, + DB::statement('CREATE TABLE database_hosts + ("id" integer primary key autoincrement not null, + "name" varchar not null, + "host" varchar not null, + "port" integer not null, + "username" varchar not null, + "password" text not null, + "max_databases" integer, + "node_id" integer, + "created_at" datetime, + "updated_at" datetime, foreign key("node_id") references "nodes"("id") on delete set null)'); DB::statement('INSERT INTO database_hosts SELECT * FROM _database_hosts_old'); DB::statement('DROP TABLE _database_hosts_old'); @@ -69,10 +69,10 @@ return new class extends Migration // mount_node_node_id_foreign // mount_node_mount_id_foreign DB::statement('ALTER TABLE mount_node RENAME TO _mount_node_old'); - DB::statement('CREATE TABLE mount_node - ("node_id" integer not null, - "mount_id" integer not null, - foreign key("node_id") references "nodes"("id") on delete cascade on update cascade, + DB::statement('CREATE TABLE mount_node + ("node_id" integer not null, + "mount_id" integer not null, + foreign key("node_id") references "nodes"("id") on delete cascade on update cascade, foreign key("mount_id") references "mounts"("id") on delete cascade on update cascade)'); DB::statement('INSERT INTO mount_node SELECT * FROM _mount_node_old'); DB::statement('DROP TABLE _mount_node_old'); @@ -83,38 +83,38 @@ return new class extends Migration // servers_egg_id_foreign // servers_allocation_id_foreign DB::statement('ALTER TABLE servers RENAME TO _servers_old'); - DB::statement('CREATE TABLE servers - ("id" integer primary key autoincrement not null, - "uuid" varchar not null, - "uuid_short" varchar not null, - "node_id" integer not null, - "name" varchar not null, - "owner_id" integer not null, - "memory" integer not null, - "swap" integer not null, - "disk" integer not null, - "io" integer not null, - "cpu" integer not null, - "egg_id" integer not null, - "startup" text not null, - "created_at" datetime, - "updated_at" datetime, - "allocation_id" integer not null, - "image" varchar not null, - "description" text not null, - "skip_scripts" tinyint(1) not null default \'0\', - "external_id" varchar, - "database_limit" integer default \'0\', - "allocation_limit" integer, - "threads" varchar, - "backup_limit" integer not null default \'0\', - "status" varchar, - "installed_at" datetime, - "oom_killer" integer not null default \'0\', - "docker_labels" text, - foreign key("node_id") references "nodes"("id"), - foreign key("owner_id") references "users"("id"), - foreign key("egg_id") references "eggs"("id"), + DB::statement('CREATE TABLE servers + ("id" integer primary key autoincrement not null, + "uuid" varchar not null, + "uuid_short" varchar not null, + "node_id" integer not null, + "name" varchar not null, + "owner_id" integer not null, + "memory" integer not null, + "swap" integer not null, + "disk" integer not null, + "io" integer not null, + "cpu" integer not null, + "egg_id" integer not null, + "startup" text not null, + "created_at" datetime, + "updated_at" datetime, + "allocation_id" integer not null, + "image" varchar not null, + "description" text not null, + "skip_scripts" tinyint(1) not null default \'0\', + "external_id" varchar, + "database_limit" integer default \'0\', + "allocation_limit" integer, + "threads" varchar, + "backup_limit" integer not null default \'0\', + "status" varchar, + "installed_at" datetime, + "oom_killer" integer not null default \'0\', + "docker_labels" text, + foreign key("node_id") references "nodes"("id"), + foreign key("owner_id") references "users"("id"), + foreign key("egg_id") references "eggs"("id"), foreign key("allocation_id") references "allocations"("id"))'); DB::statement('INSERT INTO servers SELECT * FROM _servers_old'); DB::statement('DROP TABLE _servers_old'); @@ -126,18 +126,18 @@ return new class extends Migration // databases_server_id_foreign // databases_database_host_id_foreign DB::statement('ALTER TABLE databases RENAME TO _databases_old'); - DB::statement('CREATE TABLE databases - ("id" integer primary key autoincrement not null, - "server_id" integer not null, - "database_host_id" integer not null, - "database" varchar not null, - "username" varchar not null, - "remote" varchar not null default \'%\', - "password" text not null, - "created_at" datetime, - "updated_at" datetime, - "max_connections" integer default \'0\', - foreign key("server_id") references "servers"("id"), + DB::statement('CREATE TABLE databases + ("id" integer primary key autoincrement not null, + "server_id" integer not null, + "database_host_id" integer not null, + "database" varchar not null, + "username" varchar not null, + "remote" varchar not null default \'%\', + "password" text not null, + "created_at" datetime, + "updated_at" datetime, + "max_connections" integer default \'0\', + foreign key("server_id") references "servers"("id"), foreign key("database_host_id") references "database_hosts"("id"))'); DB::statement('INSERT INTO databases SELECT * FROM _databases_old'); DB::statement('DROP TABLE _databases_old'); @@ -147,16 +147,16 @@ return new class extends Migration // allocations_node_id_foreign // allocations_server_id_foreign DB::statement('ALTER TABLE allocations RENAME TO _allocations_old'); - DB::statement('CREATE TABLE allocations - ("id" integer primary key autoincrement not null, - "node_id" integer not null, - "ip" varchar not null, - "port" integer not null, - "server_id" integer, - "created_at" datetime, - "updated_at" datetime, - "ip_alias" text, - "notes" varchar, + DB::statement('CREATE TABLE allocations + ("id" integer primary key autoincrement not null, + "node_id" integer not null, + "ip" varchar not null, + "port" integer not null, + "server_id" integer, + "created_at" datetime, + "updated_at" datetime, + "ip_alias" text, + "notes" varchar, foreign key("node_id") references "nodes"("id") on delete cascade, foreign key("server_id") references "servers"("id") on delete cascade on update set null)'); DB::statement('INSERT INTO allocations SELECT * FROM _allocations_old'); @@ -166,32 +166,32 @@ return new class extends Migration // eggs_config_from_foreign // eggs_copy_script_from_foreign DB::statement('ALTER TABLE eggs RENAME TO _eggs_old'); - DB::statement('CREATE TABLE eggs - ("id" integer primary key autoincrement not null, - "name" varchar not null, - "description" text, - "created_at" datetime, - "updated_at" datetime, - "startup" text, - "config_from" integer, - "config_stop" varchar, - "config_logs" text, - "config_startup" text, - "config_files" text, - "script_install" text, - "script_is_privileged" tinyint(1) not null default \'1\', - "script_entry" varchar not null default \'ash\', - "script_container" varchar not null default \'alpine:3.4\', - "copy_script_from" integer, - "uuid" varchar not null, - "author" varchar not null, - "features" text, - "docker_images" text, - "update_url" text, - "file_denylist" text, - "force_outgoing_ip" tinyint(1) not null default \'0\', - "tags" text not null, - foreign key("config_from") references "eggs"("id") on delete set null, + DB::statement('CREATE TABLE eggs + ("id" integer primary key autoincrement not null, + "name" varchar not null, + "description" text, + "created_at" datetime, + "updated_at" datetime, + "startup" text, + "config_from" integer, + "config_stop" varchar, + "config_logs" text, + "config_startup" text, + "config_files" text, + "script_install" text, + "script_is_privileged" tinyint(1) not null default \'1\', + "script_entry" varchar not null default \'ash\', + "script_container" varchar not null default \'alpine:3.4\', + "copy_script_from" integer, + "uuid" varchar not null, + "author" varchar not null, + "features" text, + "docker_images" text, + "update_url" text, + "file_denylist" text, + "force_outgoing_ip" tinyint(1) not null default \'0\', + "tags" text not null, + foreign key("config_from") references "eggs"("id") on delete set null, foreign key("copy_script_from") references "eggs"("id") on delete set null)'); DB::statement('INSERT INTO eggs SELECT * FROM _eggs_old'); DB::statement('DROP TABLE _eggs_old'); @@ -200,10 +200,10 @@ return new class extends Migration // egg_mount_mount_id_foreign // egg_mount_egg_id_foreign DB::statement('ALTER TABLE egg_mount RENAME TO _egg_mount_old'); - DB::statement('CREATE TABLE egg_mount - ("egg_id" integer not null, - "mount_id" integer not null, - foreign key("egg_id") references "eggs"("id") on delete cascade on update cascade, + DB::statement('CREATE TABLE egg_mount + ("egg_id" integer not null, + "mount_id" integer not null, + foreign key("egg_id") references "eggs"("id") on delete cascade on update cascade, foreign key("mount_id") references "mounts"("id") on delete cascade on update cascade)'); DB::statement('INSERT INTO egg_mount SELECT * FROM _egg_mount_old'); DB::statement('DROP TABLE _egg_mount_old'); @@ -211,19 +211,19 @@ return new class extends Migration // service_variables_egg_id_foreign DB::statement('ALTER TABLE egg_variables RENAME TO _egg_variables_old'); - DB::statement('CREATE TABLE egg_variables - ("id" integer primary key autoincrement not null, - "egg_id" integer not null, - "name" varchar not null, - "description" text not null, - "env_variable" varchar not null, - "default_value" text not null, - "user_viewable" integer not null, - "user_editable" integer not null, - "rules" text not null, - "created_at" datetime, - "updated_at" datetime, - "sort" integer, + DB::statement('CREATE TABLE egg_variables + ("id" integer primary key autoincrement not null, + "egg_id" integer not null, + "name" varchar not null, + "description" text not null, + "env_variable" varchar not null, + "default_value" text not null, + "user_viewable" integer not null, + "user_editable" integer not null, + "rules" text not null, + "created_at" datetime, + "updated_at" datetime, + "sort" integer, foreign key("egg_id") references "eggs"("id") on delete cascade)'); DB::statement('INSERT INTO egg_variables SELECT * FROM _egg_variables_old'); DB::statement('DROP TABLE _egg_variables_old'); @@ -231,10 +231,10 @@ return new class extends Migration // mount_server_server_id_foreign // mount_server_mount_id_foreign DB::statement('ALTER TABLE mount_server RENAME TO _mount_server_old'); - DB::statement('CREATE TABLE mount_server - ("server_id" integer not null, - "mount_id" integer not null, - foreign key("server_id") references "servers"("id") on delete cascade on update cascade, + DB::statement('CREATE TABLE mount_server + ("server_id" integer not null, + "mount_id" integer not null, + foreign key("server_id") references "servers"("id") on delete cascade on update cascade, foreign key("mount_id") references "mounts"("id") on delete cascade on update cascade)'); DB::statement('INSERT INTO mount_server SELECT * FROM _mount_server_old'); DB::statement('DROP TABLE _mount_server_old'); @@ -242,14 +242,14 @@ return new class extends Migration // server_variables_variable_id_foreign DB::statement('ALTER TABLE server_variables RENAME TO _server_variables_old'); - DB::statement('CREATE TABLE server_variables - ("id" integer primary key autoincrement not null, - "server_id" integer not null, - "variable_id" integer not null, - "variable_value" text not null, - "created_at" datetime, - "updated_at" datetime, - foreign key("server_id") references "servers"("id") on delete cascade, + DB::statement('CREATE TABLE server_variables + ("id" integer primary key autoincrement not null, + "server_id" integer not null, + "variable_id" integer not null, + "variable_value" text not null, + "created_at" datetime, + "updated_at" datetime, + foreign key("server_id") references "servers"("id") on delete cascade, foreign key("variable_id") references "egg_variables"("id") on delete cascade)'); DB::statement('INSERT INTO server_variables SELECT * FROM _server_variables_old'); DB::statement('DROP TABLE _server_variables_old'); @@ -257,14 +257,14 @@ return new class extends Migration // subusers_user_id_foreign // subusers_server_id_foreign DB::statement('ALTER TABLE subusers RENAME TO _subusers_old'); - DB::statement('CREATE TABLE subusers - ("id" integer primary key autoincrement not null, - "user_id" integer not null, - "server_id" integer not null, - "created_at" datetime, - "updated_at" datetime, - "permissions" text, - foreign key("user_id") references "users"("id") on delete cascade, + DB::statement('CREATE TABLE subusers + ("id" integer primary key autoincrement not null, + "user_id" integer not null, + "server_id" integer not null, + "created_at" datetime, + "updated_at" datetime, + "permissions" text, + foreign key("user_id") references "users"("id") on delete cascade, foreign key("server_id") references "servers"("id") on delete cascade)'); DB::statement('INSERT INTO subusers SELECT * FROM _subusers_old'); DB::statement('DROP TABLE _subusers_old'); diff --git a/database/migrations/2024_11_04_185326_revamp_api_keys_permissions.php b/database/migrations/2024_11_04_185326_revamp_api_keys_permissions.php new file mode 100644 index 000000000..ee8b64974 --- /dev/null +++ b/database/migrations/2024_11_04_185326_revamp_api_keys_permissions.php @@ -0,0 +1,94 @@ +json('permissions'); + }); + + foreach (ApiKey::query() as $apiKey) { + $permissions = [ + Server::RESOURCE_NAME => intval($apiKey->r_servers ?? 0), + Node::RESOURCE_NAME => intval($apiKey->r_nodes ?? 0), + Allocation::RESOURCE_NAME => intval($apiKey->r_allocations ?? 0), + User::RESOURCE_NAME => intval($apiKey->r_users ?? 0), + Egg::RESOURCE_NAME => intval($apiKey->r_eggs ?? 0), + DatabaseHost::RESOURCE_NAME => intval($apiKey->r_database_hosts ?? 0), + Database::RESOURCE_NAME => intval($apiKey->r_server_databases ?? 0), + Mount::RESOURCE_NAME => intval($apiKey->r_mounts ?? 0), + ]; + + DB::table('api_keys') + ->where('id', $apiKey->id) + ->update(['permissions' => $permissions]); + } + + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn([ + 'r_servers', + 'r_nodes', + 'r_allocations', + 'r_users', + 'r_eggs', + 'r_database_hosts', + 'r_server_databases', + 'r_mounts', + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('api_keys', function (Blueprint $table) { + $table->unsignedTinyInteger('r_servers')->default(0); + $table->unsignedTinyInteger('r_nodes')->default(0); + $table->unsignedTinyInteger('r_allocations')->default(0); + $table->unsignedTinyInteger('r_users')->default(0); + $table->unsignedTinyInteger('r_eggs')->default(0); + $table->unsignedTinyInteger('r_database_hosts')->default(0); + $table->unsignedTinyInteger('r_server_databases')->default(0); + $table->unsignedTinyInteger('r_mounts')->default(0); + }); + + foreach (ApiKey::query() as $apiKey) { + DB::table('api_keys') + ->where('id', $apiKey->id) + ->update([ + 'r_servers' => $apiKey->permissions[Server::RESOURCE_NAME], + 'r_nodes' => $apiKey->permissions[Node::RESOURCE_NAME], + 'r_allocations' => $apiKey->permissions[Allocation::RESOURCE_NAME], + 'r_users' => $apiKey->permissions[User::RESOURCE_NAME], + 'r_eggs' => $apiKey->permissions[Egg::RESOURCE_NAME], + 'r_database_hosts' => $apiKey->permissions[DatabaseHost::RESOURCE_NAME], + 'r_server_databases' => $apiKey->permissions[Database::RESOURCE_NAME], + 'r_mounts' => $apiKey->permissions[Mount::RESOURCE_NAME], + ]); + } + + Schema::table('api_keys', function (Blueprint $table) { + $table->dropColumn('permissions'); + }); + } +}; diff --git a/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php b/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php index c104a3eae..8ec689972 100644 --- a/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php +++ b/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php @@ -2,10 +2,17 @@ namespace App\Tests\Integration\Api\Application; +use App\Models\Allocation; use Illuminate\Http\Request; use App\Models\User; use PHPUnit\Framework\Assert; use App\Models\ApiKey; +use App\Models\Database; +use App\Models\DatabaseHost; +use App\Models\Egg; +use App\Models\Mount; +use App\Models\Node; +use App\Models\Server; use App\Models\Role; use App\Services\Acl\Api\AdminAcl; use App\Tests\Integration\IntegrationTestCase; @@ -79,18 +86,21 @@ abstract class ApplicationApiIntegrationTestCase extends IntegrationTestCase */ protected function createApiKey(User $user, array $permissions = []): ApiKey { - return ApiKey::factory()->create(array_merge([ + return ApiKey::factory()->create([ 'user_id' => $user->id, 'key_type' => ApiKey::TYPE_APPLICATION, - 'r_servers' => AdminAcl::READ | AdminAcl::WRITE, - 'r_nodes' => AdminAcl::READ | AdminAcl::WRITE, - 'r_allocations' => AdminAcl::READ | AdminAcl::WRITE, - 'r_users' => AdminAcl::READ | AdminAcl::WRITE, - 'r_eggs' => AdminAcl::READ | AdminAcl::WRITE, - 'r_database_hosts' => AdminAcl::READ | AdminAcl::WRITE, - 'r_server_databases' => AdminAcl::READ | AdminAcl::WRITE, - 'r_mounts' => AdminAcl::READ | AdminAcl::WRITE, - ], $permissions)); + 'permissions' => array_merge([ + Server::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + Node::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + Allocation::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + User::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + Egg::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + DatabaseHost::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + Database::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + Mount::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + Role::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + ], $permissions), + ]); } /** diff --git a/tests/Integration/Api/Application/EggControllerTest.php b/tests/Integration/Api/Application/EggControllerTest.php index 14a41cd6a..28d059fb2 100644 --- a/tests/Integration/Api/Application/EggControllerTest.php +++ b/tests/Integration/Api/Application/EggControllerTest.php @@ -3,6 +3,7 @@ namespace App\Tests\Integration\Api\Application; use App\Models\Egg; +use App\Services\Acl\Api\AdminAcl; use App\Transformers\Api\Application\EggTransformer; use Illuminate\Http\Response; use Illuminate\Support\Arr; @@ -111,7 +112,7 @@ class EggControllerTest extends ApplicationApiIntegrationTestCase public function testErrorReturnedIfNoPermission(): void { $egg = Egg::query()->findOrFail(1); - $this->createNewDefaultApiKey($this->getApiUser(), ['r_eggs' => 0]); + $this->createNewDefaultApiKey($this->getApiUser(), [Egg::RESOURCE_NAME => AdminAcl::NONE]); $response = $this->getJson('/api/application/eggs'); $this->assertAccessDeniedJson($response); diff --git a/tests/Integration/Api/Application/Users/ExternalUserControllerTest.php b/tests/Integration/Api/Application/Users/ExternalUserControllerTest.php index 24f3996eb..f4d7bddb9 100644 --- a/tests/Integration/Api/Application/Users/ExternalUserControllerTest.php +++ b/tests/Integration/Api/Application/Users/ExternalUserControllerTest.php @@ -4,6 +4,7 @@ namespace App\Tests\Integration\Api\Application\Users; use Illuminate\Support\Str; use App\Models\User; +use App\Services\Acl\Api\AdminAcl; use Illuminate\Http\Response; use App\Tests\Integration\Api\Application\ApplicationApiIntegrationTestCase; @@ -62,7 +63,7 @@ class ExternalUserControllerTest extends ApplicationApiIntegrationTestCase public function testErrorReturnedIfNoPermission(): void { $user = User::factory()->create(['external_id' => Str::random()]); - $this->createNewDefaultApiKey($this->getApiUser(), ['r_users' => 0]); + $this->createNewDefaultApiKey($this->getApiUser(), [User::RESOURCE_NAME => AdminAcl::NONE]); $response = $this->getJson('/api/application/users/external/' . $user->external_id); $this->assertAccessDeniedJson($response); diff --git a/tests/Integration/Api/Application/Users/UserControllerTest.php b/tests/Integration/Api/Application/Users/UserControllerTest.php index 6787ff1f4..41cfc26b7 100644 --- a/tests/Integration/Api/Application/Users/UserControllerTest.php +++ b/tests/Integration/Api/Application/Users/UserControllerTest.php @@ -2,6 +2,7 @@ namespace App\Tests\Integration\Api\Application\Users; +use App\Models\Server; use App\Models\User; use Illuminate\Http\Response; use App\Services\Acl\Api\AdminAcl; @@ -152,7 +153,7 @@ class UserControllerTest extends ApplicationApiIntegrationTestCase */ public function testKeyWithoutPermissionCannotLoadRelationship(): void { - $this->createNewDefaultApiKey($this->getApiUser(), ['r_servers' => 0]); + $this->createNewDefaultApiKey($this->getApiUser(), [Server::RESOURCE_NAME => AdminAcl::NONE]); $user = User::factory()->create(); $this->createServerModel(['user_id' => $user->id]); @@ -197,7 +198,7 @@ class UserControllerTest extends ApplicationApiIntegrationTestCase public function testErrorReturnedIfNoPermission(): void { $user = User::factory()->create(); - $this->createNewDefaultApiKey($this->getApiUser(), ['r_users' => 0]); + $this->createNewDefaultApiKey($this->getApiUser(), [User::RESOURCE_NAME => AdminAcl::NONE]); $response = $this->getJson('/api/application/users/' . $user->id); $this->assertAccessDeniedJson($response); @@ -286,7 +287,7 @@ class UserControllerTest extends ApplicationApiIntegrationTestCase */ public function testApiKeyWithoutWritePermissions(string $method, string $url): void { - $this->createNewDefaultApiKey($this->getApiUser(), ['r_users' => AdminAcl::READ]); + $this->createNewDefaultApiKey($this->getApiUser(), [User::RESOURCE_NAME => AdminAcl::READ]); if (str_contains($url, '{id}')) { $user = User::factory()->create(); diff --git a/tests/Unit/Services/Acl/Api/AdminAclTest.php b/tests/Unit/Services/Acl/Api/AdminAclTest.php index 8254457a1..2f7da99b4 100644 --- a/tests/Unit/Services/Acl/Api/AdminAclTest.php +++ b/tests/Unit/Services/Acl/Api/AdminAclTest.php @@ -5,6 +5,7 @@ namespace App\Tests\Unit\Services\Acl\Api; use App\Models\ApiKey; use App\Tests\TestCase; use App\Services\Acl\Api\AdminAcl; +use App\Models\Server; class AdminAclTest extends TestCase { @@ -23,9 +24,11 @@ class AdminAclTest extends TestCase */ public function testCheck(): void { - $model = ApiKey::factory()->make(['r_servers' => AdminAcl::READ | AdminAcl::WRITE]); + $model = ApiKey::factory()->make(['permissions' => [ + Server::RESOURCE_NAME => AdminAcl::READ | AdminAcl::WRITE, + ]]); - $this->assertTrue(AdminAcl::check($model, AdminAcl::RESOURCE_SERVERS, AdminAcl::WRITE)); + $this->assertTrue(AdminAcl::check($model, Server::RESOURCE_NAME, AdminAcl::WRITE)); } /**