From 9c7b49e2b92a5c0ad6e186612c0373e5fb7cbb8f Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Mon, 28 Dec 2020 12:47:08 -0700 Subject: [PATCH] Add proper permissions for role application routes, allow admins to access application api --- .../Api/Application/RoleController.php | 76 ------------ .../Api/Application/Roles/RoleController.php | 110 ++++++++++++++++++ app/Http/Requests/Admin/RoleFormRequest.php | 29 ----- .../Api/Application/ApplicationApiRequest.php | 4 + .../Application/Roles/DeleteRoleRequest.php | 32 +++++ .../Api/Application/Roles/GetRoleRequest.php | 20 ++++ .../Api/Application/Roles/GetRolesRequest.php | 19 +++ .../Application/Roles/StoreRoleRequest.php | 32 +++++ .../Application/Roles/UpdateRoleRequest.php | 20 ++++ app/Models/ApiKey.php | 2 + app/Services/Acl/Api/AdminAcl.php | 2 +- .../Api/Application/AdminRoleTransformer.php | 33 ++++++ .../scripts/api/admin/roles/createRole.ts | 2 +- resources/scripts/api/admin/roles/getRoles.ts | 4 +- resources/scripts/api/transformers.ts | 7 ++ routes/api-application.php | 7 +- 16 files changed, 287 insertions(+), 112 deletions(-) delete mode 100644 app/Http/Controllers/Api/Application/RoleController.php create mode 100644 app/Http/Controllers/Api/Application/Roles/RoleController.php delete mode 100644 app/Http/Requests/Admin/RoleFormRequest.php create mode 100644 app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php create mode 100644 app/Http/Requests/Api/Application/Roles/GetRoleRequest.php create mode 100644 app/Http/Requests/Api/Application/Roles/GetRolesRequest.php create mode 100644 app/Http/Requests/Api/Application/Roles/StoreRoleRequest.php create mode 100644 app/Http/Requests/Api/Application/Roles/UpdateRoleRequest.php create mode 100644 app/Transformers/Api/Application/AdminRoleTransformer.php diff --git a/app/Http/Controllers/Api/Application/RoleController.php b/app/Http/Controllers/Api/Application/RoleController.php deleted file mode 100644 index f74f35300..000000000 --- a/app/Http/Controllers/Api/Application/RoleController.php +++ /dev/null @@ -1,76 +0,0 @@ -repository = $repository; - } - - /** - * Returns an array of all roles. - * - * @return \Illuminate\Http\JsonResponse - */ - public function index() - { - return new JsonResponse($this->repository->all()); - } - - /** - * Creates a new role. - * - * @param \Pterodactyl\Http\Requests\Admin\RoleFormRequest $request - * - * @return \Illuminate\Http\JsonResponse - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - */ - public function create(RoleFormRequest $request) - { - $role = $this->repository->create($request->normalize()); - - return new JsonResponse($role); - } - - /** - * Updates a role. - * - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response - */ - public function update() - { - return response('', 204); - } - - /** - * Deletes a role. - * - * @param int $role_id - * - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response - */ - public function delete(int $role_id) - { - $this->repository->delete($role_id); - - return response('', 204); - } -} diff --git a/app/Http/Controllers/Api/Application/Roles/RoleController.php b/app/Http/Controllers/Api/Application/Roles/RoleController.php new file mode 100644 index 000000000..dced9d7a6 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Roles/RoleController.php @@ -0,0 +1,110 @@ +repository = $repository; + } + + /** + * Returns an array of all roles. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Roles\GetRolesRequest $request + * + * @return array + */ + public function index(GetRolesRequest $request) + { + return $this->fractal->collection(AdminRole::all()) + ->transformWith($this->getTransformer(AdminRoleTransformer::class)) + ->toArray(); + } + + /** + * Returns a single role. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Roles\GetRolesRequest $request + * @param \Pterodactyl\Models\AdminRole $role + * + * @return array + */ + public function view(GetRolesRequest $request, AdminRole $role): array + { + return $this->fractal->item($role) + ->transformWith($this->getTransformer(AdminRoleTransformer::class)) + ->toArray(); + } + + /** + * Creates a new role. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Roles\StoreRoleRequest $request + * + * @return \Illuminate\Http\JsonResponse + */ + public function store(StoreRoleRequest $request) + { + $role = AdminRole::query()->create($request->validated()); + + return $this->fractal->item($role) + ->transformWith($this->getTransformer(AdminRoleTransformer::class)) + ->respond(JsonResponse::HTTP_CREATED); + } + + /** + * Updates a role. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Roles\UpdateRoleRequest $request + * @param \Pterodactyl\Models\AdminRole $role + * + * @return array + */ + public function update(UpdateRoleRequest $request, AdminRole $role) + { + $role->update($request->validated()); + + return $this->fractal->item($role) + ->transformWith($this->getTransformer(AdminRoleTransformer::class)) + ->toArray(); + } + + /** + * Deletes a role. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Roles\DeleteRoleRequest $request + * @param \Pterodactyl\Models\AdminRole $role + * + * @return \Illuminate\Http\JsonResponse + */ + public function delete(DeleteRoleRequest $request, AdminRole $role) + { + $this->repository->delete($role->id); + + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); + } +} diff --git a/app/Http/Requests/Admin/RoleFormRequest.php b/app/Http/Requests/Admin/RoleFormRequest.php deleted file mode 100644 index c672b017c..000000000 --- a/app/Http/Requests/Admin/RoleFormRequest.php +++ /dev/null @@ -1,29 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Admin; - -use Pterodactyl\Models\AdminRole; - -class RoleFormRequest extends AdminFormRequest -{ - /** - * Setup the validation rules to use for these requests. - * - * @return array - */ - public function rules() - { - if ($this->method() === 'PATCH') { - return AdminRole::getRulesForUpdate($this->route()->parameter('mount')->id); - } - - return AdminRole::getRules(); - } -} diff --git a/app/Http/Requests/Api/Application/ApplicationApiRequest.php b/app/Http/Requests/Api/Application/ApplicationApiRequest.php index 133532718..d2d88052b 100644 --- a/app/Http/Requests/Api/Application/ApplicationApiRequest.php +++ b/app/Http/Requests/Api/Application/ApplicationApiRequest.php @@ -50,6 +50,10 @@ abstract class ApplicationApiRequest extends FormRequest throw new PterodactylException('An ACL resource must be defined on API requests.'); } + if (! is_null($this->user())) { + return $this->user()->root_admin; + } + return AdminAcl::check($this->key(), $this->resource, $this->permission); } diff --git a/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php b/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php new file mode 100644 index 000000000..94c73d0a8 --- /dev/null +++ b/app/Http/Requests/Api/Application/Roles/DeleteRoleRequest.php @@ -0,0 +1,32 @@ +route()->parameter('role'); + + return $role instanceof AdminRole && $role->exists; + } +} diff --git a/app/Http/Requests/Api/Application/Roles/GetRoleRequest.php b/app/Http/Requests/Api/Application/Roles/GetRoleRequest.php new file mode 100644 index 000000000..cd583c85c --- /dev/null +++ b/app/Http/Requests/Api/Application/Roles/GetRoleRequest.php @@ -0,0 +1,20 @@ +route()->parameter('role'); + + return $role instanceof AdminRole && $role->exists; + } +} diff --git a/app/Http/Requests/Api/Application/Roles/GetRolesRequest.php b/app/Http/Requests/Api/Application/Roles/GetRolesRequest.php new file mode 100644 index 000000000..567765da9 --- /dev/null +++ b/app/Http/Requests/Api/Application/Roles/GetRolesRequest.php @@ -0,0 +1,19 @@ +route()->parameter('role')->id); + } +} diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 072a74f9b..ea1c54e56 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -64,6 +64,7 @@ class ApiKey extends Model 'r_' . AdminAcl::RESOURCE_NESTS => 'int', 'r_' . AdminAcl::RESOURCE_NODES => 'int', 'r_' . AdminAcl::RESOURCE_SERVERS => 'int', + 'r_' . AdminAcl::RESOURCE_ROLES => 'int', ]; /** @@ -110,6 +111,7 @@ class ApiKey extends Model 'r_' . AdminAcl::RESOURCE_NESTS => '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_ROLES => 'integer|min:0|max:3', ]; /** diff --git a/app/Services/Acl/Api/AdminAcl.php b/app/Services/Acl/Api/AdminAcl.php index 96085e152..9fead0524 100644 --- a/app/Services/Acl/Api/AdminAcl.php +++ b/app/Services/Acl/Api/AdminAcl.php @@ -34,6 +34,7 @@ class AdminAcl const RESOURCE_EGGS = 'eggs'; const RESOURCE_DATABASE_HOSTS = 'database_hosts'; const RESOURCE_SERVER_DATABASES = 'server_databases'; + const RESOURCE_ROLES = 'roles'; /** * Determine if an API key has permission to perform a specific read/write operation. @@ -69,7 +70,6 @@ class AdminAcl * Return a list of all resource constants defined in this ACL. * * @return array - * @throws \ReflectionException */ public static function getResourceList(): array { diff --git a/app/Transformers/Api/Application/AdminRoleTransformer.php b/app/Transformers/Api/Application/AdminRoleTransformer.php new file mode 100644 index 000000000..de3ebded2 --- /dev/null +++ b/app/Transformers/Api/Application/AdminRoleTransformer.php @@ -0,0 +1,33 @@ + $role->id, + 'name' => $role->name, + 'description' => $role->description, + ]; + } +} diff --git a/resources/scripts/api/admin/roles/createRole.ts b/resources/scripts/api/admin/roles/createRole.ts index e62c9bf52..b1d6a7861 100644 --- a/resources/scripts/api/admin/roles/createRole.ts +++ b/resources/scripts/api/admin/roles/createRole.ts @@ -6,7 +6,7 @@ export default (name: string, description?: string): Promise => { http.post('/api/application/roles', { name, description, }) - .then(({ data }) => resolve(data)) + .then(({ data }) => resolve(data.attributes)) .catch(reject); }); }; diff --git a/resources/scripts/api/admin/roles/getRoles.ts b/resources/scripts/api/admin/roles/getRoles.ts index 63def3c1d..0a6213a08 100644 --- a/resources/scripts/api/admin/roles/getRoles.ts +++ b/resources/scripts/api/admin/roles/getRoles.ts @@ -1,16 +1,16 @@ import http from '@/api/http'; +import { rawDataToAdminRole } from '@/api/transformers'; export interface Role { id: number; name: string; description: string | null; - sortId: number; } export default (): Promise => { return new Promise((resolve, reject) => { http.get('/api/application/roles') - .then(({ data }) => resolve(data || [])) + .then(({ data }) => resolve((data.data || []).map(rawDataToAdminRole))) .catch(reject); }); }; diff --git a/resources/scripts/api/transformers.ts b/resources/scripts/api/transformers.ts index e08c00076..94d4f711b 100644 --- a/resources/scripts/api/transformers.ts +++ b/resources/scripts/api/transformers.ts @@ -1,3 +1,4 @@ +import { Role } from '@/api/admin/roles/getRoles'; import { Allocation } from '@/api/server/getServer'; import { FractalResponseData } from '@/api/http'; import { FileObject } from '@/api/server/files/loadDirectory'; @@ -74,3 +75,9 @@ export const rawDataToServerEggVariable = ({ attributes }: FractalResponseData): isEditable: attributes.is_editable, rules: attributes.rules.split('|'), }); + +export const rawDataToAdminRole = ({ attributes }: FractalResponseData): Role => ({ + id: attributes.id, + name: attributes.name, + description: attributes.description, +}); diff --git a/routes/api-application.php b/routes/api-application.php index d6eb1d83c..6000d138a 100644 --- a/routes/api-application.php +++ b/routes/api-application.php @@ -131,9 +131,10 @@ Route::group(['prefix' => '/nests'], function () { */ Route::group(['prefix' => '/roles'], function () { - Route::get('/', 'RoleController@index')->name('api.application.roles'); + Route::get('/', 'Roles\RoleController@index'); + Route::get('/{role}', 'Roles\RoleController@view'); - Route::post('/', 'RoleController@create'); + Route::post('/', 'Roles\oleController@store'); - Route::delete('/{role}', 'RoleController@delete'); + Route::delete('/{role}', 'Roles\RoleController@delete'); });