diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index ed7c004b2..9f94b003e 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -71,6 +71,7 @@ class Handler extends ExceptionHandler $response = response()->json( [ 'error' => $displayError, + 'type' => (! config('app.debug')) ? null : class_basename($exception), 'http_code' => (method_exists($exception, 'getStatusCode')) ? $exception->getStatusCode() : 500, 'trace' => (! config('app.debug')) ? null : $exception->getTrace(), ], diff --git a/app/Http/Controllers/API/Admin/Users/UserController.php b/app/Http/Controllers/API/Admin/Users/UserController.php index 46243b19c..68b6c0062 100644 --- a/app/Http/Controllers/API/Admin/Users/UserController.php +++ b/app/Http/Controllers/API/Admin/Users/UserController.php @@ -4,20 +4,30 @@ namespace Pterodactyl\Http\Controllers\API\Admin\Users; use Spatie\Fractal\Fractal; use Illuminate\Http\Request; +use Pterodactyl\Models\User; +use Illuminate\Http\Response; +use Illuminate\Http\JsonResponse; use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Services\Users\UserUpdateService; +use Pterodactyl\Services\Users\UserCreationService; +use Pterodactyl\Services\Users\UserDeletionService; use Pterodactyl\Transformers\Admin\UserTransformer; +use Pterodactyl\Http\Requests\Admin\UserFormRequest; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; -use Illuminate\Contracts\Config\Repository as ConfigRepository; -/** - * @SWG\Swagger( - * schemes={"https"}, - * basePath="/api/admin/users" - * ) - */ class UserController extends Controller { + /** + * @var \Pterodactyl\Services\Users\UserCreationService + */ + private $creationService; + + /** + * @var \Pterodactyl\Services\Users\UserDeletionService + */ + private $deletionService; + /** * @var \Spatie\Fractal\Fractal */ @@ -27,47 +37,160 @@ class UserController extends Controller * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface */ private $repository; + /** - * @var \Illuminate\Contracts\Config\Repository + * @var \Pterodactyl\Services\Users\UserUpdateService */ - private $config; + private $updateService; /** * UserController constructor. * - * @param \Illuminate\Contracts\Config\Repository $config * @param \Spatie\Fractal\Fractal $fractal * @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository + * @param \Pterodactyl\Services\Users\UserCreationService $creationService + * @param \Pterodactyl\Services\Users\UserDeletionService $deletionService + * @param \Pterodactyl\Services\Users\UserUpdateService $updateService */ public function __construct( - ConfigRepository $config, Fractal $fractal, - UserRepositoryInterface $repository + UserRepositoryInterface $repository, + UserCreationService $creationService, + UserDeletionService $deletionService, + UserUpdateService $updateService ) { + $this->creationService = $creationService; + $this->deletionService = $deletionService; $this->fractal = $fractal; $this->repository = $repository; - $this->config = $config; + $this->updateService = $updateService; } /** - * Handle request to list all users on the panel. + * Handle request to list all users on the panel. Returns a JSONAPI representation + * of a collection of users including any defined relations passed in + * the request. * * @param \Illuminate\Http\Request $request * @return array */ - public function index(Request $request) + public function index(Request $request): array { - $users = $this->repository->all($this->config->get('pterodactyl.paginate.api.users')); + $users = $this->repository->all(config('pterodactyl.paginate.api.users')); $fractal = $this->fractal->collection($users) ->transformWith(new UserTransformer($request)) ->withResourceName('user') ->paginateWith(new IlluminatePaginatorAdapter($users)); - if ($this->config->get('pterodactyl.api.include_on_list') && $request->input('include')) { + if (config('pterodactyl.api.include_on_list') && $request->has('include')) { $fractal->parseIncludes(explode(',', $request->input('include'))); } return $fractal->toArray(); } + + /** + * Handle a request to view a single user. Includes any relations that + * were defined in the request. + * + * @param \Illuminate\Http\Request $request + * @param \Pterodactyl\Models\User $user + * @return array + */ + public function view(Request $request, User $user): array + { + $fractal = $this->fractal->item($user) + ->transformWith(new UserTransformer($request)) + ->withResourceName('user'); + + if ($request->has('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->toArray(); + } + + /** + * Update an existing user on the system and return the response. Returns the + * updated user model response on success. Supports handling of token revocation + * errors when switching a user from an admin to a normal user. + * + * 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 + * @return array + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(UserFormRequest $request, User $user): array + { + $this->updateService->setUserLevel(User::USER_LEVEL_ADMIN); + $collection = $this->updateService->handle($user, $request->normalize()); + + $errors = []; + if (! empty($collection->get('exceptions'))) { + foreach ($collection->get('exceptions') as $node => $exception) { + /** @var \GuzzleHttp\Exception\RequestException $exception */ + /** @var \GuzzleHttp\Psr7\Response|null $response */ + $response = method_exists($exception, 'getResponse') ? $exception->getResponse() : null; + $message = trans('admin/server.exceptions.daemon_exception', [ + 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), + ]); + + $errors[] = ['message' => $message, 'node' => $node]; + } + } + + return $this->fractal->item($collection->get('user')) + ->transformWith(new UserTransformer($request)) + ->withResourceName('user') + ->addMeta([ + 'revocation_errors' => $errors, + ]) + ->toArray(); + } + + /** + * 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 + * @return \Illuminate\Http\JsonResponse + * + * @throws \Exception + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + */ + public function store(UserFormRequest $request): JsonResponse + { + $user = $this->creationService->handle($request->normalize()); + + return $this->fractal->item($user) + ->transformWith(new UserTransformer($request)) + ->withResourceName('user') + ->addMeta([ + 'link' => route('api.admin.user.view', ['user' => $user->id]), + ]) + ->respond(201); + } + + /** + * Handle a request to delete a user from the Panel. Returns a HTTP/204 response + * on successful deletion. + * + * @param \Pterodactyl\Models\User $user + * @return \Illuminate\Http\Response + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete(User $user): Response + { + $this->deletionService->handle($user); + + return response('', 204); + } } diff --git a/routes/api-admin.php b/routes/api-admin.php index abd570ec9..884b00dbc 100644 --- a/routes/api-admin.php +++ b/routes/api-admin.php @@ -10,10 +10,10 @@ */ Route::group(['prefix' => '/users'], function () { Route::get('/', 'Users\UserController@index')->name('api.admin.user.list'); - Route::get('/{id}', 'Users\UserController@view'); + Route::get('/{user}', 'Users\UserController@view')->name('api.admin.user.view'); - Route::post('/', 'Users\UserController@store'); - Route::put('/{id}', 'Users\UserController@update'); + Route::post('/', 'Users\UserController@store')->name('api.admin.user.store'); + Route::put('/{user}', 'Users\UserController@update')->name('api.admin.user.update'); - Route::delete('/{id}', 'Users\UserController@delete'); + Route::delete('/{user}', 'Users\UserController@delete')->name('api.admin.user.delete'); });