From d644a5395175cd8446991411d4a0121e7fd2bd92 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 12 Jan 2018 20:39:15 -0600 Subject: [PATCH] Update users & locations to use new permissions format --- .../Admin/Locations/LocationController.php | 49 ++++++++++------- .../API/Admin/Users/UserController.php | 27 +++++----- .../Admin/Locations/DeleteLocationRequest.php | 32 +++++++++++ .../Admin/Locations/GetLocationRequest.php | 21 ++++++++ .../Admin/Locations/GetLocationsRequest.php | 19 +++++++ .../Admin/Locations/StoreLocationRequest.php | 46 ++++++++++++++++ .../Admin/Locations/UpdateLocationRequest.php | 36 +++++++++++++ .../API/Admin/Users/DeleteUserRequest.php | 32 +++++++++++ .../API/Admin/Users/GetUserRequest.php | 16 +----- .../API/Admin/Users/StoreUserRequest.php | 54 +++++++++++++++++++ .../API/Admin/Users/UpdateUserRequest.php | 41 ++++++++++++++ app/Models/Location.php | 7 --- app/Models/User.php | 2 + .../Api/Admin/LocationTransformer.php | 30 ++++------- routes/api-admin.php | 15 ++++++ 15 files changed, 355 insertions(+), 72 deletions(-) create mode 100644 app/Http/Requests/API/Admin/Locations/DeleteLocationRequest.php create mode 100644 app/Http/Requests/API/Admin/Locations/GetLocationRequest.php create mode 100644 app/Http/Requests/API/Admin/Locations/GetLocationsRequest.php create mode 100644 app/Http/Requests/API/Admin/Locations/StoreLocationRequest.php create mode 100644 app/Http/Requests/API/Admin/Locations/UpdateLocationRequest.php create mode 100644 app/Http/Requests/API/Admin/Users/DeleteUserRequest.php create mode 100644 app/Http/Requests/API/Admin/Users/StoreUserRequest.php create mode 100644 app/Http/Requests/API/Admin/Users/UpdateUserRequest.php diff --git a/app/Http/Controllers/API/Admin/Locations/LocationController.php b/app/Http/Controllers/API/Admin/Locations/LocationController.php index 537c331e9..54e9a9b46 100644 --- a/app/Http/Controllers/API/Admin/Locations/LocationController.php +++ b/app/Http/Controllers/API/Admin/Locations/LocationController.php @@ -3,18 +3,19 @@ namespace Pterodactyl\Http\Controllers\API\Admin\Locations; use Spatie\Fractal\Fractal; -use Illuminate\Http\Request; use Illuminate\Http\Response; use Pterodactyl\Models\Location; use Illuminate\Http\JsonResponse; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Http\Requests\Admin\LocationFormRequest; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use Pterodactyl\Services\Locations\LocationUpdateService; use Pterodactyl\Services\Locations\LocationCreationService; use Pterodactyl\Services\Locations\LocationDeletionService; use Pterodactyl\Transformers\Api\Admin\LocationTransformer; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; +use Pterodactyl\Http\Requests\API\Admin\Locations\GetLocationsRequest; +use Pterodactyl\Http\Requests\API\Admin\Locations\DeleteLocationRequest; +use Pterodactyl\Http\Requests\API\Admin\Locations\UpdateLocationRequest; class LocationController extends Controller { @@ -69,15 +70,15 @@ class LocationController extends Controller /** * Return all of the locations currently registered on the Panel. * - * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Http\Requests\API\Admin\Locations\GetLocationsRequest $request * @return array */ - public function index(Request $request): array + public function index(GetLocationsRequest $request): array { $locations = $this->repository->paginated(100); return $this->fractal->collection($locations) - ->transformWith(new LocationTransformer($request)) + ->transformWith((new LocationTransformer)->setKey($request->key())) ->withResourceName('location') ->paginateWith(new IlluminatePaginatorAdapter($locations)) ->toArray(); @@ -86,59 +87,67 @@ class LocationController extends Controller /** * Return a single location. * - * @param \Illuminate\Http\Request $request - * @param \Pterodactyl\Models\Location $location + * @param \Pterodactyl\Http\Controllers\API\Admin\Locations\GetLocationRequest $request + * @param \Pterodactyl\Models\Location $location * @return array */ - public function view(Request $request, Location $location): array + public function view(GetLocationRequest $request, Location $location): array { return $this->fractal->item($location) - ->transformWith(new LocationTransformer($request)) + ->transformWith((new LocationTransformer)->setKey($request->key())) ->withResourceName('location') ->toArray(); } /** - * @param \Pterodactyl\Http\Requests\Admin\LocationFormRequest $request + * Store a new location on the Panel and return a HTTP/201 response code with the + * new location attached. + * + * @param \Pterodactyl\Http\Controllers\API\Admin\Locations\StoreLocationRequest $request * @return \Illuminate\Http\JsonResponse * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function store(LocationFormRequest $request): JsonResponse + public function store(StoreLocationRequest $request): JsonResponse { - $location = $this->creationService->handle($request->normalize()); + $location = $this->creationService->handle($request->validated()); return $this->fractal->item($location) - ->transformWith(new LocationTransformer($request)) + ->transformWith((new LocationTransformer)->setKey($request->key())) ->withResourceName('location') ->respond(201); } /** - * @param \Pterodactyl\Http\Requests\Admin\LocationFormRequest $request - * @param \Pterodactyl\Models\Location $location + * Update a location on the Panel and return the updated record to the user. + * + * @param \Pterodactyl\Http\Requests\API\Admin\Locations\UpdateLocationRequest $request + * @param \Pterodactyl\Models\Location $location * @return array * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update(LocationFormRequest $request, Location $location): array + public function update(UpdateLocationRequest $request, Location $location): array { - $location = $this->updateService->handle($location, $request->normalize()); + $location = $this->updateService->handle($location, $request->validated()); return $this->fractal->item($location) - ->transformWith(new LocationTransformer($request)) + ->transformWith((new LocationTransformer)->setKey($request->key())) ->withResourceName('location') ->toArray(); } /** - * @param \Pterodactyl\Models\Location $location + * Delete a location from the Panel. + * + * @param \Pterodactyl\Http\Requests\API\Admin\Locations\DeleteLocationRequest $request + * @param \Pterodactyl\Models\Location $location * @return \Illuminate\Http\Response * * @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException */ - public function delete(Location $location): Response + public function delete(DeleteLocationRequest $request, Location $location): Response { $this->deletionService->handle($location); diff --git a/app/Http/Controllers/API/Admin/Users/UserController.php b/app/Http/Controllers/API/Admin/Users/UserController.php index 9801fa6ef..3cb435b66 100644 --- a/app/Http/Controllers/API/Admin/Users/UserController.php +++ b/app/Http/Controllers/API/Admin/Users/UserController.php @@ -11,12 +11,14 @@ use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Services\Users\UserUpdateService; use Pterodactyl\Services\Users\UserCreationService; use Pterodactyl\Services\Users\UserDeletionService; -use Pterodactyl\Http\Requests\Admin\UserFormRequest; use Pterodactyl\Transformers\Api\Admin\UserTransformer; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Http\Requests\API\Admin\Users\GetUserRequest; use Pterodactyl\Http\Requests\API\Admin\Users\GetUsersRequest; +use Pterodactyl\Http\Requests\API\Admin\Users\StoreUserRequest; +use Pterodactyl\Http\Requests\API\Admin\Users\DeleteUserRequest; +use Pterodactyl\Http\Requests\API\Admin\Users\UpdateUserRequest; class UserController extends Controller { @@ -111,17 +113,17 @@ class UserController extends Controller * Revocation errors are returned under the 'revocation_errors' key in the response * meta. If there are no errors this is an empty array. * - * @param \Pterodactyl\Http\Requests\Admin\UserFormRequest $request - * @param \Pterodactyl\Models\User $user + * @param \Pterodactyl\Http\Requests\API\Admin\Users\UpdateUserRequest $request + * @param \Pterodactyl\Models\User $user * @return array * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update(UserFormRequest $request, User $user): array + public function update(UpdateUserRequest $request, User $user): array { $this->updateService->setUserLevel(User::USER_LEVEL_ADMIN); - $collection = $this->updateService->handle($user, $request->normalize()); + $collection = $this->updateService->handle($user, $request->validated()); $errors = []; if (! empty($collection->get('exceptions'))) { @@ -138,7 +140,7 @@ class UserController extends Controller } $response = $this->fractal->item($collection->get('model')) - ->transformWith(new UserTransformer($request)) + ->transformWith((new UserTransformer)->setKey($request->key())) ->withResourceName('user'); if (count($errors) > 0) { @@ -154,18 +156,18 @@ class UserController extends Controller * Store a new user on the system. Returns the created user and a HTTP/201 * header on successful creation. * - * @param \Pterodactyl\Http\Requests\Admin\UserFormRequest $request + * @param \Pterodactyl\Http\Requests\API\Admin\Users\StoreUserRequest $request * @return \Illuminate\Http\JsonResponse * * @throws \Exception * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function store(UserFormRequest $request): JsonResponse + public function store(StoreUserRequest $request): JsonResponse { - $user = $this->creationService->handle($request->normalize()); + $user = $this->creationService->handle($request->validated()); return $this->fractal->item($user) - ->transformWith(new UserTransformer($request)) + ->transformWith((new UserTransformer)->setKey($request->key())) ->withResourceName('user') ->addMeta([ 'link' => route('api.admin.user.view', ['user' => $user->id]), @@ -177,12 +179,13 @@ class UserController extends Controller * Handle a request to delete a user from the Panel. Returns a HTTP/204 response * on successful deletion. * - * @param \Pterodactyl\Models\User $user + * @param \Pterodactyl\Http\Requests\API\Admin\Users\DeleteUserRequest $request + * @param \Pterodactyl\Models\User $user * @return \Illuminate\Http\Response * * @throws \Pterodactyl\Exceptions\DisplayException */ - public function delete(User $user): Response + public function delete(DeleteUserRequest $request, User $user): Response { $this->deletionService->handle($user); diff --git a/app/Http/Requests/API/Admin/Locations/DeleteLocationRequest.php b/app/Http/Requests/API/Admin/Locations/DeleteLocationRequest.php new file mode 100644 index 000000000..eb2ebba51 --- /dev/null +++ b/app/Http/Requests/API/Admin/Locations/DeleteLocationRequest.php @@ -0,0 +1,32 @@ +route()->parameter('location'); + + return $location instanceof Location && $location->exists; + } +} diff --git a/app/Http/Requests/API/Admin/Locations/GetLocationRequest.php b/app/Http/Requests/API/Admin/Locations/GetLocationRequest.php new file mode 100644 index 000000000..36485e199 --- /dev/null +++ b/app/Http/Requests/API/Admin/Locations/GetLocationRequest.php @@ -0,0 +1,21 @@ +route()->parameter('location'); + + return $location instanceof Location && $location->exists; + } +} diff --git a/app/Http/Requests/API/Admin/Locations/GetLocationsRequest.php b/app/Http/Requests/API/Admin/Locations/GetLocationsRequest.php new file mode 100644 index 000000000..6e42c4682 --- /dev/null +++ b/app/Http/Requests/API/Admin/Locations/GetLocationsRequest.php @@ -0,0 +1,19 @@ +only([ + 'long', + 'short', + ])->toArray(); + } + + /** + * Rename fields to be more clear in error messages. + * + * @return array + */ + public function attributes() + { + return [ + 'long' => 'Location Description', + 'short' => 'Location Identifier', + ]; + } +} diff --git a/app/Http/Requests/API/Admin/Locations/UpdateLocationRequest.php b/app/Http/Requests/API/Admin/Locations/UpdateLocationRequest.php new file mode 100644 index 000000000..3adc3020e --- /dev/null +++ b/app/Http/Requests/API/Admin/Locations/UpdateLocationRequest.php @@ -0,0 +1,36 @@ +route()->parameter('location'); + + return $location instanceof Location && $location->exists; + } + + /** + * Rules to validate this request aganist. + * + * @return array + */ + public function rules(): array + { + $locationId = $this->route()->parameter('location')->id; + + return collect(Location::getUpdateRulesForId($locationId))->only([ + 'short', + 'long', + ]); + } +} diff --git a/app/Http/Requests/API/Admin/Users/DeleteUserRequest.php b/app/Http/Requests/API/Admin/Users/DeleteUserRequest.php new file mode 100644 index 000000000..d13606a53 --- /dev/null +++ b/app/Http/Requests/API/Admin/Users/DeleteUserRequest.php @@ -0,0 +1,32 @@ +route()->parameter('user'); + + return $user instanceof User && $user->exists; + } +} diff --git a/app/Http/Requests/API/Admin/Users/GetUserRequest.php b/app/Http/Requests/API/Admin/Users/GetUserRequest.php index a699adcb5..4f159253b 100644 --- a/app/Http/Requests/API/Admin/Users/GetUserRequest.php +++ b/app/Http/Requests/API/Admin/Users/GetUserRequest.php @@ -3,21 +3,9 @@ namespace Pterodactyl\Http\Requests\API\Admin\Users; use Pterodactyl\Models\User; -use Pterodactyl\Services\Acl\Api\AdminAcl; -use Pterodactyl\Http\Requests\API\Admin\ApiAdminRequest; -class GetUserRequest extends ApiAdminRequest +class GetUserRequest extends GetUsersRequest { - /** - * @var string - */ - protected $resource = AdminAcl::RESOURCE_USERS; - - /** - * @var int - */ - protected $permission = AdminAcl::READ; - /** * Determine if the requested user exists on the Panel. * @@ -27,6 +15,6 @@ class GetUserRequest extends ApiAdminRequest { $user = $this->route()->parameter('user'); - return $user instanceof User && $user->exists; + return $user instanceof User && $user->exists; } } diff --git a/app/Http/Requests/API/Admin/Users/StoreUserRequest.php b/app/Http/Requests/API/Admin/Users/StoreUserRequest.php new file mode 100644 index 000000000..545cea0c5 --- /dev/null +++ b/app/Http/Requests/API/Admin/Users/StoreUserRequest.php @@ -0,0 +1,54 @@ +only([ + 'external_id', + 'email', + 'username', + 'name_first', + 'name_last', + 'password', + 'language', + 'root_admin', + ])->toArray(); + } + + /** + * Rename some fields to be more user friendly. + * + * @return array + */ + public function attributes() + { + return [ + 'external_id' => 'Third Party Identifier', + 'name_first' => 'First Name', + 'name_last' => 'Last Name', + 'root_admin' => 'Root Administrator Status', + ]; + } +} diff --git a/app/Http/Requests/API/Admin/Users/UpdateUserRequest.php b/app/Http/Requests/API/Admin/Users/UpdateUserRequest.php new file mode 100644 index 000000000..e2b29d059 --- /dev/null +++ b/app/Http/Requests/API/Admin/Users/UpdateUserRequest.php @@ -0,0 +1,41 @@ +route()->parameter('user'); + + return $user instanceof User && $user->exists; + } + + /** + * Return the validation rules for this request. + * + * @return array + */ + public function rules(): array + { + $userId = $this->route()->parameter('user')->id; + + return collect(User::getUpdateRulesForId($userId))->only([ + 'external_id', + 'email', + 'username', + 'name_first', + 'name_last', + 'password', + 'language', + 'root_admin', + ])->toArray(); + } +} diff --git a/app/Models/Location.php b/app/Models/Location.php index 62c4eb47d..30808c0dd 100644 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Models; diff --git a/app/Models/User.php b/app/Models/User.php index 8c1bf5854..82cec4ddc 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -126,6 +126,7 @@ class User extends Model implements protected static $applicationRules = [ 'uuid' => 'required', 'email' => 'required', + 'external_id' => 'sometimes', 'username' => 'required', 'name_first' => 'required', 'name_last' => 'required', @@ -142,6 +143,7 @@ class User extends Model implements protected static $dataIntegrityRules = [ 'uuid' => 'string|size:36|unique:users,uuid', 'email' => 'email|unique:users,email', + 'external_id' => 'nullable|string|max:255|unique:users,external_id', 'username' => 'alpha_dash|between:1,255|unique:users,username', 'name_first' => 'string|between:1,255', 'name_last' => 'string|between:1,255', diff --git a/app/Transformers/Api/Admin/LocationTransformer.php b/app/Transformers/Api/Admin/LocationTransformer.php index 7d4b9fff1..81fa9d928 100644 --- a/app/Transformers/Api/Admin/LocationTransformer.php +++ b/app/Transformers/Api/Admin/LocationTransformer.php @@ -3,7 +3,7 @@ namespace Pterodactyl\Transformers\Api\Admin; use Pterodactyl\Models\Location; -use Pterodactyl\Transformers\Api\BaseTransformer; +use Pterodactyl\Services\Acl\Api\AdminAcl; class LocationTransformer extends BaseTransformer { @@ -29,41 +29,33 @@ class LocationTransformer extends BaseTransformer * Return the nodes associated with this location. * * @param \Pterodactyl\Models\Location $location - * @return bool|\League\Fractal\Resource\Collection - * - * @throws \Pterodactyl\Exceptions\PterodactylException + * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource */ public function includeServers(Location $location) { - if (! $this->authorize('server-list')) { - return false; + if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) { + return $this->null(); } - if (! $location->relationLoaded('servers')) { - $location->load('servers'); - } + $location->loadMissing('servers'); - return $this->collection($location->getRelation('servers'), new ServerTransformer($this->getRequest()), 'server'); + return $this->collection($location->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server'); } /** * Return the nodes associated with this location. * * @param \Pterodactyl\Models\Location $location - * @return bool|\League\Fractal\Resource\Collection - * - * @throws \Pterodactyl\Exceptions\PterodactylException + * @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource */ public function includeNodes(Location $location) { - if (! $this->authorize('node-list')) { - return false; + if (! $this->authorize(AdminAcl::RESOURCE_NODES)) { + return $this->null(); } - if (! $location->relationLoaded('nodes')) { - $location->load('nodes'); - } + $location->loadMissing('nodes'); - return $this->collection($location->getRelation('nodes'), new NodeTransformer($this->getRequest()), 'node'); + return $this->collection($location->getRelation('nodes'), $this->makeTransformer(NodeTransformer::class), 'node'); } } diff --git a/routes/api-admin.php b/routes/api-admin.php index 2f51be62b..6b04b9cf6 100644 --- a/routes/api-admin.php +++ b/routes/api-admin.php @@ -1,6 +1,9 @@ '/users'], function () { | */ Route::group(['prefix' => '/nodes'], function () { + Route::bind('node', function ($value) { + return Node::find($value) ?? new Node; + }); + Route::get('/', 'Nodes\NodeController@index')->name('api.admin.node.list'); Route::get('/{node}', 'Nodes\NodeController@view')->name('api.admin.node.view'); @@ -42,6 +49,10 @@ Route::group(['prefix' => '/nodes'], function () { Route::delete('/{node}', 'Nodes\NodeController@delete')->name('api.admin.node.delete'); Route::group(['prefix' => '/{node}/allocations'], function () { + Route::bind('allocation', function ($value) { + return Allocation::find($value) ?? new Allocation; + }); + Route::get('/', 'Nodes\AllocationController@index')->name('api.admin.node.allocations.list'); Route::delete('/{allocation}', 'Nodes\AllocationController@delete')->name('api.admin.node.allocations.delete'); @@ -57,6 +68,10 @@ Route::group(['prefix' => '/nodes'], function () { | */ Route::group(['prefix' => '/locations'], function () { + Route::bind('location', function ($value) { + return Location::find($value) ?? new Location; + }); + Route::get('/', 'Locations\LocationController@index')->name('api.admin.location.list'); Route::get('/{location}', 'Locations\LocationController@view')->name('api.admin.location.view');