diff --git a/CHANGELOG.md b/CHANGELOG.md index f4e652519..7d7b219cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * Attempting to reset a password for an account that does not exist no longer returns an error, rather it displays a success message. Failed resets trigger a `Pterodactyl\Events\Auth\FailedPasswordReset` event that can be caught if needed to perform other actions. * Servers are no longer queued for deletion due to the general hassle and extra logic required. * Updated all panel components to run on Laravel v5.4 rather than 5.3 which is EOL. +* Routes are now handled in the `routes/` folder, and use a significantly cleaner syntax. Controller names and methods have been updated as well to be clearer as well as avoid conflicts with PHP reserved keywords. +* API has been completely overhauled to use new permissions system. **Any old API keys will immediately become invalid and fail to operate properly anymore. You will need to generate new keys.** ## v0.6.0-pre.7 (Courageous Carniadactylus) ### Fixed diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index bab6ef3e1..71d66cdc6 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -4,7 +4,6 @@ namespace Pterodactyl\Exceptions; use Log; use Exception; -use DisplayException; use Illuminate\Auth\AuthenticationException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; @@ -46,10 +45,20 @@ class Handler extends ExceptionHandler */ public function render($request, Exception $exception) { - if ($request->expectsJson()) { + if ($request->expectsJson() || $request->isJson() || $request->is(...config('pterodactyl.json_routes'))) { + $exception = $this->prepareException($exception); + + if (config('app.debug')) { + $report = [ + 'code' => (! $this->isHttpException($exception)) ?: $exception->getStatusCode(), + 'message' => class_basename($exception) . ' in ' . $exception->getFile() . ' on line ' . $exception->getLine(), + ]; + } + $response = response()->json([ - 'error' => ($exception instanceof DisplayException) ? $exception->getMessage() : 'An unhandled error occured while attempting to process this request.', - ], ($this->isHttpException($exception)) ? $exception->getStatusCode() : 500); + 'error' => (config('app.debug')) ? $exception->getMessage() : 'An unhandled exception was encountered with this request.', + 'exception' => ! isset($report) ?: $report, + ], ($this->isHttpException($exception)) ? $exception->getStatusCode() : 500, [], JSON_UNESCAPED_SLASHES); parent::report($exception); } diff --git a/app/Http/Controllers/API/LocationController.php b/app/Http/Controllers/API/Admin/LocationController.php old mode 100755 new mode 100644 similarity index 69% rename from app/Http/Controllers/API/LocationController.php rename to app/Http/Controllers/API/Admin/LocationController.php index 29ff72c34..41405e91e --- a/app/Http/Controllers/API/LocationController.php +++ b/app/Http/Controllers/API/Admin/LocationController.php @@ -22,27 +22,30 @@ * SOFTWARE. */ -namespace Pterodactyl\Http\Controllers\API; +namespace Pterodactyl\Http\Controllers\API\Admin; +use Fractal; use Illuminate\Http\Request; use Pterodactyl\Models\Location; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Transformers\Admin\LocationTransformer; -class LocationController extends BaseController +class LocationController extends Controller { /** - * Lists all locations currently on the system. + * Controller to handle returning all locations on the system. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return array */ public function index(Request $request) { - return Location::with('nodes')->get()->map(function ($item) { - $item->nodes->transform(function ($item) { - return collect($item)->only(['id', 'name', 'fqdn', 'scheme', 'daemonListen', 'daemonSFTP']); - }); + $this->authorize('location-list', $request->apiKey()); - return $item; - })->toArray(); + return Fractal::create() + ->collection(Location::all()) + ->transformWith(new LocationTransformer($request)) + ->withResourceName('location') + ->toArray(); } } diff --git a/app/Http/Controllers/API/Admin/NodeController.php b/app/Http/Controllers/API/Admin/NodeController.php new file mode 100644 index 000000000..d70e89969 --- /dev/null +++ b/app/Http/Controllers/API/Admin/NodeController.php @@ -0,0 +1,173 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\Node; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\NodeRepository; +use Pterodactyl\Transformers\Admin\NodeTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; + +class NodeController extends Controller +{ + /** + * Controller to handle returning all nodes on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('node-list', $request->apiKey()); + + $nodes = Node::paginate(config('pterodactyl.paginate.api.nodes')); + $fractal = Fractal::create()->collection($nodes) + ->transformWith(new NodeTransformer($request)) + ->withResourceName('user') + ->paginateWith(new IlluminatePaginatorAdapter($nodes)); + + if (config('pterodactyl.api.include_on_list') && $request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->toArray(); + } + + /** + * Display information about a single node on the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('node-view', $request->apiKey()); + + $fractal = Fractal::create()->item(Node::findOrFail($id)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new NodeTransformer($request)) + ->withResourceName('node') + ->toArray(); + } + + /** + * Display information about a single node on the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse + */ + public function viewConfig(Request $request, $id) + { + $this->authorize('node-view-config', $request->apiKey()); + + $node = Node::findOrFail($id); + + return response()->json(json_decode($node->getConfigurationAsJson())); + } + + /** + * Create a new node on the system. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse|array + */ + public function store(Request $request) + { + $this->authorize('node-create', $request->apiKey()); + + $repo = new NodeRepository; + try { + $node = $repo->create(array_merge( + $request->only([ + 'public', 'disk_overallocate', 'memory_overallocate', + ]), + $request->intersect([ + 'name', 'location_id', 'fqdn', + 'scheme', 'memory', 'disk', + 'daemonBase', 'daemonSFTP', 'daemonListen', + ]) + )); + + $fractal = Fractal::create()->item($node)->transformWith(new NodeTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('node')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to create this node. Please try again.', + ], 500); + } + } + + /** + * Delete a node from the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function delete(Request $request, $id) + { + $this->authorize('node-delete', $request->apiKey()); + + $repo = new NodeRepository; + try { + $repo->delete($id); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to delete this node. Please try again.', + ], 500); + } + } +} diff --git a/app/Http/Controllers/API/Admin/ServerController.php b/app/Http/Controllers/API/Admin/ServerController.php new file mode 100644 index 000000000..89dfd8cd0 --- /dev/null +++ b/app/Http/Controllers/API/Admin/ServerController.php @@ -0,0 +1,428 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\Server; +use GuzzleHttp\Exception\TransferException; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\ServerRepository; +use Pterodactyl\Transformers\Admin\ServerTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; + +class ServerController extends Controller +{ + /** + * Controller to handle returning all servers on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('server-list', $request->apiKey()); + + $servers = Server::paginate(config('pterodactyl.paginate.api.servers')); + $fractal = Fractal::create()->collection($servers) + ->transformWith(new ServerTransformer($request)) + ->withResourceName('user') + ->paginateWith(new IlluminatePaginatorAdapter($servers)); + + if (config('pterodactyl.api.include_on_list') && $request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->toArray(); + } + + /** + * Controller to handle returning information on a single server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('server-view', $request->apiKey()); + + $server = Server::findOrFail($id); + $fractal = Fractal::create()->item($server); + + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new ServerTransformer($request)) + ->withResourceName('server') + ->toArray(); + } + + /** + * Create a new server on the system. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse|array + */ + public function store(Request $request) + { + $this->authorize('server-create', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->create($request->all()); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to add this server. Please try again.', + ], 500); + } + } + + /** + * Delete a server from the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function delete(Request $request, $id) + { + $this->authorize('server-delete', $request->apiKey()); + + $repo = new ServerRepository; + try { + $repo->delete($id, $request->has('force_delete')); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to add this server. Please try again.', + ], 500); + } + } + + /** + * Update the details for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse|array + */ + public function details(Request $request, $id) + { + $this->authorize('server-edit-details', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->updateDetails($id, $request->intersect([ + 'owner_id', 'name', 'description', 'reset_token', + ])); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify this server. Please try again.', + ], 500); + } + } + + /** + * Set the new docker container for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\RedirectResponse|array + */ + public function container(Request $request, $id) + { + $this->authorize('server-edit-container', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->updateContainer($id, $request->intersect('docker_image')); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify this server container. Please try again.', + ], 500); + } + } + + /** + * Toggles the install status for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function install(Request $request, $id) + { + $this->authorize('server-install', $request->apiKey()); + + $repo = new ServerRepository; + try { + $repo->toggleInstall($id); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to toggle the install status for this server. Please try again.', + ], 500); + } + } + + /** + * Setup a server to have a container rebuild. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function rebuild(Request $request, $id) + { + $this->authorize('server-rebuild', $request->apiKey()); + $server = Server::with('node')->findOrFail($id); + + try { + $server->node->guzzleClient([ + 'X-Access-Server' => $server->uuid, + 'X-Access-Token' => $server->node->daemonSecret, + ])->request('POST', '/server/rebuild'); + + return response('', 204); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } + } + + /** + * Manage the suspension status for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function suspend(Request $request, $id) + { + $this->authorize('server-suspend', $request->apiKey()); + + $repo = new ServerRepository; + $action = $request->input('action'); + if (! in_array($action, ['suspend', 'unsuspend'])) { + return response()->json([ + 'error' => 'The action provided was invalid. Action should be one of: suspend, unsuspend.', + ], 400); + } + + try { + $repo->$action($id); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to ' . $action . ' this server. Please try again.', + ], 500); + } + } + + /** + * Update the build configuration for a server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\JsonResponse|array + */ + public function build(Request $request, $id) + { + $this->authorize('server-edit-build', $request->apiKey()); + + $repo = new ServerRepository; + try { + $server = $repo->changeBuild($id, $request->intersect([ + 'allocation_id', 'add_allocations', 'remove_allocations', + 'memory', 'swap', 'io', 'cpu', + ])); + + $fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('server')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify the build settings for this server. Please try again.', + ], 500); + } + } + + /** + * Update the startup command as well as variables. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function startup(Request $request, $id) + { + $this->authorize('server-edit-startup', $request->apiKey()); + + $repo = new ServerRepository; + try { + $repo->updateStartup($id, $request->all(), true); + + return response('', 204); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (TransferException $ex) { + Log::warning($ex); + + return response()->json([ + 'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.', + ], 504); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to modify the startup settings for this server. Please try again.', + ], 500); + } + } +} diff --git a/app/Http/Controllers/API/Admin/ServiceController.php b/app/Http/Controllers/API/Admin/ServiceController.php new file mode 100644 index 000000000..fbe911f14 --- /dev/null +++ b/app/Http/Controllers/API/Admin/ServiceController.php @@ -0,0 +1,74 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\Service; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Transformers\Admin\ServiceTransformer; + +class ServiceController extends Controller +{ + /** + * Controller to handle returning all locations on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('service-list', $request->apiKey()); + + return Fractal::create() + ->collection(Service::all()) + ->transformWith(new ServiceTransformer($request)) + ->withResourceName('service') + ->toArray(); + } + + /** + * Controller to handle returning information on a single server. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('service-view', $request->apiKey()); + + $service = Service::findOrFail($id); + $fractal = Fractal::create()->item($service); + + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new ServiceTransformer($request)) + ->withResourceName('service') + ->toArray(); + } +} diff --git a/app/Http/Controllers/API/Admin/UserController.php b/app/Http/Controllers/API/Admin/UserController.php new file mode 100644 index 000000000..150ca4f87 --- /dev/null +++ b/app/Http/Controllers/API/Admin/UserController.php @@ -0,0 +1,184 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Controllers\API\Admin; + +use Fractal; +use Illuminate\Http\Request; +use Pterodactyl\Models\User; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\UserRepository; +use Pterodactyl\Transformers\Admin\UserTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; + +class UserController extends Controller +{ + /** + * Controller to handle returning all users on the system. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) + { + $this->authorize('user-list', $request->apiKey()); + + $users = User::paginate(config('pterodactyl.paginate.api.users')); + $fractal = Fractal::create()->collection($users) + ->transformWith(new UserTransformer($request)) + ->withResourceName('user') + ->paginateWith(new IlluminatePaginatorAdapter($users)); + + if (config('pterodactyl.api.include_on_list') && $request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->toArray(); + } + + /** + * Display information about a single user on the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return array + */ + public function view(Request $request, $id) + { + $this->authorize('user-view', $request->apiKey()); + + $fractal = Fractal::create()->item(User::findOrFail($id)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new UserTransformer($request)) + ->withResourceName('user') + ->toArray(); + } + + /** + * Create a new user on the system. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse|array + */ + public function store(Request $request) + { + $this->authorize('user-create', $request->apiKey()); + + $repo = new UserRepository; + try { + $user = $repo->create($request->only([ + 'custom_id', 'email', 'password', 'name_first', + 'name_last', 'username', 'root_admin', + ])); + + $fractal = Fractal::create()->item($user)->transformWith(new UserTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('user')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to create this user. Please try again.', + ], 500); + } + } + + /** + * Update a user. + * + * @param \Illuminate\Http\Request $request + * @param int $user + * @return \Illuminate\Http\RedirectResponse + */ + public function update(Request $request, $user) + { + $this->authorize('user-edit', $request->apiKey()); + + $repo = new UserRepository; + try { + $user = $repo->update($user, $request->intersect([ + 'email', 'password', 'name_first', + 'name_last', 'username', 'root_admin', + ])); + + $fractal = Fractal::create()->item($user)->transformWith(new UserTransformer($request)); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->withResourceName('user')->toArray(); + } catch (DisplayValidationException $ex) { + return response()->json([ + 'error' => json_decode($ex->getMessage()), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to update this user. Please try again.', + ], 500); + } + } + + /** + * Delete a user from the system. + * + * @param \Illuminate\Http\Request $request + * @param int $id + * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse + */ + public function delete(Request $request, $id) + { + $this->authorize('user-delete', $request->apiKey()); + + $repo = new UserRepository; + try { + $repo->delete($id); + + return response('', 204); + } catch (DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 400); + } catch (\Exception $ex) { + Log::error($ex); + + return response()->json([ + 'error' => 'An unhandled exception occured while attemping to delete this user. Please try again.', + ], 500); + } + } +} diff --git a/app/Http/Controllers/API/NodeController.php b/app/Http/Controllers/API/NodeController.php deleted file mode 100755 index 6a03bdba1..000000000 --- a/app/Http/Controllers/API/NodeController.php +++ /dev/null @@ -1,172 +0,0 @@ -. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace Pterodactyl\Http\Controllers\API; - -use Log; -use Illuminate\Http\Request; -use Pterodactyl\Models\Node; -use Pterodactyl\Models\Allocation; -use Dingo\Api\Exception\ResourceException; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Repositories\NodeRepository; -use Pterodactyl\Exceptions\DisplayValidationException; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; - -class NodeController extends BaseController -{ - /** - * Lists all nodes currently on the system. - * - * @param Request $request - * @return array - */ - public function index(Request $request) - { - return Node::all()->toArray(); - } - - /** - * Create a new node. - * - * @param Request $request - * @return array - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\DisplayValidationException - */ - public function create(Request $request) - { - $repo = new NodeRepository; - - try { - $node = $repo->create(array_merge( - $request->only([ - 'public', 'disk_overallocate', 'memory_overallocate', - ]), - $request->intersect([ - 'name', 'location_id', 'fqdn', - 'scheme', 'memory', 'disk', - 'daemonBase', 'daemonSFTP', 'daemonListen', - ]) - )); - - return ['id' => $node->id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - Log::error($ex); - throw new BadRequestHttpException('There was an error while attempting to add this node to the system. This error has been logged.'); - } - } - - /** - * Lists specific fields about a server or all fields pertaining to that node. - * - * @param Request $request - * @param int $id - * @param string $fields - * @return array - */ - public function view(Request $request, $id, $fields = null) - { - $node = Node::with('allocations')->findOrFail($id); - - $node->allocations->transform(function ($item) { - return collect($item)->only([ - 'id', 'ip', 'ip_alias', 'port', 'server_id', - ]); - }); - - if (! empty($request->input('fields'))) { - $fields = explode(',', $request->input('fields')); - if (! empty($fields) && is_array($fields)) { - return collect($node)->only($fields); - } - } - - return $node->toArray(); - } - - /** - * Returns a configuration file for a given node. - * - * @param Request $request - * @param int $id - * @return array - */ - public function config(Request $request, $id) - { - $node = Node::findOrFail($id); - - return $node->getConfigurationAsJson(); - } - - /** - * Returns a listing of all allocations for every node. - * - * @param Request $request - * @return array - */ - public function allocations(Request $request) - { - return Allocation::all()->toArray(); - } - - /** - * Returns a listing of the allocation for the specified server id. - * - * @param Request $request - * @param int $id - * @return array - */ - public function allocationsView(Request $request, $id) - { - return Allocation::where('server_id', $id)->get()->toArray(); - } - - /** - * Delete a node. - * - * @param Request $request - * @param int $id - * @return void - */ - public function delete(Request $request, $id) - { - $repo = new NodeRepository; - try { - $repo->delete($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $e) { - throw new ServiceUnavailableHttpException('An error occured while attempting to delete this node.'); - } - } -} diff --git a/app/Http/Controllers/API/ServerController.php b/app/Http/Controllers/API/ServerController.php deleted file mode 100755 index 769fa9412..000000000 --- a/app/Http/Controllers/API/ServerController.php +++ /dev/null @@ -1,231 +0,0 @@ -. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace Pterodactyl\Http\Controllers\API; - -use Log; -use Illuminate\Http\Request; -use Pterodactyl\Models\Server; -use Dingo\Api\Exception\ResourceException; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Repositories\ServerRepository; -use Pterodactyl\Exceptions\DisplayValidationException; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; - -class ServerController extends BaseController -{ - /** - * Lists all servers currently on the system. - * - * @param Request $request - * @return array - */ - public function index(Request $request) - { - return Server::all()->toArray(); - } - - /** - * Create Server. - * - * @param Request $request - * @return array - */ - public function create(Request $request) - { - $repo = new ServerRepository; - - try { - $server = $repo->create($request->all()); - - return ['id' => $server->id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - Log::error($ex); - throw new BadRequestHttpException('There was an error while attempting to add this server to the system.'); - } - } - - /** - * List Specific Server. - * - * @param Request $request - * @param int $id - * @return array - */ - public function view(Request $request, $id) - { - $server = Server::with('node', 'allocations', 'pack')->where('id', $id)->firstOrFail(); - - if (! is_null($request->input('fields'))) { - $fields = explode(',', $request->input('fields')); - if (! empty($fields) && is_array($fields)) { - return collect($server)->only($fields); - } - } - - if ($request->input('daemon') === 'true') { - try { - $response = $server->node->guzzleClient([ - 'X-Access-Token' => $server->node->daemonSecret, - ])->request('GET', '/servers'); - - $server->daemon = json_decode($response->getBody())->{$server->uuid}; - } catch (\GuzzleHttp\Exception\TransferException $ex) { - // Couldn't hit the daemon, return what we have though. - $server->daemon = [ - 'error' => 'There was an error encountered while attempting to connect to the remote daemon.', - ]; - } - } - - $server->allocations->transform(function ($item) { - return collect($item)->except(['created_at', 'updated_at']); - }); - - return $server->toArray(); - } - - /** - * Update Server configuration. - * - * @param Request $request - * @param int $id - * @return array - */ - public function config(Request $request, $id) - { - $repo = new ServerRepository; - - try { - $server = $repo->updateDetails($id, $request->intersect([ - 'owner_id', 'name', 'reset_token', - ])); - - return ['id' => $id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to update server on system due to an error.'); - } - } - - /** - * Update Server Build Configuration. - * - * @param Request $request - * @param int $id - * @return array - */ - public function build(Request $request, $id) - { - $repo = new ServerRepository; - - try { - $server = $repo->changeBuild($id, $request->intersect([ - 'allocation_id', 'add_allocations', 'remove_allocations', - 'memory', 'swap', 'io', 'cpu', - ])); - - return ['id' => $id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to update server on system due to an error.'); - } - } - - /** - * Suspend Server. - * - * @param Request $request - * @param int $id - * @return void - */ - public function suspend(Request $request, $id) - { - try { - $repo = new ServerRepository; - $repo->suspend($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('An error occured while attempting to suspend this server instance.'); - } - } - - /** - * Unsuspend Server. - * - * @param Request $request - * @param int $id - * @return void - */ - public function unsuspend(Request $request, $id) - { - try { - $repo = new ServerRepository; - $repo->unsuspend($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('An error occured while attempting to unsuspend this server instance.'); - } - } - - /** - * Delete Server. - * - * @param Request $request - * @param int $id - * @param string|null $force - * @return void - */ - public function delete(Request $request, $id, $force = null) - { - $repo = new ServerRepository; - - try { - $repo->delete($id, is_null($force)); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $e) { - throw new ServiceUnavailableHttpException('An error occured while attempting to delete this server.'); - } - } -} diff --git a/app/Http/Controllers/API/User/InfoController.php b/app/Http/Controllers/API/User/CoreController.php similarity index 64% rename from app/Http/Controllers/API/User/InfoController.php rename to app/Http/Controllers/API/User/CoreController.php index faf1be622..5c1d07406 100644 --- a/app/Http/Controllers/API/User/InfoController.php +++ b/app/Http/Controllers/API/User/CoreController.php @@ -24,24 +24,28 @@ namespace Pterodactyl\Http\Controllers\API\User; +use Fractal; use Illuminate\Http\Request; -use Pterodactyl\Http\Controllers\API\BaseController; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Transformers\User\OverviewTransformer; -class InfoController extends BaseController +class CoreController extends Controller { - public function me(Request $request) + /** + * Controller to handle base user request for all of their servers. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function index(Request $request) { - return $request->user()->access('service', 'node', 'allocation', 'option')->get()->map(function ($server) { - return [ - 'id' => $server->uuidShort, - 'uuid' => $server->uuid, - 'name' => $server->name, - 'node' => $server->node->name, - 'ip' => $server->allocation->alias, - 'port' => $server->allocation->port, - 'service' => $server->service->name, - 'option' => $server->option->name, - ]; - })->all(); + $this->authorize('user.server-list', $request->apiKey()); + + $servers = $request->user()->access('service', 'node', 'allocation', 'option')->get(); + + return Fractal::collection($servers) + ->transformWith(new OverviewTransformer) + ->withResourceName('server') + ->toArray(); } } diff --git a/app/Http/Controllers/API/User/ServerController.php b/app/Http/Controllers/API/User/ServerController.php index ced350476..1c5399cf9 100644 --- a/app/Http/Controllers/API/User/ServerController.php +++ b/app/Http/Controllers/API/User/ServerController.php @@ -24,109 +24,75 @@ namespace Pterodactyl\Http\Controllers\API\User; -use Log; +use Fractal; use Illuminate\Http\Request; use Pterodactyl\Models\Server; -use Pterodactyl\Http\Controllers\API\BaseController; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\Daemon\PowerRepository; +use Pterodactyl\Transformers\User\ServerTransformer; -class ServerController extends BaseController +class ServerController extends Controller { - public function info(Request $request, $uuid) + /** + * Controller to handle base request for individual server information. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return array + */ + public function index(Request $request, $uuid) { - $server = Server::byUuid($uuid)->load('allocations'); + $this->authorize('user.server-view', $request->apiKey()); - try { - $response = $server->guzzleClient()->request('GET', '/server'); + $server = Server::byUuid($uuid); + $fractal = Fractal::create()->item($server); - $json = json_decode($response->getBody()); - $daemon = [ - 'status' => $json->status, - 'stats' => $json->proc, - ]; - } catch (\Exception $ex) { - $daemon = [ - 'error' => 'An error was encountered while trying to connect to the daemon to collect information. It might be offline.', - ]; - Log::error($ex); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); } - return [ - 'uuidShort' => $server->uuidShort, - 'uuid' => $server->uuid, - 'name' => $server->name, - 'node' => $server->node->name, - 'limits' => [ - 'memory' => $server->memory, - 'swap' => $server->swap, - 'disk' => $server->disk, - 'io' => $server->io, - 'cpu' => $server->cpu, - 'oom_disabled' => (bool) $server->oom_disabled, - ], - 'allocations' => $server->allocations->map(function ($item) use ($server) { - return [ - 'ip' => $item->alias, - 'port' => $item->port, - 'default' => ($item->id === $server->allocation_id), - ]; - }), - 'sftp' => [ - 'username' => ($request->user()->can('view-sftp', $server)) ? $server->username : null, - ], - 'daemon' => [ - 'token' => $server->daemonSecret, - 'response' => $daemon, - ], - ]; + return $fractal->transformWith(new ServerTransformer) + ->withResourceName('server') + ->toArray(); } + /** + * Controller to handle request for server power toggle. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\Response + */ public function power(Request $request, $uuid) { + $this->authorize('user.server-power', $request->apiKey()); + $server = Server::byUuid($uuid); $request->user()->can('power-' . $request->input('action'), $server); - if (empty($request->input('action'))) { - return $this->response()->error([ - 'error' => 'An action must be passed to this request.', - ], 422); - } + $repo = new PowerRepository($server); + $repo->do($request->input('action')); - $res = $server->guzzleClient()->request('PUT', '/server/power', [ - 'exceptions' => false, - 'json' => [ - 'action' => $request->input('action'), - ], - ]); - - if ($res->getStatusCode() !== 204) { - return $this->response->error(json_decode($res->getBody())->error, $res->getStatusCode()); - } - - return $this->response->noContent(); + return response('', 204)->header('Content-Type', 'application/json'); } + /** + * Controller to handle base request for individual server information. + * + * @param \Illuminate\Http\Request $request + * @param string $uuid + * @return \Illuminate\Http\Response + */ public function command(Request $request, $uuid) { + $this->authorize('user.server-command', $request->apiKey()); + $server = Server::byUuid($uuid); $request->user()->can('send-command', $server); - if (empty($request->input('command'))) { - return $this->response()->error([ - 'error' => 'A command must be passed to this request.', - ], 422); - } + $repo = new CommandRepository($server); + $repo->send($request->input('command')); - $res = $server->guzzleClient()->request('POST', '/server/command', [ - 'exceptions' => false, - 'json' => [ - 'command' => $request->input('command'), - ], - ]); - - if ($res->getStatusCode() !== 204) { - return $this->response->error(json_decode($res->getBody())->error, $res->getStatusCode()); - } - - return $this->response->noContent(); + return response('', 204)->header('Content-Type', 'application/json'); } } diff --git a/app/Http/Controllers/API/UserController.php b/app/Http/Controllers/API/UserController.php deleted file mode 100755 index 981baef51..000000000 --- a/app/Http/Controllers/API/UserController.php +++ /dev/null @@ -1,151 +0,0 @@ -. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace Pterodactyl\Http\Controllers\API; - -use Illuminate\Http\Request; -use Pterodactyl\Models\User; -use Dingo\Api\Exception\ResourceException; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Repositories\UserRepository; -use Pterodactyl\Exceptions\DisplayValidationException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; - -class UserController extends BaseController -{ - /** - * Lists all users currently on the system. - * - * @param Request $request - * @return array - */ - public function index(Request $request) - { - return User::all()->toArray(); - } - - /** - * Lists specific fields about a user or all fields pertaining to that user. - * - * @param Request $request - * @param int $id - * @return array - */ - public function view(Request $request, $id) - { - $user = User::with('servers')->where((is_numeric($id) ? 'id' : 'email'), $id)->firstOrFail(); - - $user->servers->transform(function ($item) { - return collect($item)->only([ - 'id', 'node_id', 'uuidShort', - 'uuid', 'name', 'suspended', - 'owner_id', - ]); - }); - - if (! is_null($request->input('fields'))) { - $fields = explode(',', $request->input('fields')); - if (! empty($fields) && is_array($fields)) { - return collect($user)->only($fields); - } - } - - return $user->toArray(); - } - - /** - * Create a New User. - * - * @param Request $request - * @return array - */ - public function create(Request $request) - { - $repo = new UserRepository; - - try { - $user = $user->create($request->only([ - 'email', 'password', 'name_first', - 'name_last', 'username', 'root_admin', - ])); - - return ['id' => $user->id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to create a user on the system due to an error.'); - } - } - - /** - * Update an Existing User. - * - * @param Request $request - * @param int $id - * @return array - */ - public function update(Request $request, $id) - { - $repo = new UserRepository; - - try { - $user = $repo->update($id, $request->only([ - 'email', 'password', 'name_first', - 'name_last', 'username', 'root_admin', - ])); - - return ['id' => $id]; - } catch (DisplayValidationException $ex) { - throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true)); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to update a user on the system due to an error.'); - } - } - - /** - * Delete a User. - * - * @param Request $request - * @param int $id - * @return void - */ - public function delete(Request $request, $id) - { - $repo = new UserRepository; - - try { - $repo->delete($id); - - return $this->response->noContent(); - } catch (DisplayException $ex) { - throw new ResourceException($ex->getMessage()); - } catch (\Exception $ex) { - throw new ServiceUnavailableHttpException('Unable to delete this user due to an error.'); - } - } -} diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index 0c895fe15..1300b9643 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -60,7 +60,7 @@ class NodesController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse */ - public function new(Request $request) + public function create(Request $request) { $locations = Models\Location::all(); if (! $locations->count()) { @@ -78,7 +78,7 @@ class NodesController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { try { $repo = new NodeRepository; diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php index 4dfe34321..d4f383a92 100644 --- a/app/Http/Controllers/Admin/OptionController.php +++ b/app/Http/Controllers/Admin/OptionController.php @@ -44,7 +44,7 @@ class OptionController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { $services = Service::with('options')->get(); Javascript::put(['services' => $services->keyBy('id')]); @@ -58,7 +58,7 @@ class OptionController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Response\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { $repo = new OptionRepository; diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php index 14d248dc8..d02273672 100644 --- a/app/Http/Controllers/Admin/PackController.php +++ b/app/Http/Controllers/Admin/PackController.php @@ -60,7 +60,7 @@ class PackController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { return view('admin.packs.new', [ 'services' => Service::with('options')->get(), @@ -86,7 +86,7 @@ class PackController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function create(Request $request) + public function store(Request $request) { $repo = new PackRepository; diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 07cba84fd..85bc1a4db 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -63,7 +63,7 @@ class ServersController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { $services = Models\Service::with('options.packs', 'options.variables')->get(); Javascript::put([ @@ -86,7 +86,7 @@ class ServersController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Response\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { try { $repo = new ServerRepository; @@ -97,6 +97,9 @@ class ServersController extends Controller return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash(); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash(); @@ -111,7 +114,7 @@ class ServersController extends Controller * @param \Illuminate\Http\Request $request * @return array */ - public function newServerNodes(Request $request) + public function nodes(Request $request) { $nodes = Models\Node::with('allocations')->where('location_id', $request->input('location'))->get(); @@ -284,8 +287,9 @@ class ServersController extends Controller Alert::success('Successfully updated this server\'s docker image.')->flash(); } catch (DisplayValidationException $ex) { return redirect()->route('admin.servers.view.details', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + Alert::danger('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.'); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to update this server\'s docker image. This error has been logged.')->flash(); @@ -366,8 +370,9 @@ class ServersController extends Controller $repo->$action($id); Alert::success('Server has been ' . $action . 'ed.'); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash(); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to ' . $action . ' this server. This error has been logged.')->flash(); @@ -398,6 +403,9 @@ class ServersController extends Controller return redirect()->route('admin.servers.view.build', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); + } catch (TransferException $ex) { + Log::warning($ex); + Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash(); } catch (\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to add this server. This error has been logged.')->flash(); diff --git a/app/Http/Controllers/Admin/ServiceController.php b/app/Http/Controllers/Admin/ServiceController.php index 2e63e5306..beaac5340 100644 --- a/app/Http/Controllers/Admin/ServiceController.php +++ b/app/Http/Controllers/Admin/ServiceController.php @@ -54,7 +54,7 @@ class ServiceController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function new(Request $request) + public function create(Request $request) { return view('admin.services.new'); } @@ -91,7 +91,7 @@ class ServiceController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse */ - public function create(Request $request) + public function store(Request $request) { $repo = new ServiceRepository; diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 8439cc2ab..238f2e1e2 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -42,7 +42,7 @@ class UserController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function getIndex(Request $request) + public function index(Request $request) { $users = User::withCount('servers'); @@ -61,7 +61,7 @@ class UserController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ - public function getNew(Request $request) + public function create(Request $request) { return view('admin.users.new'); } @@ -73,7 +73,7 @@ class UserController extends Controller * @param int $id * @return \Illuminate\View\View */ - public function getView(Request $request, $id) + public function view(Request $request, $id) { return view('admin.users.view', [ 'user' => User::with('servers.node')->findOrFail($id), @@ -87,7 +87,7 @@ class UserController extends Controller * @param int $id * @return \Illuminate\Http\RedirectResponse */ - public function deleteUser(Request $request, $id) + public function delete(Request $request, $id) { try { $repo = new UserRepository; @@ -111,7 +111,7 @@ class UserController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse */ - public function postNew(Request $request) + public function store(Request $request) { try { $user = new UserRepository; @@ -136,26 +136,26 @@ class UserController extends Controller * Update a user. * * @param \Illuminate\Http\Request $request - * @param int $user + * @param int $id * @return \Illuminate\Http\RedirectResponse */ - public function updateUser(Request $request, $user) + public function update(Request $request, $id) { try { $repo = new UserRepository; - $repo->update($user, $request->only([ + $user = $repo->update($user, $request->intersect([ 'email', 'password', 'name_first', 'name_last', 'username', 'root_admin', ])); Alert::success('User account was successfully updated.')->flash(); } catch (DisplayValidationException $ex) { - return redirect()->route('admin.users.view', $user)->withErrors(json_decode($ex->getMessage())); - } catch (\Exception $e) { - Log::error($e); + return redirect()->route('admin.users.view', $id)->withErrors(json_decode($ex->getMessage())); + } catch (\Exception $ex) { + Log::error($ex); Alert::danger('An error occured while attempting to update this user.')->flash(); } - return redirect()->route('admin.users.view', $user); + return redirect()->route('admin.users.view', $id); } /** @@ -164,7 +164,7 @@ class UserController extends Controller * @param \Illuminate\Http\Request $request * @return \Pterodactyl\Models\User */ - public function getJson(Request $request) + public function json(Request $request) { return User::select('id', 'email', 'username', 'name_first', 'name_last') ->search($request->input('q')) diff --git a/app/Http/Controllers/Base/APIController.php b/app/Http/Controllers/Base/APIController.php index bc0ce7be4..9c3816db3 100644 --- a/app/Http/Controllers/Base/APIController.php +++ b/app/Http/Controllers/Base/APIController.php @@ -27,8 +27,9 @@ namespace Pterodactyl\Http\Controllers\Base; use Log; use Alert; -use Pterodactyl\Models; use Illuminate\Http\Request; +use Pterodactyl\Models\APIKey; +use Pterodactyl\Models\APIPermission; use Pterodactyl\Repositories\APIRepository; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; @@ -45,7 +46,7 @@ class APIController extends Controller public function index(Request $request) { return view('base.api.index', [ - 'keys' => Models\APIKey::where('user_id', $request->user()->id)->get(), + 'keys' => APIKey::where('user_id', $request->user()->id)->get(), ]); } @@ -57,7 +58,12 @@ class APIController extends Controller */ public function create(Request $request) { - return view('base.api.new'); + return view('base.api.new', [ + 'permissions' => [ + 'user' => collect(APIPermission::permissions())->pull('_user'), + 'admin' => collect(APIPermission::permissions())->except('_user')->toArray(), + ], + ]); } /** @@ -66,13 +72,13 @@ class APIController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse */ - public function save(Request $request) + public function store(Request $request) { try { $repo = new APIRepository($request->user()); $secret = $repo->create($request->intersect([ 'memo', 'allowed_ips', - 'adminPermissions', 'permissions', + 'admin_permissions', 'permissions', ])); Alert::success('An API Key-Pair has successfully been generated. The API secret for this public key is shown below and will not be shown again.

' . $secret . '')->flash(); diff --git a/app/Http/Controllers/Server/SubuserController.php b/app/Http/Controllers/Server/SubuserController.php index 3caba0e0e..c0c15f29d 100644 --- a/app/Http/Controllers/Server/SubuserController.php +++ b/app/Http/Controllers/Server/SubuserController.php @@ -43,7 +43,7 @@ class SubuserController extends Controller * @param string $uuid * @return \Illuminate\View\View */ - public function getIndex(Request $request, $uuid) + public function index(Request $request, $uuid) { $server = Models\Server::byUuid($uuid)->load('subusers.user'); $this->authorize('list-subusers', $server); @@ -65,7 +65,7 @@ class SubuserController extends Controller * @param int $id * @return \Illuminate\View\View */ - public function getView(Request $request, $uuid, $id) + public function view(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid)->load('node'); $this->authorize('view-subuser', $server); @@ -94,7 +94,7 @@ class SubuserController extends Controller * @param int $id * @return \Illuminate\Http\RedirectResponse */ - public function postView(Request $request, $uuid, $id) + public function update(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid); $this->authorize('edit-subuser', $server); @@ -139,7 +139,7 @@ class SubuserController extends Controller * @param string $uuid * @return \Illuminate\View\View */ - public function getNew(Request $request, $uuid) + public function create(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); $this->authorize('create-subuser', $server); @@ -159,7 +159,7 @@ class SubuserController extends Controller * @param string $uuid * @return \Illuminate\Http\RedirectResponse */ - public function postNew(Request $request, $uuid) + public function store(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); $this->authorize('create-subuser', $server); @@ -195,7 +195,7 @@ class SubuserController extends Controller * @param int $id * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response */ - public function deleteSubuser(Request $request, $uuid, $id) + public function delete(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid); $this->authorize('delete-subuser', $server); diff --git a/app/Http/Controllers/Server/TaskController.php b/app/Http/Controllers/Server/TaskController.php index 1ddc3eac2..1bd7f3244 100644 --- a/app/Http/Controllers/Server/TaskController.php +++ b/app/Http/Controllers/Server/TaskController.php @@ -42,7 +42,7 @@ class TaskController extends Controller * @param string $uuid * @return \Illuminate\View\View */ - public function getIndex(Request $request, $uuid) + public function index(Request $request, $uuid) { $server = Models\Server::byUuid($uuid)->load('tasks'); $this->authorize('list-tasks', $server); @@ -66,7 +66,7 @@ class TaskController extends Controller * @param string $uuid * @return \Illuminate\View\View */ - public function getNew(Request $request, $uuid) + public function create(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); $this->authorize('create-task', $server); @@ -85,7 +85,7 @@ class TaskController extends Controller * @param string $uuid * @return \Illuminate\Http\RedirectResponse */ - public function postNew(Request $request, $uuid) + public function store(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); $this->authorize('create-task', $server); @@ -117,7 +117,7 @@ class TaskController extends Controller * @param int $id * @return \Illuminate\Http\JsonResponse */ - public function deleteTask(Request $request, $uuid, $id) + public function delete(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid)->load('tasks'); $this->authorize('delete-task', $server); @@ -151,7 +151,7 @@ class TaskController extends Controller * @param int $id * @return \Illuminate\Http\JsonResponse */ - public function toggleTask(Request $request, $uuid, $id) + public function toggle(Request $request, $uuid, $id) { $server = Models\Server::byUuid($uuid)->load('tasks'); $this->authorize('toggle-task', $server); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 845e14aab..a70895a3e 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -39,6 +39,7 @@ class Kernel extends HttpKernel \Pterodactyl\Http\Middleware\LanguageMiddleware::class, ], 'api' => [ + \Pterodactyl\Http\Middleware\HMACAuthorization::class, 'throttle:60,1', 'bindings', ], diff --git a/app/Http/Middleware/APISecretToken.php b/app/Http/Middleware/APISecretToken.php deleted file mode 100755 index 25bf891ba..000000000 --- a/app/Http/Middleware/APISecretToken.php +++ /dev/null @@ -1,133 +0,0 @@ -. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace Pterodactyl\Http\Middleware; - -use Auth; -use Crypt; -use Config; -use IPTools\IP; -use IPTools\Range; -use Dingo\Api\Routing\Route; -use Illuminate\Http\Request; -use Pterodactyl\Models\User; -use Pterodactyl\Models\APIKey; -use Pterodactyl\Models\APIPermission; -use Pterodactyl\Services\APILogService; -use Dingo\Api\Auth\Provider\Authorization; -use Symfony\Component\HttpKernel\Exception\HttpException; // 400 -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 401 -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403 -use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; //500 - -class APISecretToken extends Authorization -{ - protected $algo = 'sha256'; - - protected $permissionAllowed = false; - - protected $url = ''; - - public function __construct() - { - Config::set('session.driver', 'array'); - } - - public function getAuthorizationMethod() - { - return 'Authorization'; - } - - public function authenticate(Request $request, Route $route) - { - if (! $request->bearerToken() || empty($request->bearerToken())) { - APILogService::log($request, 'The authentication header was missing or malformed.'); - throw new UnauthorizedHttpException('The authentication header was missing or malformed.'); - } - - list($public, $hashed) = explode('.', $request->bearerToken()); - - $key = APIKey::where('public', $public)->first(); - if (! $key) { - APILogService::log($request, 'Invalid API Key.'); - throw new AccessDeniedHttpException('Invalid API Key.'); - } - - // Check for Resource Permissions - if (! empty($request->route()->getName())) { - if (! is_null($key->allowed_ips)) { - $inRange = false; - foreach (json_decode($key->allowed_ips) as $ip) { - if (Range::parse($ip)->contains(new IP($request->ip()))) { - $inRange = true; - break; - } - } - if (! $inRange) { - APILogService::log($request, 'This IP address <' . $request->ip() . '> does not have permission to use this API key.'); - throw new AccessDeniedHttpException('This IP address <' . $request->ip() . '> does not have permission to use this API key.'); - } - } - - $permission = APIPermission::where('key_id', $key->id)->where('permission', $request->route()->getName()); - - // Suport Wildcards - if (starts_with($request->route()->getName(), 'api.user')) { - $permission->orWhere('permission', 'api.user.*'); - } elseif (starts_with($request->route()->getName(), 'api.admin')) { - $permission->orWhere('permission', 'api.admin.*'); - } - - if (! $permission->first()) { - APILogService::log($request, 'You do not have permission to access this resource. This API Key requires the ' . $request->route()->getName() . ' permission node.'); - throw new AccessDeniedHttpException('You do not have permission to access this resource. This API Key requires the ' . $request->route()->getName() . ' permission node.'); - } - } - - try { - $decrypted = Crypt::decrypt($key->secret); - } catch (\Illuminate\Contracts\Encryption\DecryptException $ex) { - APILogService::log($request, 'There was an error while attempting to check your secret key.'); - throw new HttpException('There was an error while attempting to check your secret key.'); - } - - $this->url = urldecode($request->fullUrl()); - if ($this->_generateHMAC($request->getContent(), $decrypted) !== base64_decode($hashed)) { - APILogService::log($request, 'The hashed body was not valid. Potential modification of contents in route.'); - throw new BadRequestHttpException('The hashed body was not valid. Potential modification of contents in route.'); - } - - // Log the Route Access - APILogService::log($request, null, true); - - return Auth::loginUsingId($key->user_id); - } - - protected function _generateHMAC($body, $key) - { - $data = $this->url . $body; - - return hash_hmac($this->algo, $data, $key, true); - } -} diff --git a/app/Http/Middleware/CheckServer.php b/app/Http/Middleware/CheckServer.php index 33f817295..4cfe08191 100644 --- a/app/Http/Middleware/CheckServer.php +++ b/app/Http/Middleware/CheckServer.php @@ -28,9 +28,26 @@ use Auth; use Closure; use Illuminate\Http\Request; use Pterodactyl\Models\Server; +use Illuminate\Auth\AuthenticationException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; class CheckServer { + /** + * The elquent model for the server. + * + * @var \Pterodactyl\Models\Server + */ + protected $server; + + /** + * The request object. + * + * @var \Illuminate\Http\Request + */ + protected $request; + /** * Handle an incoming request. * @@ -41,22 +58,72 @@ class CheckServer public function handle(Request $request, Closure $next) { if (! Auth::user()) { - return redirect()->guest('auth/login'); + throw new AuthenticationException(); } - $server = Server::byUuid($request->route()->server); - if (! $server) { + $this->request = $request; + $this->server = Server::byUuid($request->route()->server); + + if (! $this->exists()) { return response()->view('errors.404', [], 404); } - if ($server->suspended) { + if ($this->suspended()) { return response()->view('errors.suspended', [], 403); } - if (! $server->installed) { + if (! $this->installed()) { return response()->view('errors.installing', [], 403); } return $next($request); } + + /** + * Determine if the server was found on the system. + * + * @return bool + */ + protected function exists() + { + if (! $this->server) { + if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) { + throw new NotFoundHttpException('The requested server was not found on the system.'); + } + } + + return (! $this->server) ? false : true; + } + + /** + * Determine if the server is suspended. + * + * @return bool + */ + protected function suspended() + { + if ($this->server->suspended) { + if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) { + throw new AccessDeniedHttpException('Server is suspended.'); + } + } + + return $this->server->suspended; + } + + /** + * Determine if the server is installed. + * + * @return bool + */ + protected function installed() + { + if ($this->server->installed !== 1) { + if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) { + throw new AccessDeniedHttpException('Server is completing install process.'); + } + } + + return $this->server->installed === 1; + } } diff --git a/app/Http/Middleware/HMACAuthorization.php b/app/Http/Middleware/HMACAuthorization.php new file mode 100644 index 000000000..2c08c1449 --- /dev/null +++ b/app/Http/Middleware/HMACAuthorization.php @@ -0,0 +1,229 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Http\Middleware; + +use Auth; +use Crypt; +use Config; +use Closure; +use Debugbar; +use IPTools\IP; +use IPTools\Range; +use Illuminate\Http\Request; +use Pterodactyl\Models\APIKey; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 400 +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403 + +class HMACAuthorization +{ + /** + * The algorithm to use for handling HMAC encryption. + * + * @var string + */ + const HMAC_ALGORITHM = 'sha256'; + + /** + * Stored values from the Authorization header. + * + * @var array + */ + protected $token = []; + + /** + * The eloquent model for the API key. + * + * @var \Pterodactyl\Models\APIKey + */ + protected $key; + + /** + * The illuminate request object. + * + * @var \Illuminate\Http\Request + */ + private $request; + + /** + * Construct class instance. + * + * @return void + */ + public function __construct() + { + Debugbar::disable(); + Config::set('session.driver', 'array'); + } + + /** + * Handle an incoming request for the API. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle(Request $request, Closure $next) + { + $this->request = $request; + + $this->checkBearer(); + $this->validateRequest(); + $this->validateIPAccess(); + $this->validateContents(); + + Auth::loginUsingId($this->key()->user_id); + + return $next($request); + } + + /** + * Checks that the Bearer token is provided and in a valid format. + * + * @return void + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + protected function checkBearer() + { + if (empty($this->request()->bearerToken())) { + throw new BadRequestHttpException('Request was missing required Authorization header.'); + } + + $token = explode('.', $this->request()->bearerToken()); + if (count($token) !== 2) { + throw new BadRequestHttpException('The Authorization header passed was not in a validate public/private key format.'); + } + + $this->token = [ + 'public' => $token[0], + 'hash' => $token[1], + ]; + } + + /** + * Determine if the request contains a valid public API key + * as well as permissions for the resource. + * + * @return void + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + protected function validateRequest() + { + $this->key = APIKey::where('public', $this->public())->first(); + if (! $this->key) { + throw new AccessDeniedHttpException('Unable to identify requester. Authorization token is invalid.'); + } + } + + /** + * Determine if the requesting IP address is allowed to use this API key. + * + * @return bool + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + protected function validateIPAccess() + { + if (! is_null($this->key()->allowed_ips)) { + foreach ($this->key()->allowed_ips as $ip) { + if (Range::parse($ip)->contains(new IP($this->request()->ip()))) { + return true; + } + } + + throw new AccessDeniedHttpException('This IP address does not have permission to access the API using these credentials.'); + } + + return true; + } + + /** + * Determine if the HMAC sent in the request matches the one generated + * on the panel side. + * + * @return void + * + * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + */ + protected function validateContents() + { + if (base64_decode($this->hash()) !== $this->generateSignature()) { + throw new BadRequestHttpException('The HMAC for the request was invalid.'); + } + } + + /** + * Generate a HMAC from the request and known API secret key. + * + * @return string + */ + protected function generateSignature() + { + $content = urldecode($this->request()->fullUrl()) . $this->request()->getContent(); + + return hash_hmac(self::HMAC_ALGORITHM, $content, Crypt::decrypt($this->key()->secret), true); + } + + /** + * Return the public key passed in the Authorization header. + * + * @return string + */ + protected function public() + { + return $this->token['public']; + } + + /** + * Return the base64'd HMAC sent in the Authorization header. + * + * @return string + */ + protected function hash() + { + return $this->token['hash']; + } + + /** + * Return the API Key model. + * + * @return \Pterodactyl\Models\APIKey + */ + protected function key() + { + return $this->key; + } + + /** + * Return the Illuminate Request object. + * + * @return \Illuminate\Http\Request + */ + private function request() + { + return $this->request; + } +} diff --git a/app/Http/Routes/ServerRoutes.php b/app/Http/Routes/ServerRoutes.php deleted file mode 100644 index b03a530d6..000000000 --- a/app/Http/Routes/ServerRoutes.php +++ /dev/null @@ -1,192 +0,0 @@ -. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace Pterodactyl\Http\Routes; - -use Illuminate\Routing\Router; - -class ServerRoutes -{ - /** - * Server routes. - * - * @param \Illuminate\Routing\Router $router - * @return void - */ - public function map(Router $router) - { - // Returns Server Status - $router->get('/server/{server}/ajax/status', [ - 'middleware' => ['auth', 'csrf'], - 'as' => 'server.ajax.status', - 'uses' => 'Server\AjaxController@getStatus', - ]); - - $router->group([ - 'prefix' => 'server/{server}', - 'middleware' => [ - 'auth', - 'server', - 'csrf', - ], - ], function ($server) use ($router) { - - // Index View for Server - $router->get('/', [ - 'as' => 'server.index', - 'uses' => 'Server\ServerController@getIndex', - ]); - - $router->get('/settings/databases', [ - 'as' => 'server.settings.databases', - 'uses' => 'Server\ServerController@getDatabases', - ]); - - $router->get('/settings/sftp', [ - 'as' => 'server.settings.sftp', - 'uses' => 'Server\ServerController@getSFTP', - ]); - - $router->post('/settings/sftp', [ - 'uses' => 'Server\ServerController@postSettingsSFTP', - ]); - - $router->get('/settings/startup', [ - 'as' => 'server.settings.startup', - 'uses' => 'Server\ServerController@getStartup', - ]); - - $router->post('/settings/startup', [ - 'uses' => 'Server\ServerController@postSettingsStartup', - ]); - - $router->get('/settings/allocation', [ - 'as' => 'server.settings.allocation', - 'uses' => 'Server\ServerController@getAllocation', - ]); - - // File Manager Routes - $router->get('/files', [ - 'as' => 'server.files.index', - 'uses' => 'Server\ServerController@getFiles', - ]); - - $router->get('/files/edit/{file}', [ - 'as' => 'server.files.edit', - 'uses' => 'Server\ServerController@getEditFile', - ])->where('file', '.*'); - - $router->get('/files/download/{file}', [ - 'as' => 'server.files.download', - 'uses' => 'Server\ServerController@getDownloadFile', - ])->where('file', '.*'); - - $router->get('/files/add', [ - 'as' => 'server.files.add', - 'uses' => 'Server\ServerController@getAddFile', - ]); - - $router->post('files/directory-list', [ - 'as' => 'server.files.directory-list', - 'uses' => 'Server\AjaxController@postDirectoryList', - ]); - - $router->post('files/save', [ - 'as' => 'server.files.save', - 'uses' => 'Server\AjaxController@postSaveFile', - ]); - - // Sub-User Routes - $router->get('users', [ - 'as' => 'server.subusers', - 'uses' => 'Server\SubuserController@getIndex', - ]); - - $router->get('users/new', [ - 'as' => 'server.subusers.new', - 'uses' => 'Server\SubuserController@getNew', - ]); - - $router->post('users/new', [ - 'uses' => 'Server\SubuserController@postNew', - ]); - - $router->get('users/view/{id}', [ - 'as' => 'server.subusers.view', - 'uses' => 'Server\SubuserController@getView', - ]); - - $router->post('users/view/{id}', [ - 'uses' => 'Server\SubuserController@postView', - ]); - - $router->delete('users/delete/{id}', [ - 'as' => 'server.subusers.delete', - 'uses' => 'Server\SubuserController@deleteSubuser', - ]); - - $router->get('tasks/', [ - 'as' => 'server.tasks', - 'uses' => 'Server\TaskController@getIndex', - ]); - - $router->get('tasks/view/{id}', [ - 'as' => 'server.tasks.view', - 'uses' => 'Server\TaskController@getView', - ]); - - $router->get('tasks/new', [ - 'as' => 'server.tasks.new', - 'uses' => 'Server\TaskController@getNew', - ]); - - $router->post('tasks/new', [ - 'uses' => 'Server\TaskController@postNew', - ]); - - $router->delete('tasks/delete/{id}', [ - 'as' => 'server.tasks.delete', - 'uses' => 'Server\TaskController@deleteTask', - ]); - - $router->post('tasks/toggle/{id}', [ - 'as' => 'server.tasks.toggle', - 'uses' => 'Server\TaskController@toggleTask', - ]); - - // Assorted AJAX Routes - $router->group(['prefix' => 'ajax'], function ($server) use ($router) { - // Sets the Default Connection for the Server - $router->post('set-primary', [ - 'uses' => 'Server\AjaxController@postSetPrimary', - ]); - - $router->post('settings/reset-database-password', [ - 'as' => 'server.ajax.reset-database-password', - 'uses' => 'Server\AjaxController@postResetDatabasePassword', - ]); - }); - }); - } -} diff --git a/app/Models/APIKey.php b/app/Models/APIKey.php index 68e481712..6ed73b7c2 100644 --- a/app/Models/APIKey.php +++ b/app/Models/APIKey.php @@ -28,6 +28,13 @@ use Illuminate\Database\Eloquent\Model; class APIKey extends Model { + /** + * Public key defined length used in verification methods. + * + * @var int + */ + const PUBLIC_KEY_LEN = 16; + /** * The table associated with the model. * @@ -42,6 +49,15 @@ class APIKey extends Model */ protected $hidden = ['secret']; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'allowed_ips' => 'json', + ]; + /** * Fields that are not mass assignable. * diff --git a/app/Models/APIPermission.php b/app/Models/APIPermission.php index 90e7ff16e..bfe2fc908 100644 --- a/app/Models/APIPermission.php +++ b/app/Models/APIPermission.php @@ -57,4 +57,75 @@ class APIPermission extends Model * @var bool */ public $timestamps = false; + + /** + * List of permissions available for the API. + * + * @var array + */ + protected static $permissions = [ + // Items within this block are available to non-adminitrative users. + '_user' => [ + 'server' => [ + 'list', + 'view', + 'power', + 'command', + ], + ], + + // All other pemissions below are administrative actions. + 'server' => [ + 'list', + 'create', + 'view', + 'edit-details', + 'edit-container', + 'edit-build', + 'edit-startup', + 'suspend', + 'install', + 'rebuild', + 'delete', + ], + 'location' => [ + 'list', + ], + 'node' => [ + 'list', + 'view', + 'view-config', + 'create', + 'delete', + ], + 'user' => [ + 'list', + 'view', + 'create', + 'edit', + 'delete', + ], + 'service' => [ + 'list', + 'view', + ], + 'option' => [ + 'list', + 'view', + ], + 'pack' => [ + 'list', + 'view', + ], + ]; + + /** + * Return permissions for API. + * + * @return array + */ + public static function permissions() + { + return self::$permissions; + } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 633c1a542..8a3725ed6 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -27,6 +27,7 @@ namespace Pterodactyl\Models; use Auth; use Cache; use Carbon; +use Schema; use Javascript; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; @@ -203,6 +204,16 @@ class Server extends Model return Javascript::put($response); } + /** + * Return the columns available for this table. + * + * @return array + */ + public function getTableColumns() + { + return Schema::getColumnListing($this->getTable()); + } + /** * Gets the user who owns the server. * @@ -313,4 +324,14 @@ class Server extends Model { return $this->hasMany(Database::class); } + + /** + * Gets the location of the server. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function location() + { + return $this->node->location(); + } } diff --git a/app/Policies/APIKeyPolicy.php b/app/Policies/APIKeyPolicy.php new file mode 100644 index 000000000..58b187b48 --- /dev/null +++ b/app/Policies/APIKeyPolicy.php @@ -0,0 +1,73 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Policies; + +use Cache; +use Carbon; +use Pterodactyl\Models\User; +use Pterodactyl\Models\APIKey as Key; +use Pterodactyl\Models\APIPermission as Permission; + +class APIKeyPolicy +{ + /** + * Checks if the API key has permission to perform an action. + * + * @param \Pterodactyl\Models\User $user + * @param \Pterodactyl\Models\APIKey $key + * @param string $permission + * @return bool + */ + protected function checkPermission(User $user, Key $key, $permission) + { + // Non-administrative users cannot use administrative routes. + if (! starts_with('user.') && ! $user->isRootAdmin()) { + return false; + } + + // We don't tag this cache key with the user uuid because the key is already unique, + // and multiple users are not defiend for a single key. + $permissions = Cache::remember('APIKeyPolicy.' . $key->public, Carbon::now()->addSeconds(5), function () use ($key) { + return $key->permissions()->get()->transform(function ($item) { + return $item->permission; + })->values(); + }); + + return $permissions->search($permission, true) !== false; + } + + /** + * Determine if a user has permission to perform this action against the system. + * + * @param \Pterodactyl\Models\User $user + * @param string $permission + * @param \Pterodactyl\Models\APIKey $key + * @return bool + */ + public function before(User $user, $permission, Key $key) + { + return $this->checkPermission($user, $key, $permission); + } +} diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index f375afecc..56cd359e1 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -39,13 +39,9 @@ class ServerPolicy * @param string $permission * @return bool */ - private function checkPermission(User $user, Server $server, $permission) + protected function checkPermission(User $user, Server $server, $permission) { - if ($this->isOwner($user, $server)) { - return true; - } - - $permissions = Cache::remember('ServerPolicy.' . $user->uuid . $server->uuid, Carbon::now()->addSeconds(10), function () use ($user, $server) { + $permissions = Cache::remember('ServerPolicy.' . $user->uuid . $server->uuid, Carbon::now()->addSeconds(5), function () use ($user, $server) { return $user->permissions()->server($server)->get()->transform(function ($item) { return $item->permission; })->values(); @@ -54,486 +50,20 @@ class ServerPolicy return $permissions->search($permission, true) !== false; } - /** - * Determine if current user is the owner of a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - protected function isOwner(User $user, Server $server) - { - return $server->owner_id === $user->id; - } - /** * Runs before any of the functions are called. Used to determine if user is root admin, if so, ignore permissions. * - * @param \Pterodactyl\Models\User $user - * @param string $ability + * @param \Pterodactyl\Models\User $user + * @param string $ability + * @param \Pterodactyl\Models\Server $server * @return bool */ - public function before(User $user, $ability) + public function before(User $user, $ability, Server $server) { - if ($user->root_admin === 1) { + if ($user->isRootAdmin() || $server->owner_id === $user->id) { return true; } - } - /** - * Check if user has permission to control power for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function power(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power'); - } - - /** - * Check if user has permission to start a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerStart(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-start'); - } - - /** - * Check if user has permission to stop a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerStop(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-stop'); - } - - /** - * Check if user has permission to restart a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerRestart(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-restart'); - } - - /** - * Check if user has permission to kill a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function powerKill(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'power-kill'); - } - - /** - * Check if user has permission to run a command on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function sendCommand(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'send-command'); - } - - /** - * Check if user has permission to list files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function listFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'list-files'); - } - - /** - * Check if user has permission to edit files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function editFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'edit-files'); - } - - /** - * Check if user has permission to save files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function saveFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'save-files'); - } - - /** - * Check if user has permission to move and rename files and folders on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function moveFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'move-files'); - } - - /** - * Check if user has permission to copy folders and files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function copyFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'copy-files'); - } - - /** - * Check if user has permission to compress files and folders on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function compressFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'compress-files'); - } - - /** - * Check if user has permission to decompress files on a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function decompressFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'decompress-files'); - } - - /** - * Check if user has permission to add files to a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function createFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'create-files'); - } - - /** - * Check if user has permission to upload files to a server. - * This permission relies on the user having the 'create-files' permission as well due to page authorization. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function uploadFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'upload-files'); - } - - /** - * Check if user has permission to download files from a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function downloadFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'download-files'); - } - - /** - * Check if user has permission to delete files from a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function deleteFiles(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'delete-files'); - } - - /** - * Check if user has permission to view subusers for the server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function listSubusers(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'list-subusers'); - } - - /** - * Check if user has permission to view specific subuser permissions. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-subuser'); - } - - /** - * Check if user has permission to edit a subuser. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function editSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'edit-subuser'); - } - - /** - * Check if user has permission to delete a subuser. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function deleteSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'delete-subuser'); - } - - /** - * Check if user has permission to edit a subuser. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function createSubuser(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'create-subuser'); - } - - /** - * Check if user has permission to set the default connection for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function setConnection(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'set-connection'); - } - - /** - * Check if user has permission to view the startup command used for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewStartup(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-startup'); - } - - /** - * Check if user has permission to edit the startup command used for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function editStartup(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'edit-startup'); - } - - /** - * Check if user has permission to view the SFTP information for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewSftp(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-sftp'); - } - - /** - * Check if user has permission to reset the SFTP password for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function resetSftp(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'reset-sftp'); - } - - /** - * Check if user has permission to view the SFTP password for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewSftpPassword(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-sftp-password'); - } - - /** - * Check if user has permission to view databases for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewDatabases(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-databases'); - } - - /** - * Check if user has permission to reset database passwords. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function resetDbPassword(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'reset-db-password'); - } - - /** - * Check if user has permission to view all tasks for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function listTasks(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'list-tasks'); - } - - /** - * Check if user has permission to view a specific task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-task'); - } - - /** - * Check if user has permission to view a toggle a task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function toggleTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'toggle-task'); - } - - /** - * Check if user has permission to queue a task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function queueTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'queue-task'); - } - - /** - * Check if user has permission to delete a specific task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function deleteTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'delete-task'); - } - - /** - * Check if user has permission to create a task for a server. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function createTask(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'create-task'); - } - - /** - * Check if user has permission to view server allocations. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function viewAllocation(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'view-allocation'); - } - - /** - * Check if user has permission to set the default allocation. - * - * @param \Pterodactyl\Models\User $user - * @param \Pterodactyl\Models\Server $server - * @return bool - */ - public function setAllocation(User $user, Server $server) - { - return $this->checkPermission($user, $server, 'set-allocation'); + return $this->checkPermission($user, $server, $ability); } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 674d7600a..e1401e844 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -13,6 +13,7 @@ class AuthServiceProvider extends ServiceProvider */ protected $policies = [ 'Pterodactyl\Models\Server' => 'Pterodactyl\Policies\ServerPolicy', + 'Pterodactyl\Models\APIKey' => 'Pterodactyl\Policies\APIKeyPolicy', ]; /** diff --git a/app/Providers/MacroServiceProvider.php b/app/Providers/MacroServiceProvider.php index ad23aaf74..8dd08f73b 100644 --- a/app/Providers/MacroServiceProvider.php +++ b/app/Providers/MacroServiceProvider.php @@ -25,6 +25,10 @@ namespace Pterodactyl\Providers; use File; +use Cache; +use Carbon; +use Request; +use Pterodactyl\Models\APIKey; use Illuminate\Support\ServiceProvider; class MacroServiceProvider extends ServiceProvider @@ -48,5 +52,35 @@ class MacroServiceProvider extends ServiceProvider return round($size, ($i < 2) ? 0 : $precision) . ' ' . $units[$i]; }); + + Request::macro('apiKey', function () { + if (! Request::bearerToken()) { + return false; + } + + $parts = explode('.', Request::bearerToken()); + + if (count($parts) === 2 && strlen($parts[0]) === APIKey::PUBLIC_KEY_LEN) { + // Because the key itself isn't changing frequently, we simply cache this for + // 15 minutes to speed up the API and keep requests flowing. + return Cache::tags([ + 'ApiKeyMacro', + 'ApiKeyMacro:Key:' . $parts[0], + ])->remember('ApiKeyMacro.' . $parts[0], Carbon::now()->addMinutes(15), function () use ($parts) { + return APIKey::where('public', $parts[0])->first(); + }); + } + + return false; + }); + + Request::macro('apiKeyHasPermission', function ($permission) { + $key = Request::apiKey(); + if (! $key) { + return false; + } + + return Request::user()->can($permission, $key); + }); } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 0aa015828..d129489af 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -33,6 +33,14 @@ class RouteServiceProvider extends ServiceProvider */ public function map() { + Route::middleware(['api'])->prefix('/api/user') + ->namespace($this->namespace . '\API\User') + ->group(base_path('routes/api.php')); + + Route::middleware(['api'])->prefix('/api/admin') + ->namespace($this->namespace . '\API\Admin') + ->group(base_path('routes/api-admin.php')); + Route::middleware(['web', 'auth', 'csrf']) ->namespace($this->namespace . '\Base') ->group(base_path('routes/base.php')); diff --git a/app/Repositories/APIRepository.php b/app/Repositories/APIRepository.php index ff2c15c87..db9b9d6b7 100644 --- a/app/Repositories/APIRepository.php +++ b/app/Repositories/APIRepository.php @@ -29,65 +29,14 @@ use Auth; use Crypt; use Validator; use IPTools\Network; -use Pterodactyl\Models; +use Pterodactyl\Models\User; +use Pterodactyl\Models\APIKey as Key; use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Models\APIPermission as Permission; use Pterodactyl\Exceptions\DisplayValidationException; class APIRepository { - /** - * Valid API permissions. - * - * @var array - */ - protected $permissions = [ - 'admin' => [ - '*', - - // User Management Routes - 'users.list', - 'users.create', - 'users.view', - 'users.update', - 'users.delete', - - // Server Manaement Routes - 'servers.list', - 'servers.create', - 'servers.view', - 'servers.config', - 'servers.build', - 'servers.suspend', - 'servers.unsuspend', - 'servers.delete', - - // Node Management Routes - 'nodes.list', - 'nodes.view', - 'nodes.create', - 'nodes.allocations', - 'nodes.delete', - - // Service Routes - 'services.list', - 'services.view', - - // Location Routes - 'locations.list', - - ], - 'user' => [ - '*', - - // Informational - 'me', - - // Server Control - 'server', - 'server.power', - ], - ]; - /** * Holder for listing of allowed IPs when creating a new key. * @@ -108,11 +57,11 @@ class APIRepository * @param null|\Pterodactyl\Models\User $user * @return void */ - public function __construct(Models\User $user = null) + public function __construct(User $user = null) { $this->user = is_null($user) ? Auth::user() : $user; if (is_null($this->user)) { - throw new \Exception('Cannot access API Repository without passing a user to constructor.'); + throw new \Exception('Unable to initialize user for API repository instance.'); } } @@ -129,8 +78,9 @@ class APIRepository { $validator = Validator::make($data, [ 'memo' => 'string|max:500', + 'allowed_ips' => 'sometimes|string', 'permissions' => 'sometimes|required|array', - 'adminPermissions' => 'sometimes|required|array', + 'admin_permissions' => 'sometimes|required|array', ]); $validator->after(function ($validator) use ($data) { @@ -156,8 +106,7 @@ class APIRepository DB::beginTransaction(); try { $secretKey = str_random(16) . '.' . str_random(7) . '.' . str_random(7); - $key = new Models\APIKey; - $key->fill([ + $key = Key::create([ 'user_id' => $this->user->id, 'public' => str_random(16), 'secret' => Crypt::encrypt($secretKey), @@ -165,44 +114,61 @@ class APIRepository 'memo' => $data['memo'], 'expires_at' => null, ]); - $key->save(); $totalPermissions = 0; + $pNodes = Permission::permissions(); + if (isset($data['permissions'])) { - foreach ($data['permissions'] as $permNode) { - if (! strpos($permNode, ':')) { + foreach ($data['permissions'] as $permission) { + $parts = explode('-', $permission); + + if (count($parts) !== 2) { continue; } - list($toss, $permission) = explode(':', $permNode); - if (in_array($permission, $this->permissions['user'])) { - $totalPermissions++; - $model = new Models\APIPermission; - $model->fill([ - 'key_id' => $key->id, - 'permission' => 'api.user.' . $permission, - ]); - $model->save(); + list($block, $search) = $parts; + + if (! array_key_exists($block, $pNodes['_user'])) { + continue; } + + if (! in_array($search, $pNodes['_user'][$block])) { + continue; + } + + $totalPermissions++; + Permission::create([ + 'key_id' => $key->id, + 'permission' => 'user.' . $permission, + ]); } } - if ($this->user->isRootAdmin() && isset($data['adminPermissions'])) { - foreach ($data['adminPermissions'] as $permNode) { - if (! strpos($permNode, ':')) { + if ($this->user->isRootAdmin() && isset($data['admin_permissions'])) { + unset($pNodes['_user']); + + foreach ($data['admin_permissions'] as $permNode) { + $parts = explode('-', $permission); + + if (count($parts) !== 2) { continue; } - list($toss, $permission) = explode(':', $permNode); - if (in_array($permission, $this->permissions['admin'])) { - $totalPermissions++; - $model = new Models\APIPermission; - $model->fill([ - 'key_id' => $key->id, - 'permission' => 'api.admin.' . $permission, - ]); - $model->save(); + list($block, $search) = $parts; + + if (! array_key_exists($block, $pNodes)) { + continue; } + + if (! in_array($search, $pNodes[$block])) { + continue; + } + + $totalPermissions++; + Permission::create([ + 'key_id' => $key->id, + 'permission' => $permission, + ]); } } @@ -229,20 +195,13 @@ class APIRepository */ public function revoke($key) { - DB::beginTransaction(); - - try { - $model = Models\APIKey::with('permissions')->where('public', $key)->where('user_id', $this->user->id)->firstOrFail(); + DB::transaction(function () use ($key) { + $model = Key::with('permissions')->where('public', $key)->where('user_id', $this->user->id)->firstOrFail(); foreach ($model->permissions as &$permission) { $permission->delete(); } $model->delete(); - - DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } + }); } } diff --git a/app/Repositories/Daemon/CommandRepository.php b/app/Repositories/Daemon/CommandRepository.php index 51afc6105..b7149b453 100644 --- a/app/Repositories/Daemon/CommandRepository.php +++ b/app/Repositories/Daemon/CommandRepository.php @@ -25,6 +25,7 @@ namespace Pterodactyl\Repositories\Daemon; use Pterodactyl\Models; +use GuzzleHttp\Exception\ConnectException; use Pterodactyl\Exceptions\DisplayException; class CommandRepository @@ -60,20 +61,20 @@ class CommandRepository { // We don't use the user's specific daemon secret here since we // are assuming that a call to this function has been validated. - // Additionally not all calls to this will be from a logged in user. - // (e.g. task queue or API) try { - $response = $this->server->node->guzzleClient([ - 'X-Access-Token' => $this->server->daemonSecret, - 'X-Access-Server' => $this->server->uuid, - ])->request('POST', '/server/command', ['json' => ['command' => $command]]); + $response = $this->server->guzzleClient()->request('PUT', '/server/command', [ + 'http_errors' => false, + 'json' => [ + 'command' => $command, + ], + ]); if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { - throw new DisplayException('Command sending responded with a non-200 error code.'); + throw new DisplayException('Command sending responded with a non-200 error code (HTTP/' . $response->getStatusCode() . ').'); } return $response->getBody(); - } catch (\Exception $ex) { + } catch (ConnectException $ex) { throw $ex; } } diff --git a/app/Repositories/Daemon/PowerRepository.php b/app/Repositories/Daemon/PowerRepository.php index e4b4cbb18..bc5696986 100644 --- a/app/Repositories/Daemon/PowerRepository.php +++ b/app/Repositories/Daemon/PowerRepository.php @@ -25,6 +25,7 @@ namespace Pterodactyl\Repositories\Daemon; use Pterodactyl\Models; +use GuzzleHttp\Exception\ConnectException; use Pterodactyl\Exceptions\DisplayException; class PowerRepository @@ -60,20 +61,20 @@ class PowerRepository { // We don't use the user's specific daemon secret here since we // are assuming that a call to this function has been validated. - // Additionally not all calls to this will be from a logged in user. - // (e.g. task queue or API) try { - $response = $this->server->node->guzzleClient([ - 'X-Access-Token' => $this->server->daemonSecret, - 'X-Access-Server' => $this->server->uuid, - ])->request('PUT', '/server/power', ['json' => ['action' => $action]]); + $response = $this->server->guzzleClient()->request('PUT', '/server/power', [ + 'http_errors' => false, + 'json' => [ + 'action' => $action, + ], + ]); if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { - throw new DisplayException('Power status responded with a non-200 error code.'); + throw new DisplayException('Power toggle endpoint responded with a non-200 error code (HTTP/' . $response->getStatusCode() . ').'); } return $response->getBody(); - } catch (\Exception $ex) { + } catch (ConnectException $ex) { throw $ex; } } diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index b4dcd1c81..28ca9a22b 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -337,9 +337,6 @@ class ServerRepository DB::commit(); return $server; - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('There was an error while attempting to connect to the daemon to add this server.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; @@ -351,7 +348,7 @@ class ServerRepository * * @param int $id * @param array $data - * @return bool + * @return \Pterodactyl\Models\Server * * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayValidationException @@ -409,7 +406,9 @@ class ServerRepository ]); if ($res->getStatusCode() === 204) { - return DB::commit(); + DB::commit(); + + return $server; } else { throw new DisplayException('Daemon returned a a non HTTP/204 error code. HTTP/' + $res->getStatusCode()); } @@ -424,9 +423,8 @@ class ServerRepository * * @param int $id * @param array $data - * @return bool + * @return \Pterodactyl\Models\Server * - * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function updateContainer($id, array $data) @@ -461,10 +459,7 @@ class ServerRepository DB::commit(); - return true; - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.', $ex); + return $server; } catch (\Exception $ex) { DB::rollBack(); throw $ex; @@ -609,9 +604,6 @@ class ServerRepository DB::commit(); return $server; - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('A TransferException occured while attempting to update the server configuration, check that the daemon is online. This error has been logged.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; @@ -799,8 +791,6 @@ class ServerRepository * @param int $id * @param bool $deleted * @return void - * - * @throws \Pterodactyl\Exceptions\DisplayException */ public function suspend($id, $deleted = false) { @@ -824,9 +814,6 @@ class ServerRepository ])->request('POST', '/server/suspend'); return DB::commit(); - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('An error occured while attempting to contact the remote daemon to suspend this server.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; @@ -838,8 +825,6 @@ class ServerRepository * * @param int $id * @return void - * - * @throws \Pterodactyl\Exceptions\DisplayException */ public function unsuspend($id) { @@ -848,7 +833,6 @@ class ServerRepository DB::beginTransaction(); try { - // Already unsuspended, no need to make more requests. if ($server->suspended === 0) { return true; @@ -863,9 +847,6 @@ class ServerRepository ])->request('POST', '/server/unsuspend'); return DB::commit(); - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('An error occured while attempting to contact the remote daemon to un-suspend this server.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; @@ -879,7 +860,6 @@ class ServerRepository * @param string $password * @return void * - * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function updateSFTPPassword($id, $password) @@ -910,9 +890,6 @@ class ServerRepository DB::commit(); return true; - } catch (TransferException $ex) { - DB::rollBack(); - throw new DisplayException('There was an error while attmping to contact the remote service to change the password.', $ex); } catch (\Exception $ex) { DB::rollBack(); throw $ex; diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 5619f26d5..b2ddaeadd 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -114,7 +114,7 @@ class UserRepository * * @param int $id * @param array $data - * @return bool + * @return \Pterodactyl\Models\User * * @throws \Pterodactyl\Exceptions\DisplayValidationException */ @@ -147,9 +147,9 @@ class UserRepository unset($data['password']); } - $user->fill($data); + $user->fill($data)->save(); - return $user->save(); + return $user; } /** diff --git a/app/Transformers/Admin/AllocationTransformer.php b/app/Transformers/Admin/AllocationTransformer.php new file mode 100644 index 000000000..e7bd15c36 --- /dev/null +++ b/app/Transformers/Admin/AllocationTransformer.php @@ -0,0 +1,98 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Allocation; +use League\Fractal\TransformerAbstract; + +class AllocationTransformer extends TransformerAbstract +{ + /** + * The filter to be applied to this transformer. + * + * @var bool|string + */ + protected $filter; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @param bool $filter + * @return void + */ + public function __construct($request = false, $filter = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + $this->filter = $filter; + } + + /** + * Return a generic transformed allocation array. + * + * @return array + */ + public function transform(Allocation $allocation) + { + return $this->transformWithFilter($allocation); + } + + /** + * Determine which transformer filter to apply. + * + * @return array + */ + protected function transformWithFilter(Allocation $allocation) + { + if ($this->filter === 'server') { + return $this->transformForServer($allocation); + } + + return $allocation->toArray(); + } + + /** + * Transform the allocation to only return information not duplicated + * in the server response (discard node_id and server_id). + * + * @return array + */ + protected function transformForServer(Allocation $allocation) + { + return collect($allocation)->only('id', 'ip', 'ip_alias', 'port')->toArray(); + } +} diff --git a/app/Transformers/Admin/LocationTransformer.php b/app/Transformers/Admin/LocationTransformer.php new file mode 100644 index 000000000..f3fd95885 --- /dev/null +++ b/app/Transformers/Admin/LocationTransformer.php @@ -0,0 +1,102 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Location; +use League\Fractal\TransformerAbstract; + +class LocationTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'nodes', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed pack array. + * + * @return array + */ + public function transform(Location $location) + { + return $location->toArray(); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Location $location) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($location->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeNodes(Location $location) + { + if ($this->request && ! $this->request->apiKeyHasPermission('node-list')) { + return; + } + + return $this->collection($location->nodes, new NodeTransformer($this->request), 'node'); + } +} diff --git a/app/Transformers/Admin/NodeTransformer.php b/app/Transformers/Admin/NodeTransformer.php new file mode 100644 index 000000000..d18b64f23 --- /dev/null +++ b/app/Transformers/Admin/NodeTransformer.php @@ -0,0 +1,117 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Node; +use League\Fractal\TransformerAbstract; + +class NodeTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'allocations', + 'location', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed pack array. + * + * @return array + */ + public function transform(Node $node) + { + return $node->toArray(); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAllocations(Node $node) + { + if ($this->request && ! $this->request->apiKeyHasPermission('node-view')) { + return; + } + + return $this->collection($node->allocations, new AllocationTransformer($this->request), 'allocation'); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeLocation(Node $node) + { + if ($this->request && ! $this->request->apiKeyHasPermission('location-list')) { + return; + } + + return $this->item($node->location, new LocationTransformer($this->request), 'location'); + } + + /** + * Return the nodes associated with this location. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Node $node) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($node->servers, new ServerTransformer($this->request), 'server'); + } +} diff --git a/app/Transformers/Admin/OptionTransformer.php b/app/Transformers/Admin/OptionTransformer.php new file mode 100644 index 000000000..5b86b53d6 --- /dev/null +++ b/app/Transformers/Admin/OptionTransformer.php @@ -0,0 +1,132 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\ServiceOption; +use League\Fractal\TransformerAbstract; + +class OptionTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'service', + 'packs', + 'servers', + 'variables', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed service option array. + * + * @return array + */ + public function transform(ServiceOption $option) + { + return $option->toArray(); + } + + /** + * Return the parent service for this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeService(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('service-view')) { + return; + } + + return $this->item($option->service, new ServiceTransformer($this->request), 'service'); + } + + /** + * Return the packs associated with this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includePacks(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('pack-list')) { + return; + } + + return $this->collection($option->packs, new PackTransformer($this->request), 'pack'); + } + + /** + * Return the servers associated with this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($option->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the variables for this service option. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeVariables(ServiceOption $option) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->collection($option->variables, new ServiceVariableTransformer($this->request), 'variable'); + } +} diff --git a/app/Transformers/Admin/PackTransformer.php b/app/Transformers/Admin/PackTransformer.php new file mode 100644 index 000000000..8d059faaf --- /dev/null +++ b/app/Transformers/Admin/PackTransformer.php @@ -0,0 +1,106 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Pack; +use League\Fractal\TransformerAbstract; + +class PackTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'option', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed pack array. + * + * @return array + */ + public function transform($pack) + { + if (! $pack instanceof Pack) { + return ['id' => null]; + } + + return $pack->toArray(); + } + + /** + * Return the packs associated with this service. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeOption(Pack $pack) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->item($pack->option, new OptionTransformer($this->request), 'option'); + } + + /** + * Return the packs associated with this service. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Pack $pack) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($pack->servers, new ServerTransformer($this->request), 'server'); + } +} diff --git a/app/Transformers/Admin/ServerTransformer.php b/app/Transformers/Admin/ServerTransformer.php new file mode 100644 index 000000000..4d94b7e10 --- /dev/null +++ b/app/Transformers/Admin/ServerTransformer.php @@ -0,0 +1,207 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Server; +use League\Fractal\TransformerAbstract; + +class ServerTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'allocations', + 'user', + 'subusers', + 'pack', + 'service', + 'option', + 'variables', + 'location', + 'node', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed server array. + * + * @return array + */ + public function transform(Server $server) + { + return collect($server->toArray())->only($server->getTableColumns())->toArray(); + } + + /** + * Return a generic array of allocations for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAllocations(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($server->allocations, new AllocationTransformer($this->request, 'server'), 'allocation'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeSubusers(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($server->subusers, new SubuserTransformer($this->request), 'subuser'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeUser(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('user-view')) { + return; + } + + return $this->item($server->user, new UserTransformer($this->request), 'user'); + } + + /** + * Return a generic array with pack information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includePack(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('pack-view')) { + return; + } + + return $this->item($server->pack, new PackTransformer($this->request), 'pack'); + } + + /** + * Return a generic array with service information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeService(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('service-view')) { + return; + } + + return $this->item($server->service, new ServiceTransformer($this->request), 'service'); + } + + /** + * Return a generic array with service option information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeOption(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->item($server->option, new OptionTransformer($this->request), 'option'); + } + + /** + * Return a generic array of data about subusers for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeVariables(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($server->variables, new ServerVariableTransformer($this->request), 'server_variable'); + } + + /** + * Return a generic array with pack information for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeLocation(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('location-list')) { + return; + } + + return $this->item($server->location, new LocationTransformer($this->request), 'location'); + } + + /** + * Return a generic array with pack information for this server. + * + * @return \Leauge\Fractal\Resource\Item|void + */ + public function includeNode(Server $server) + { + if ($this->request && ! $this->request->apiKeyHasPermission('node-view')) { + return; + } + + return $this->item($server->node, new NodeTransformer($this->request), 'node'); + } +} diff --git a/app/Transformers/Admin/ServerVariableTransformer.php b/app/Transformers/Admin/ServerVariableTransformer.php new file mode 100644 index 000000000..3211e0295 --- /dev/null +++ b/app/Transformers/Admin/ServerVariableTransformer.php @@ -0,0 +1,85 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\ServerVariable; +use League\Fractal\TransformerAbstract; + +class ServerVariableTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = ['parent']; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed server variable array. + * + * @return array + */ + public function transform(ServerVariable $variable) + { + return $variable->toArray(); + } + + /** + * Return the parent service variable data. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeParent(ServerVariable $variable) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->item($variable->variable, new ServiceVariableTransformer($this->request), 'variable'); + } +} diff --git a/app/Transformers/Admin/ServiceTransformer.php b/app/Transformers/Admin/ServiceTransformer.php new file mode 100644 index 000000000..2df1fc8cc --- /dev/null +++ b/app/Transformers/Admin/ServiceTransformer.php @@ -0,0 +1,117 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Service; +use League\Fractal\TransformerAbstract; + +class ServiceTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'options', + 'servers', + 'packs', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed service array. + * + * @return array + */ + public function transform(Service $service) + { + return $service->toArray(); + } + + /** + * Return the the service options. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeOptions(Service $service) + { + if ($this->request && ! $this->request->apiKeyHasPermission('option-list')) { + return; + } + + return $this->collection($service->options, new OptionTransformer($this->request), 'option'); + } + + /** + * Return the servers associated with this service. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(Service $service) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($service->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the packs associated with this service. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includePacks(Service $service) + { + if ($this->request && ! $this->request->apiKeyHasPermission('pack-list')) { + return; + } + + return $this->collection($service->packs, new PackTransformer($this->request), 'pack'); + } +} diff --git a/app/Transformers/Admin/ServiceVariableTransformer.php b/app/Transformers/Admin/ServiceVariableTransformer.php new file mode 100644 index 000000000..aa10428d9 --- /dev/null +++ b/app/Transformers/Admin/ServiceVariableTransformer.php @@ -0,0 +1,85 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use League\Fractal\TransformerAbstract; +use Pterodactyl\Models\ServiceVariable; + +class ServiceVariableTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = ['variables']; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed server variable array. + * + * @return array + */ + public function transform(ServiceVariable $variable) + { + return $variable->toArray(); + } + + /** + * Return the server variables associated with this variable. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeVariables(ServiceVariable $variable) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return $this->collection($variable->serverVariable, new ServerVariableTransformer($this->request), 'server_variable'); + } +} diff --git a/app/Transformers/Admin/SubuserTransformer.php b/app/Transformers/Admin/SubuserTransformer.php new file mode 100644 index 000000000..129da7ad3 --- /dev/null +++ b/app/Transformers/Admin/SubuserTransformer.php @@ -0,0 +1,77 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\Subuser; +use Pterodactyl\Models\Permission; +use League\Fractal\TransformerAbstract; + +class SubuserTransformer extends TransformerAbstract +{ + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed subuser array. + * + * @return array + */ + public function transform(Subuser $subuser) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + + return [ + 'id' => $subuser->id, + 'username' => $subuser->user->username, + 'email' => $subuser->user->email, + '2fa' => (bool) $subuser->user->use_totp, + 'permissions' => $subuser->permissions->pluck('permission'), + 'created_at' => $subuser->created_at, + 'updated_at' => $subuser->updated_at, + ]; + } +} diff --git a/app/Transformers/Admin/UserTransformer.php b/app/Transformers/Admin/UserTransformer.php new file mode 100644 index 000000000..0d26961b9 --- /dev/null +++ b/app/Transformers/Admin/UserTransformer.php @@ -0,0 +1,102 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\Admin; + +use Illuminate\Http\Request; +use Pterodactyl\Models\User; +use League\Fractal\TransformerAbstract; + +class UserTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'access', + 'servers', + ]; + + /** + * The Illuminate Request object if provided. + * + * @var \Illuminate\Http\Request|bool + */ + protected $request; + + /** + * Setup request object for transformer. + * + * @param \Illuminate\Http\Request|bool $request + * @return void + */ + public function __construct($request = false) + { + if (! $request instanceof Request && $request !== false) { + throw new DisplayException('Request passed to constructor must be of type Request or false.'); + } + + $this->request = $request; + } + + /** + * Return a generic transformed subuser array. + * + * @return array + */ + public function transform(User $user) + { + return $user->toArray(); + } + + /** + * Return the servers associated with this user. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeServers(User $user) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($user->servers, new ServerTransformer($this->request), 'server'); + } + + /** + * Return the servers that this user can access. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAccess(User $user) + { + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($user->access()->get(), new ServerTransformer($this->request), 'server'); + } +} diff --git a/app/Transformers/User/AllocationTransformer.php b/app/Transformers/User/AllocationTransformer.php new file mode 100644 index 000000000..0fd0be453 --- /dev/null +++ b/app/Transformers/User/AllocationTransformer.php @@ -0,0 +1,64 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\User; + +use Pterodactyl\Models\Server; +use Pterodactyl\Models\Allocation; +use League\Fractal\TransformerAbstract; + +class AllocationTransformer extends TransformerAbstract +{ + /** + * Server eloquent model. + * + * @return \Pterodactyl\Models\Server + */ + protected $server; + + /** + * Setup allocation transformer with access to server data. + * + * @return void + */ + public function __construct(Server $server) + { + $this->server = $server; + } + + /** + * Return a generic transformed allocation array. + * + * @return array + */ + public function transform(Allocation $allocation) + { + return [ + 'id' => $allocation->id, + 'ip' => $allocation->alias, + 'port' => $allocation->port, + 'default' => ($allocation->id === $this->server->allocation_id), + ]; + } +} diff --git a/app/Transformers/User/OverviewTransformer.php b/app/Transformers/User/OverviewTransformer.php new file mode 100644 index 000000000..c8e1db9ed --- /dev/null +++ b/app/Transformers/User/OverviewTransformer.php @@ -0,0 +1,50 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\User; + +use Pterodactyl\Models\Server; +use League\Fractal\TransformerAbstract; + +class OverviewTransformer extends TransformerAbstract +{ + /** + * Return a generic transformed server array. + * + * @return array + */ + public function transform(Server $server) + { + return [ + 'id' => $server->uuidShort, + 'uuid' => $server->uuid, + 'name' => $server->name, + 'node' => $server->node->name, + 'ip' => $server->allocation->alias, + 'port' => $server->allocation->port, + 'service' => $server->service->name, + 'option' => $server->option->name, + ]; + } +} diff --git a/app/Transformers/User/ServerTransformer.php b/app/Transformers/User/ServerTransformer.php new file mode 100644 index 000000000..4e5aacb56 --- /dev/null +++ b/app/Transformers/User/ServerTransformer.php @@ -0,0 +1,100 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\User; + +use Pterodactyl\Models\Server; +use League\Fractal\TransformerAbstract; + +class ServerTransformer extends TransformerAbstract +{ + /** + * List of resources that can be included. + * + * @var array + */ + protected $availableIncludes = [ + 'allocations', + 'subusers', + 'stats', + ]; + + /** + * Return a generic transformed server array. + * + * @return array + */ + public function transform(Server $server) + { + return [ + 'id' => $server->uuidShort, + 'uuid' => $server->uuid, + 'name' => $server->name, + 'description' => $server->description, + 'node' => $server->node->name, + 'limits' => [ + 'memory' => $server->memory, + 'swap' => $server->swap, + 'disk' => $server->disk, + 'io' => $server->io, + 'cpu' => $server->cpu, + 'oom_disabled' => (bool) $server->oom_disabled, + ], + ]; + } + + /** + * Return a generic array of allocations for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeAllocations(Server $server) + { + $allocations = $server->allocations; + + return $this->collection($allocations, new AllocationTransformer($server), 'allocation'); + } + + /** + * Return a generic array of subusers for this server. + * + * @return \Leauge\Fractal\Resource\Collection + */ + public function includeSubusers(Server $server) + { + $server->load('subusers.permissions', 'subusers.user'); + + return $this->collection($server->subusers, new SubuserTransformer, 'subuser'); + } + + /** + * Return a generic array of allocations for this server. + * + * @return \Leauge\Fractal\Resource\Item + */ + public function includeStats(Server $server) + { + return $this->item($server->guzzleClient(), new StatsTransformer, 'stat'); + } +} diff --git a/app/Transformers/User/StatsTransformer.php b/app/Transformers/User/StatsTransformer.php new file mode 100644 index 000000000..6b08ea8a5 --- /dev/null +++ b/app/Transformers/User/StatsTransformer.php @@ -0,0 +1,63 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Pterodactyl\Transformers\User; + +use GuzzleHttp\Client; +use League\Fractal\TransformerAbstract; +use GuzzleHttp\Exception\ConnectException; + +class StatsTransformer extends TransformerAbstract +{ + /** + * Return a generic transformed subuser array. + * + * @return array + */ + public function transform(Client $client) + { + try { + $res = $client->request('GET', '/server', ['http_errors' => false]); + + if ($res->getStatusCode() !== 200) { + return [ + 'error' => 'Error: HttpResponseException. Recieved non-200 HTTP status code from daemon: ' . $res->statusCode(), + ]; + } + + $json = json_decode($res->getBody()); + + return [ + 'id' => 1, + 'status' => $json->status, + 'resources' => $json->proc, + ]; + } catch (ConnectException $ex) { + return [ + 'error' => 'Error: ConnectException. Unable to contact the daemon to request server status.', + 'exception' => (config('app.debug')) ? $ex->getMessage() : null, + ]; + } + } +} diff --git a/app/Http/Controllers/API/ServiceController.php b/app/Transformers/User/SubuserTransformer.php old mode 100755 new mode 100644 similarity index 65% rename from app/Http/Controllers/API/ServiceController.php rename to app/Transformers/User/SubuserTransformer.php index 1916ef41c..e1a122f1f --- a/app/Http/Controllers/API/ServiceController.php +++ b/app/Transformers/User/SubuserTransformer.php @@ -22,22 +22,27 @@ * SOFTWARE. */ -namespace Pterodactyl\Http\Controllers\API; +namespace Pterodactyl\Transformers\User; -use Illuminate\Http\Request; -use Pterodactyl\Models\Service; +use Pterodactyl\Models\Subuser; +use Pterodactyl\Models\Permission; +use League\Fractal\TransformerAbstract; -class ServiceController extends BaseController +class SubuserTransformer extends TransformerAbstract { - public function index(Request $request) + /** + * Return a generic transformed subuser array. + * + * @return array + */ + public function transform(Subuser $subuser) { - return Service::all()->toArray(); - } - - public function view(Request $request, $id) - { - $service = Service::with('options.variables', 'options.packs')->findOrFail($id); - - return $service->toArray(); + return [ + 'id' => $subuser->id, + 'username' => $subuser->user->username, + 'email' => $subuser->user->email, + '2fa' => (bool) $subuser->user->use_totp, + 'permissions' => $subuser->permissions->pluck('permission'), + ]; } } diff --git a/composer.json b/composer.json index b0fcb26cc..ef76b71b0 100644 --- a/composer.json +++ b/composer.json @@ -12,26 +12,26 @@ ], "require": { "php": ">=7.0.0", - "laravel/framework": "5.4.16", - "laravel/tinker": "1.0.0", + "aws/aws-sdk-php": "3.25.1", "barryvdh/laravel-debugbar": "2.3.2", "doctrine/dbal": "2.5.12", + "edvinaskrucas/settings": "2.0.0", + "fideloper/proxy": "3.3.0", "guzzlehttp/guzzle": "6.2.3", + "igaster/laravel-theme": "1.14.0", + "laracasts/utilities": "2.1.0", + "laravel/framework": "5.4.16", + "laravel/tinker": "1.0.0", + "lord/laroute": "2.4.4", + "mtdowling/cron-expression": "1.2.0", + "nesbot/carbon": "1.22.1", + "nicolaslopezj/searchable": "1.9.5", "pragmarx/google2fa": "1.0.1", - "webpatser/laravel-uuid": "2.0.1", + "predis/predis": "1.1.1", "prologue/alerts": "0.4.1", "s1lentium/iptools": "1.1.0", - "edvinaskrucas/settings": "2.0.0", - "igaster/laravel-theme": "1.14.0", - "nesbot/carbon": "1.22.1", - "mtdowling/cron-expression": "1.2.0", - "dingo/api": "1.0.0-beta8", - "aws/aws-sdk-php": "3.25.1", - "predis/predis": "1.1.1", - "fideloper/proxy": "3.3.0", - "laracasts/utilities": "2.1.0", - "lord/laroute": "2.4.4", - "nicolaslopezj/searchable": "1.9.5" + "spatie/laravel-fractal": "3.5.0", + "webpatser/laravel-uuid": "2.0.1" }, "require-dev": { "fzaninotto/faker": "~1.4", diff --git a/composer.lock b/composer.lock index f4dcae4a8..4ce9d4ee0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "5f54adee49654a63977f6c5593e5e1a8", - "content-hash": "081359d468db67f98d08ff1f304ea712", + "hash": "ae03837be570fb8ebf2a916a50614193", + "content-hash": "d4eb663cbd7d79c85e06d626b64cdbcd", "packages": [ { "name": "aws/aws-sdk-php", @@ -195,136 +195,6 @@ ], "time": "2016-05-05 11:49:03" }, - { - "name": "dingo/api", - "version": "v1.0.0-beta8", - "source": { - "type": "git", - "url": "https://github.com/dingo/api.git", - "reference": "46cffad61942caa094dd876155e503b6819c5095" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dingo/api/zipball/46cffad61942caa094dd876155e503b6819c5095", - "reference": "46cffad61942caa094dd876155e503b6819c5095", - "shasum": "" - }, - "require": { - "dingo/blueprint": "0.2.*", - "illuminate/routing": "^5.1", - "illuminate/support": "^5.1", - "league/fractal": ">=0.12.0", - "php": "^5.5.9 || ^7.0" - }, - "require-dev": { - "illuminate/auth": "^5.1", - "illuminate/cache": "^5.1", - "illuminate/console": "^5.1", - "illuminate/database": "^5.1", - "illuminate/events": "^5.1", - "illuminate/filesystem": "^5.1", - "illuminate/log": "^5.1", - "illuminate/pagination": "^5.1", - "laravel/lumen-framework": "5.1.* || 5.2.*", - "lucadegasperi/oauth2-server-laravel": "5.0.*", - "mockery/mockery": "~0.9", - "phpunit/phpunit": "^4.8 || ^5.0", - "squizlabs/php_codesniffer": "~2.0", - "tymon/jwt-auth": "1.0.*" - }, - "suggest": { - "lucadegasperi/oauth2-server-laravel": "Protect your API with OAuth 2.0.", - "tymon/jwt-auth": "Protect your API with JSON Web Tokens." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Dingo\\Api\\": "src/" - }, - "files": [ - "src/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jason Lewis", - "email": "jason.lewis1991@gmail.com" - } - ], - "description": "A RESTful API package for the Laravel and Lumen frameworks.", - "keywords": [ - "api", - "dingo", - "laravel", - "restful" - ], - "time": "2017-02-10 00:56:04" - }, - { - "name": "dingo/blueprint", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/dingo/blueprint.git", - "reference": "690bdf0f76b4428fd52835b9d778fb4551333867" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dingo/blueprint/zipball/690bdf0f76b4428fd52835b9d778fb4551333867", - "reference": "690bdf0f76b4428fd52835b9d778fb4551333867", - "shasum": "" - }, - "require": { - "doctrine/annotations": "~1.2", - "illuminate/filesystem": "5.1.* || 5.2.* || 5.3.* || 5.4.*", - "illuminate/support": "5.1.* || 5.2.* || 5.3.* || 5.4.*", - "php": ">=5.5.9", - "phpdocumentor/reflection-docblock": "3.1.*" - }, - "require-dev": { - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.2-dev" - } - }, - "autoload": { - "psr-4": { - "Dingo\\Blueprint\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jason Lewis", - "email": "jason.lewis1991@gmail.com" - } - ], - "description": "API Blueprint documentation generator.", - "keywords": [ - "api", - "blueprint", - "dingo", - "docs", - "laravel" - ], - "time": "2017-02-11 17:28:57" - }, { "name": "dnoegel/php-xdg-base-dir", "version": "0.1", @@ -2157,152 +2027,6 @@ ], "time": "2017-03-13 16:27:32" }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2015-12-27 11:43:31" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30 07:12:33" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.2.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2016-11-25 06:54:22" - }, { "name": "pragmarx/google2fa", "version": "v1.0.1", @@ -2766,6 +2490,115 @@ ], "time": "2016-08-21 15:57:09" }, + { + "name": "spatie/fractalistic", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/fractalistic.git", + "reference": "59a2e7dfbd49e58b7de7d5621c1e7365786809f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/fractalistic/zipball/59a2e7dfbd49e58b7de7d5621c1e7365786809f9", + "reference": "59a2e7dfbd49e58b7de7d5621c1e7365786809f9", + "shasum": "" + }, + "require": { + "league/fractal": "^0.14.0|^0.15.0", + "php": "^5.6|^7.0" + }, + "require-dev": { + "illuminate/pagination": "^5.3", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Fractalistic\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A developer friendly wrapper around Fractal", + "homepage": "https://github.com/spatie/fractalistic", + "keywords": [ + "api", + "fractal", + "fractalistic", + "spatie", + "transform" + ], + "time": "2017-01-19 20:51:03" + }, + { + "name": "spatie/laravel-fractal", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-fractal.git", + "reference": "16689b0ce340bc139ba767f0bf2904e4eab2b254" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-fractal/zipball/16689b0ce340bc139ba767f0bf2904e4eab2b254", + "reference": "16689b0ce340bc139ba767f0bf2904e4eab2b254", + "shasum": "" + }, + "require": { + "illuminate/contracts": "~5.1.0|~5.2.0|~5.3.0|~5.4.0", + "illuminate/support": "~5.1.0|~5.2.0|~5.3.0|~5.4.0", + "php": "^5.6|^7.0", + "spatie/fractalistic": "^1.0" + }, + "require-dev": { + "orchestra/testbench": "~3.2.0|3.3.0|~3.4.0", + "phpunit/phpunit": "^5.7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Fractal\\": "src" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A Fractal service provider for Laravel and Lumen", + "homepage": "https://github.com/spatie/laravel-fractal", + "keywords": [ + "api", + "fractal", + "laravel", + "laravel-fractal", + "lumen", + "spatie", + "transform" + ], + "time": "2017-02-21 23:17:02" + }, { "name": "swiftmailer/swiftmailer", "version": "v5.4.6", @@ -3755,56 +3588,6 @@ ], "time": "2016-09-01 10:05:43" }, - { - "name": "webmozart/assert", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2016-11-23 20:04:58" - }, { "name": "webpatser/laravel-uuid", "version": "2.0.1", @@ -4223,6 +4006,152 @@ ], "time": "2017-01-26 22:05:40" }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27 11:43:31" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-09-30 07:12:33" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2016-11-25 06:54:22" + }, { "name": "phpspec/prophecy", "version": "v1.7.0", @@ -5299,13 +5228,61 @@ "description": "Symfony Yaml Component", "homepage": "https://symfony.com", "time": "2017-03-07 16:47:02" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23 20:04:58" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "dingo/api": 10 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/config/api.php b/config/api.php deleted file mode 100644 index 2a847e7ac..000000000 --- a/config/api.php +++ /dev/null @@ -1,220 +0,0 @@ - env('API_STANDARDS_TREE', 'x'), - - /* - |-------------------------------------------------------------------------- - | API Subtype - |-------------------------------------------------------------------------- - | - | Your subtype will follow the standards tree you use when used in the - | "Accept" header to negotiate the content type and version. - | - | For example: Accept: application/x.SUBTYPE.v1+json - | - */ - - 'subtype' => env('API_SUBTYPE', ''), - - /* - |-------------------------------------------------------------------------- - | Default API Version - |-------------------------------------------------------------------------- - | - | This is the default version when strict mode is disabled and your API - | is accessed via a web browser. It's also used as the default version - | when generating your APIs documentation. - | - */ - - 'version' => env('API_VERSION', 'v1'), - - /* - |-------------------------------------------------------------------------- - | Default API Prefix - |-------------------------------------------------------------------------- - | - | A default prefix to use for your API routes so you don't have to - | specify it for each group. - | - */ - - 'prefix' => env('API_PREFIX', 'api'), - - /* - |-------------------------------------------------------------------------- - | Default API Domain - |-------------------------------------------------------------------------- - | - | A default domain to use for your API routes so you don't have to - | specify it for each group. - | - */ - - 'domain' => env('API_DOMAIN', null), - - /* - |-------------------------------------------------------------------------- - | Name - |-------------------------------------------------------------------------- - | - | When documenting your API using the API Blueprint syntax you can - | configure a default name to avoid having to manually specify - | one when using the command. - | - */ - - 'name' => env('API_NAME', null), - - /* - |-------------------------------------------------------------------------- - | Conditional Requests - |-------------------------------------------------------------------------- - | - | Globally enable conditional requests so that an ETag header is added to - | any successful response. Subsequent requests will perform a check and - | will return a 304 Not Modified. This can also be enabled or disabled - | on certain groups or routes. - | - */ - - 'conditionalRequest' => env('API_CONDITIONAL_REQUEST', true), - - /* - |-------------------------------------------------------------------------- - | Strict Mode - |-------------------------------------------------------------------------- - | - | Enabling strict mode will require clients to send a valid Accept header - | with every request. This also voids the default API version, meaning - | your API will not be browsable via a web browser. - | - */ - - 'strict' => env('API_STRICT', false), - - /* - |-------------------------------------------------------------------------- - | Debug Mode - |-------------------------------------------------------------------------- - | - | Enabling debug mode will result in error responses caused by thrown - | exceptions to have a "debug" key that will be populated with - | more detailed information on the exception. - | - */ - - 'debug' => env('API_DEBUG', false), - - /* - |-------------------------------------------------------------------------- - | Generic Error Format - |-------------------------------------------------------------------------- - | - | When some HTTP exceptions are not caught and dealt with the API will - | generate a generic error response in the format provided. Any - | keys that aren't replaced with corresponding values will be - | removed from the final response. - | - */ - - 'errorFormat' => [ - 'message' => ':message', - 'errors' => ':errors', - 'code' => ':code', - 'status_code' => ':status_code', - 'debug' => ':debug', - ], - - /* - |-------------------------------------------------------------------------- - | API Middleware - |-------------------------------------------------------------------------- - | - | Middleware that will be applied globally to all API requests. - | - */ - - 'middleware' => [ - - ], - - /* - |-------------------------------------------------------------------------- - | Authentication Providers - |-------------------------------------------------------------------------- - | - | The authentication providers that should be used when attempting to - | authenticate an incoming API request. - | - */ - - 'auth' => [ - 'custom' => 'Pterodactyl\Http\Middleware\APISecretToken', - ], - - /* - |-------------------------------------------------------------------------- - | Throttling / Rate Limiting - |-------------------------------------------------------------------------- - | - | Consumers of your API can be limited to the amount of requests they can - | make. You can create your own throttles or simply change the default - | throttles. - | - */ - - 'throttling' => [ - - ], - - /* - |-------------------------------------------------------------------------- - | Response Transformer - |-------------------------------------------------------------------------- - | - | Responses can be transformed so that they are easier to format. By - | default a Fractal transformer will be used to transform any - | responses prior to formatting. You can easily replace - | this with your own transformer. - | - */ - - 'transformer' => env('API_TRANSFORMER', Dingo\Api\Transformer\Adapter\Fractal::class), - - /* - |-------------------------------------------------------------------------- - | Response Formats - |-------------------------------------------------------------------------- - | - | Responses can be returned in multiple formats by registering different - | response formatters. You can also customize an existing response - | formatter. - | - */ - - 'defaultFormat' => env('API_DEFAULT_FORMAT', 'json'), - - 'formats' => [ - 'json' => Dingo\Api\Http\Response\Format\Json::class, - ], - -]; diff --git a/config/app.php b/config/app.php index 1e575d420..3c3fb772d 100644 --- a/config/app.php +++ b/config/app.php @@ -127,8 +127,6 @@ return [ 'providers' => [ - Dingo\Api\Provider\LaravelServiceProvider::class, - /* * Laravel Framework Service Providers... */ @@ -180,6 +178,7 @@ return [ Fideloper\Proxy\TrustedProxyServiceProvider::class, Laracasts\Utilities\JavaScript\JavaScriptServiceProvider::class, Lord\Laroute\LarouteServiceProvider::class, + Spatie\Fractal\FractalServiceProvider::class, ], @@ -210,11 +209,10 @@ return [ 'Crypt' => Illuminate\Support\Facades\Crypt::class, 'DB' => Illuminate\Support\Facades\DB::class, 'Debugbar' => Barryvdh\Debugbar\Facade::class, - 'Dingo' => Dingo\Api\Facade\API::class, - 'DingoRoute'=> Dingo\Api\Facade\Route::class, 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 'Event' => Illuminate\Support\Facades\Event::class, 'File' => Illuminate\Support\Facades\File::class, + 'Fractal' => Spatie\Fractal\FractalFacade::class, 'Gate' => Illuminate\Support\Facades\Gate::class, 'Google2FA' => PragmaRX\Google2FA\Vendor\Laravel\Facade::class, 'Hash' => Illuminate\Support\Facades\Hash::class, diff --git a/config/debugbar.php b/config/debugbar.php index 4d9d8c45e..05e78c34c 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -28,7 +28,7 @@ return [ */ 'storage' => [ 'enabled' => true, - 'driver' => 'file', // redis, file, pdo + 'driver' => env('DEBUGBAR_DRIVER', 'file'), // redis, file, pdo 'path' => storage_path() . '/debugbar', // For file driver 'connection' => null, // Leave null for default connection (Redis/PDO) ], @@ -125,7 +125,7 @@ return [ 'enabled' => false, 'types' => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'], // array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); for MySQL 5.6.3+ ], - 'hints' => true, // Show hints for common mistakes + 'hints' => false, // Show hints for common mistakes ], 'mail' => [ 'full_log' => false, diff --git a/config/laravel-fractal.php b/config/laravel-fractal.php new file mode 100644 index 000000000..32ced203e --- /dev/null +++ b/config/laravel-fractal.php @@ -0,0 +1,18 @@ + League\Fractal\Serializer\JsonApiSerializer::class, + +]; diff --git a/config/pterodactyl.php b/config/pterodactyl.php index ddaee443e..dc3f3072c 100644 --- a/config/pterodactyl.php +++ b/config/pterodactyl.php @@ -26,8 +26,24 @@ return [ */ 'paginate' => [ 'frontend' => [ - 'servers' => 15, + 'servers' => env('APP_PAGINATE_FRONT_SERVERS', 15), ], + 'api' => [ + 'nodes' => env('APP_PAGINATE_API_NODES', 25), + 'servers' => env('APP_PAGINATE_API_SERVERS', 25), + 'users' => env('APP_PAGINATE_API_USERS', 25), + ], + ], + + /* + |-------------------------------------------------------------------------- + | API Options + |-------------------------------------------------------------------------- + | + | Configuration options for the API. + */ + 'api' => [ + 'include_on_list' => env('API_INCLUDE_ON_LIST', false), ], /* @@ -103,4 +119,18 @@ return [ 'lang' => [ 'in_context' => env('PHRASE_IN_CONTEXT', false), ], + + /* + |-------------------------------------------------------------------------- + | JSON Response Routes + |-------------------------------------------------------------------------- + | + | You should not edit this block. These routes are ajax based routes that + | expect content to be returned in JSON format. + */ + 'json_routes' => [ + 'api/*', + 'daemon/*', + 'remote/*', + ], ]; diff --git a/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php new file mode 100644 index 000000000..ccd318654 --- /dev/null +++ b/database/migrations/2017_04_02_163232_DropDeletedAtColumnFromServers.php @@ -0,0 +1,32 @@ +dropColumn('deleted_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('servers', function (Blueprint $table) { + $table->timestamp('deleted_at')->nullable(); + }); + } +} diff --git a/resources/lang/en/base.php b/resources/lang/en/base.php index 5d1a18cb2..ca5aa923c 100644 --- a/resources/lang/en/base.php +++ b/resources/lang/en/base.php @@ -46,122 +46,162 @@ return [ 'title' => 'Allowed IPs', 'description' => 'Enter a line delimitated list of IPs that are allowed to access the API using this key. CIDR notation is allowed. Leave blank to allow any IP.', ], - 'base' => [ - 'title' => 'Base Information', - 'information' => [ - 'title' => 'Base Information', - 'description' => 'Returns a listing of all servers that this account has access to.', - ], - ], - 'user_management' => [ - 'title' => 'User Management', - 'list' => [ - 'title' => 'List Users', - 'description' => 'Allows listing of all users currently on the system.', - ], - 'create' => [ - 'title' => 'Create User', - 'description' => 'Allows creating a new user on the system.', - ], - 'view' => [ - 'title' => 'List Single User', - 'description' => 'Allows viewing details about a specific user including active services.', - ], - 'update' => [ - 'title' => 'Update User', - 'description' => 'Allows modifying user details (email, password, TOTP information).', - ], - 'delete' => [ - 'title' => 'Delete User', - 'description' => 'Allows deleting a user.', - ], - ], - 'node_management' => [ - 'title' => 'Node Management', - 'list' => [ - 'title' => 'List Nodes', - 'description' => 'Allows listing of all nodes currently on the system.', - ], - 'create' => [ - 'title' => 'Create Node', - 'description' => 'Allows creating a new node on the system.', - ], - 'view' => [ - 'title' => 'List Single Node', - 'description' => 'Allows viewing details about a specific node including active services.', - ], - 'allocations' => [ - 'title' => 'List Allocations', - 'description' => 'Allows viewing all allocations on the panel for all nodes.', - ], - 'delete' => [ - 'title' => 'Delete Node', - 'description' => 'Allows deleting a node.', - ], - ], - 'server_management' => [ - 'title' => 'Server Management', + ], + 'permissions' => [ + 'user' => [ + 'server_header' => 'User Server Permissions', 'server' => [ - 'title' => 'Server Info', - 'description' => 'Allows access to viewing information about a single server including current stats and allocations.', - ], - 'power' => [ - 'title' => 'Server Power', - 'description' => 'Allows access to control server power status.', - ], - 'command' => [ - 'title' => 'Send Command', - 'description' => 'Allows a user to send a command to a specified server.', - ], - 'view' => [ - 'title' => 'Show Single Server', - 'description' => 'Allows viewing details about a specific server including the daemon_token as well as current process information.', - ], - 'list' => [ - 'title' => 'List Servers', - 'description' => 'Allows listing of all servers currently on the system.', - ], - 'create' => [ - 'title' => 'Create Server', - 'description' => 'Allows creating a new server on the system.', - ], - 'config' => [ - 'title' => 'Update Configuration', - 'description' => 'Allows modifying server config (name, owner, and access token).', - ], - 'build' => [ - 'title' => 'Update Build', - 'description' => 'Allows modifying a server\'s build parameters such as memory, CPU, and disk space along with assigned and default IPs.', - ], - 'suspend' => [ - 'title' => 'Suspend Server', - 'description' => 'Allows suspending a server instance.', - ], - 'unsuspend' => [ - 'title' => 'Unsuspend Server', - 'description' => 'Allows unsuspending a server instance.', - ], - 'delete' => [ - 'title' => 'Delete Server', - 'description' => 'Allows deleting a server.', + 'list' => [ + 'title' => 'List Servers', + 'desc' => 'Allows listing of all servers a user owns or has access to as a subuser.', + ], + 'view' => [ + 'title' => 'View Server', + 'desc'=> 'Allows viewing of specific server user can access.', + ], + 'power' => [ + 'title' => 'Toggle Power', + 'desc'=> 'Allow toggling of power status for a server.', + ], + 'command' => [ + 'title' => 'Send Command', + 'desc'=> 'Allow sending of a command to a running server.', + ], ], ], - 'service_management' => [ - 'title' => 'Service Management', - 'list' => [ - 'title' => 'List Services', - 'description' => 'Allows listing of all services configured on the system.', + 'admin' => [ + 'server_header' => 'Server Control', + 'server' => [ + 'list' => [ + 'title' => 'List Servers', + 'desc' => 'Allows listing of all servers currently on the system.', + ], + 'view' => [ + 'title' => 'View Server', + 'desc' => 'Allows view of single server including service and details.', + ], + 'delete' => [ + 'title' => 'Delete Server', + 'desc' => 'Allows deletion of a server from the system.', + ], + 'create' => [ + 'title' => 'Create Server', + 'desc' => 'Allows creation of a new server on the system.', + ], + 'edit-details' => [ + 'title' => 'Edit Server Details', + 'desc' => 'Allows editing of server details such as name, owner, description, and secret key.', + ], + 'edit-container' => [ + 'title' => 'Edit Server Container', + 'desc' => 'Allows for modification of the docker container the server runs in.', + ], + 'suspend' => [ + 'title' => 'Suspend Server', + 'desc' => 'Allows for the suspension and unsuspension of a given server.', + ], + 'install' => [ + 'title' => 'Toggle Install Status', + 'desc' => '', + ], + 'rebuild' => [ + 'title' => 'Rebuild Server', + 'desc' => '', + ], + 'edit-build' => [ + 'title' => 'Edit Server Build', + 'desc' => 'Allows editing of server build setting such as CPU and memory allocations.', + ], + 'edit-startup' => [ + 'title' => 'Edit Server Startup', + 'desc' => 'Allows modification of server startup commands and parameters.', + ], ], - 'view' => [ - 'title' => 'List Single Service', - 'description' => 'Allows listing details about each service on the system including service options and variables.', + 'location_header' => 'Location Control', + 'location' => [ + 'list' => [ + 'title' => 'List Locations', + 'desc' => 'Allows listing all locations and thier associated nodes.', + ], ], - ], - 'location_management' => [ - 'title' => 'Location Management', - 'list' => [ - 'title' => 'List Locations', - 'description' => 'Allows listing all locations and thier associated nodes.', + 'node_header' => 'Node Control', + 'node' => [ + 'list' => [ + 'title' => 'List Nodes', + 'desc' => 'Allows listing of all nodes currently on the system.', + ], + 'view' => [ + 'title' => 'View Node', + 'desc' => 'llows viewing details about a specific node including active services.', + ], + 'view-config' => [ + 'title' => 'View Node Configuration', + 'desc' => 'Danger. This allows the viewing of the node configuration file used by the daemon, and exposes secret daemon tokens.', + ], + 'create' => [ + 'title' => 'Create Node', + 'desc' => 'Allows creating a new node on the system.', + ], + 'delete' => [ + 'title' => 'Delete Node', + 'desc' => 'Allows deletion of a node from the system.', + ], + ], + 'user_header' => 'User Control', + 'user' => [ + 'list' => [ + 'title' => 'List Users', + 'desc' => 'Allows listing of all users currently on the system.', + ], + 'view' => [ + 'title' => 'View User', + 'desc' => 'Allows viewing details about a specific user including active services.', + ], + 'create' => [ + 'title' => 'Create User', + 'desc' => 'Allows creating a new user on the system.', + ], + 'edit' => [ + 'title' => 'Update User', + 'desc' => 'Allows modification of user details.', + ], + 'delete' => [ + 'title' => 'Delete User', + 'desc' => 'Allows deleting a user.', + ], + ], + 'service_header' => 'Service Control', + 'service' => [ + 'list' => [ + 'title' => 'List Services', + 'desc' => 'Allows listing of all services configured on the system.', + ], + 'view' => [ + 'title' => 'View Service', + 'desc' => 'Allows listing details about each service on the system including service options and variables.', + ], + ], + 'option_header' => 'Option Control', + 'option' => [ + 'list' => [ + 'title' => 'List Options', + 'desc' => '', + ], + 'view' => [ + 'title' => 'View Option', + 'desc' => '', + ], + ], + 'pack_header' => 'Pack Control', + 'pack' => [ + 'list' => [ + 'title' => 'List Packs', + 'desc' => '', + ], + 'view' => [ + 'title' => 'View Pack', + 'desc' => '', + ], ], ], ], diff --git a/resources/themes/pterodactyl/base/api/new.blade.php b/resources/themes/pterodactyl/base/api/new.blade.php index be972d67a..8f8c90a22 100644 --- a/resources/themes/pterodactyl/base/api/new.blade.php +++ b/resources/themes/pterodactyl/base/api/new.blade.php @@ -47,8 +47,8 @@ @endsection @section('content') -
-
+ +
@@ -75,292 +75,70 @@
+ {!! csrf_field() !!}
-
-
-
-
-
-
@lang('base.api.new.base.title')
-
-
-
-
- - -
-

@lang('base.api.new.base.information.description')

+
+
+ @foreach($permissions['user'] as $block => $perms) +
+
+
+

@lang('base.api.permissions.user.' . $block . '_header')

+
+
+ @foreach($perms as $permission) +
+
+ + +
+

@lang('base.api.permissions.user.' . $block . '.' . $permission . '.desc')

+
+ @endforeach
- @if(Auth::user()->isRootAdmin()) -
-
-
@lang('base.api.new.user_management.title')
-
-
-
-
- - -
-

@lang('base.api.new.user_management.list.description')

-
-
-
- - -
-

@lang('base.api.new.user_management.create.description')

-
-
-
- - -
-

@lang('base.api.new.user_management.view.description')

-
-
-
- - -
-

@lang('base.api.new.user_management.update.description')

-
-
-
- - -
-

@lang('base.api.new.user_management.delete.description')

-
-
-
-
-
-
@lang('base.api.new.node_management.title')
-
-
-
-
- - -
-

@lang('base.api.new.node_management.list.description')

-
-
-
- - -
-

@lang('base.api.new.node_management.create.description')

-
-
-
- - -
-

@lang('base.api.new.node_management.view.description')

-
-
-
- - -
-

@lang('base.api.new.node_management.allocations.description')

-
-
-
- - -
-

@lang('base.api.new.node_management.delete.description')

-
-
-
-
-
-
@lang('base.api.new.location_management.title')
-
-
-
-
- - -
-

@lang('base.api.new.location_management.list.description')

-
-
-
+ @if ($loop->iteration % 2 === 0) +
@endif -
-
-
-
-
@lang('base.api.new.server_management.title')
-
-
-
-
- - -
-

@lang('base.api.new.server_management.server.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.power.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.command.description')

-
- @if(Auth::user()->isRootAdmin()) -
-
- - -
-

@lang('base.api.new.server_management.view.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.list.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.create.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.config.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.build.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.suspend.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.unsuspend.description')

-
-
-
- - -
-

@lang('base.api.new.server_management.delete.description')

-
- @endif -
-
- @if(Auth::user()->isRootAdmin()) -
+ @endforeach +
+
+ @foreach($permissions['admin'] as $block => $perms) +
+
-
@lang('base.api.new.service_management.title')
+

@lang('base.api.permissions.admin.' . $block . '_header')

-
-
- - + @foreach($perms as $permission) +
+
+ + +
+

@lang('base.api.permissions.admin.' . $block . '.' . $permission . '.desc')

-

@lang('base.api.new.service_management.list.description')

-
-
-
- - -
-

@lang('base.api.new.service_management.view.description')

-
+ @endforeach
+
+ @if ($loop->iteration % 3 === 0) +
@endif -
- {!! csrf_field() !!} - -
+ @if ($loop->iteration % 2 === 0) +
+ @endif + @endforeach +
+ @endsection diff --git a/routes/admin.php b/routes/admin.php index 2f04f7835..a702e2f5c 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - Route::get('/', 'BaseController@getIndex')->name('admin.index'); /* @@ -79,15 +78,15 @@ Route::group(['prefix' => 'settings'], function () { | */ Route::group(['prefix' => 'users'], function () { - Route::get('/', 'UserController@getIndex')->name('admin.users'); - Route::get('/accounts.json', 'UserController@getJson')->name('admin.users.json'); - Route::get('/new', 'UserController@getNew')->name('admin.users.new'); - Route::get('/view/{id}', 'UserController@getView')->name('admin.users.view'); + Route::get('/', 'UserController@index')->name('admin.users'); + Route::get('/accounts.json', 'UserController@json')->name('admin.users.json'); + Route::get('/new', 'UserController@create')->name('admin.users.new'); + Route::get('/view/{id}', 'UserController@view')->name('admin.users.view'); - Route::post('/new', 'UserController@postNew'); - Route::post('/view/{id}', 'UserController@updateUser'); + Route::post('/new', 'UserController@store'); + Route::post('/view/{id}', 'UserController@update'); - Route::delete('/view/{id}', 'UserController@deleteUser'); + Route::delete('/view/{id}', 'UserController@delete'); }); /* @@ -100,8 +99,8 @@ Route::group(['prefix' => 'users'], function () { */ Route::group(['prefix' => 'servers'], function () { Route::get('/', 'ServersController@index')->name('admin.servers'); - Route::get('/new', 'ServersController@new')->name('admin.servers.new'); - Route::get('/new/nodes', 'ServersController@newServerNodes')->name('admin.servers.new.nodes'); + Route::get('/new', 'ServersController@create')->name('admin.servers.new'); + Route::get('/new/nodes', 'ServersController@nodes')->name('admin.servers.new.nodes'); Route::get('/view/{id}', 'ServersController@viewIndex')->name('admin.servers.view'); Route::get('/view/{id}/details', 'ServersController@viewDetails')->name('admin.servers.view.details'); Route::get('/view/{id}/build', 'ServersController@viewBuild')->name('admin.servers.view.build'); @@ -110,7 +109,7 @@ Route::group(['prefix' => 'servers'], function () { Route::get('/view/{id}/manage', 'ServersController@viewManage')->name('admin.servers.view.manage'); Route::get('/view/{id}/delete', 'ServersController@viewDelete')->name('admin.servers.view.delete'); - Route::post('/new', 'ServersController@create'); + Route::post('/new', 'ServersController@store'); Route::post('/view/{id}/details', 'ServersController@setDetails'); Route::post('/view/{id}/details/container', 'ServersController@setContainer')->name('admin.servers.view.details.container'); Route::post('/view/{id}/build', 'ServersController@updateBuild'); @@ -136,7 +135,7 @@ Route::group(['prefix' => 'servers'], function () { */ Route::group(['prefix' => 'nodes'], function () { Route::get('/', 'NodesController@index')->name('admin.nodes'); - Route::get('/new', 'NodesController@new')->name('admin.nodes.new'); + Route::get('/new', 'NodesController@create')->name('admin.nodes.new'); Route::get('/view/{id}', 'NodesController@viewIndex')->name('admin.nodes.view'); Route::get('/view/{id}/settings', 'NodesController@viewSettings')->name('admin.nodes.view.settings'); Route::get('/view/{id}/configuration', 'NodesController@viewConfiguration')->name('admin.nodes.view.configuration'); @@ -144,7 +143,7 @@ Route::group(['prefix' => 'nodes'], function () { Route::get('/view/{id}/servers', 'NodesController@viewServers')->name('admin.nodes.view.servers'); Route::get('/view/{id}/settings/token', 'NodesController@setToken')->name('admin.nodes.view.configuration.token'); - Route::post('/new', 'NodesController@create'); + Route::post('/new', 'NodesController@store'); Route::post('/view/{id}/settings', 'NodesController@updateSettings'); Route::post('/view/{id}/allocation', 'NodesController@createAllocation'); Route::post('/view/{id}/allocation/remove', 'NodesController@allocationRemoveBlock')->name('admin.nodes.view.allocation.removeBlock'); @@ -164,16 +163,16 @@ Route::group(['prefix' => 'nodes'], function () { */ Route::group(['prefix' => 'services'], function () { Route::get('/', 'ServiceController@index')->name('admin.services'); - Route::get('/new', 'ServiceController@new')->name('admin.services.new'); + Route::get('/new', 'ServiceController@create')->name('admin.services.new'); Route::get('/view/{id}', 'ServiceController@view')->name('admin.services.view'); Route::get('/view/{id}/functions', 'ServiceController@viewFunctions')->name('admin.services.view.functions'); - Route::get('/option/new', 'OptionController@new')->name('admin.services.option.new'); + Route::get('/option/new', 'OptionController@create')->name('admin.services.option.new'); Route::get('/option/{id}', 'OptionController@viewConfiguration')->name('admin.services.option.view'); Route::get('/option/{id}/variables', 'OptionController@viewVariables')->name('admin.services.option.variables'); - Route::post('/new', 'ServiceController@create'); + Route::post('/new', 'ServiceController@store'); Route::post('/view/{id}', 'ServiceController@edit'); - Route::post('/option/new', 'OptionController@new'); + Route::post('/option/new', 'OptionController@store'); Route::post('/option/{id}', 'OptionController@editConfiguration'); Route::post('/option/{id}/variables', 'OptionController@createVariable'); Route::post('/option/{id}/variables/{variable}', 'OptionController@editVariable')->name('admin.services.option.variables.edit'); @@ -191,11 +190,11 @@ Route::group(['prefix' => 'services'], function () { */ Route::group(['prefix' => 'packs'], function () { Route::get('/', 'PackController@index')->name('admin.packs'); - Route::get('/new', 'PackController@new')->name('admin.packs.new'); + Route::get('/new', 'PackController@create')->name('admin.packs.new'); Route::get('/new/template', 'PackController@newTemplate')->name('admin.packs.new.template'); Route::get('/view/{id}', 'PackController@view')->name('admin.packs.view'); - Route::post('/new', 'PackController@create'); + Route::post('/new', 'PackController@store'); Route::post('/view/{id}', 'PackController@update'); Route::post('/view/{id}/export/{files?}', 'PackController@export')->name('admin.packs.view.export'); }); diff --git a/routes/api-admin.php b/routes/api-admin.php new file mode 100644 index 000000000..d5ca5b0b3 --- /dev/null +++ b/routes/api-admin.php @@ -0,0 +1,112 @@ +. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +Route::get('/', 'CoreController@index'); + +/* +|-------------------------------------------------------------------------- +| Server Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/servers +| +*/ +Route::group(['prefix' => '/servers'], function () { + Route::get('/', 'ServerController@index'); + Route::get('/{id}', 'ServerController@view'); + + Route::post('/', 'ServerController@store'); + + Route::put('/{id}/details', 'ServerController@details'); + Route::put('/{id}/container', 'ServerController@container'); + Route::put('/{id}/build', 'ServerController@build'); + Route::put('/{id}/startup', 'ServerController@startup'); + + Route::patch('/{id}/install', 'ServerController@install'); + Route::patch('/{id}/rebuild', 'ServerController@rebuild'); + Route::patch('/{id}/suspend', 'ServerController@suspend'); + + Route::delete('/{id}', 'ServerController@delete'); +}); + +/* +|-------------------------------------------------------------------------- +| Location Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/locations +| +*/ +Route::group(['prefix' => '/locations'], function () { + Route::get('/', 'LocationController@index'); +}); + +/* +|-------------------------------------------------------------------------- +| Node Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/nodes +| +*/ +Route::group(['prefix' => '/nodes'], function () { + Route::get('/', 'NodeController@index'); + Route::get('/{id}', 'NodeController@view'); + Route::get('/{id}/config', 'NodeController@viewConfig'); + + Route::post('/', 'NodeController@store'); + + Route::delete('/{id}', 'NodeController@delete'); +}); + +/* +|-------------------------------------------------------------------------- +| User Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/users +| +*/ +Route::group(['prefix' => '/users'], function () { + Route::get('/', 'UserController@index'); + Route::get('/{id}', 'UserController@view'); + + Route::post('/', 'UserController@store'); + + Route::put('/{id}', 'UserController@update'); + + Route::delete('/{id}', 'UserController@delete'); +}); + +/* +|-------------------------------------------------------------------------- +| Service Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/services +| +*/ +Route::group(['prefix' => '/services'], function () { + Route::get('/', 'ServiceController@index'); + Route::get('/{id}', 'ServiceController@view'); +}); diff --git a/app/Http/Controllers/API/BaseController.php b/routes/api.php old mode 100755 new mode 100644 similarity index 65% rename from app/Http/Controllers/API/BaseController.php rename to routes/api.php index 085803a11..9b2e01dfa --- a/app/Http/Controllers/API/BaseController.php +++ b/routes/api.php @@ -21,13 +21,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +Route::get('/', 'CoreController@index')->name('api.user'); -namespace Pterodactyl\Http\Controllers\API; +/* +|-------------------------------------------------------------------------- +| Server Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/user/server/{server} +| +*/ +Route::group([ + 'prefix' => '/server/{server}', + 'middleware' => 'server', +], function () { + Route::get('/', 'ServerController@index')->name('api.user.server'); -use Dingo\Api\Routing\Helpers; -use Illuminate\Routing\Controller; - -class BaseController extends Controller -{ - use Helpers; -} + Route::post('/power', 'ServerController@power')->name('api.user.server.power'); + Route::post('/command', 'ServerController@command')->name('api.user.server.command'); +}); diff --git a/routes/auth.php b/routes/auth.php index 2d0ae392b..46de5a1b5 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - Route::get('/logout', 'LoginController@logout')->name('auth.logout')->middleware('auth'); Route::get('/login', 'LoginController@showLoginForm')->name('auth.login'); Route::get('/login/totp', 'LoginController@totp')->name('auth.totp'); diff --git a/routes/base.php b/routes/base.php index cea722192..e13e1284a 100644 --- a/routes/base.php +++ b/routes/base.php @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - Route::get('/', 'IndexController@getIndex')->name('index'); Route::get('/index', function () { redirect()->route('index'); @@ -51,9 +50,9 @@ Route::group(['prefix' => 'account'], function () { */ Route::group(['prefix' => 'account/api'], function () { Route::get('/', 'APIController@index')->name('account.api'); - Route::get('/new', 'APIController@new')->name('account.api.new'); + Route::get('/new', 'APIController@create')->name('account.api.new'); - Route::post('/new', 'APIController@save'); + Route::post('/new', 'APIController@store'); Route::delete('/revoke/{key}', 'APIController@revoke')->name('account.api.revoke'); }); diff --git a/routes/daemon.php b/routes/daemon.php index 5a1fc35c9..78edf308f 100644 --- a/routes/daemon.php +++ b/routes/daemon.php @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - Route::get('/services', 'ServiceController@list')->name('daemon.services'); Route::get('/services/pull/{service}/{file}', 'ServiceController@pull')->name('daemon.pull'); Route::get('/packs/pull/{uuid}', 'PackController@pull')->name('daemon.pack.pull'); diff --git a/routes/remote.php b/routes/remote.php index c592ad325..d7fe471c5 100644 --- a/routes/remote.php +++ b/routes/remote.php @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - Route::get('/configuration/{token}', 'RemoteController@getConfiguration')->name('remote.configuration'); Route::post('/download', 'RemoteController@postDownload')->name('remote.download'); diff --git a/routes/server.php b/routes/server.php index cf46054cb..953eab2b2 100644 --- a/routes/server.php +++ b/routes/server.php @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - Route::get('/', 'ServerController@getIndex')->name('server.index'); /* @@ -71,14 +70,14 @@ Route::group(['prefix' => 'files'], function () { | */ Route::group(['prefix' => 'users'], function () { - Route::get('/', 'SubuserController@getIndex')->name('server.subusers'); - Route::get('/new', 'SubuserController@getNew')->name('server.subusers.new'); - Route::get('/view/{id}', 'SubuserController@getView')->name('server.subusers.view'); + Route::get('/', 'SubuserController@index')->name('server.subusers'); + Route::get('/new', 'SubuserController@create')->name('server.subusers.new'); + Route::get('/view/{id}', 'SubuserController@view')->name('server.subusers.view'); - Route::post('/new', 'SubuserController@postNew'); - Route::post('/view/{id}', 'SubuserController@postView'); + Route::post('/new', 'SubuserController@store'); + Route::post('/view/{id}', 'SubuserController@update'); - Route::delete('/delete/{id}', 'SubuserController@deleteSubuser')->name('server.subusers.delete'); + Route::delete('/delete/{id}', 'SubuserController@delete')->name('server.subusers.delete'); }); /* @@ -90,15 +89,13 @@ Route::group(['prefix' => 'users'], function () { | */ Route::group(['prefix' => 'tasks'], function () { - Route::get('/', 'TaskController@getIndex')->name('server.tasks'); - Route::get('/new', 'TaskController@getNew')->name('server.tasks.new'); - Route::get('/view/{id}', 'TaskController@getView')->name('server.tasks.view'); + Route::get('/', 'TaskController@index')->name('server.tasks'); + Route::get('/new', 'TaskController@create')->name('server.tasks.new'); - Route::post('/new', 'TaskController@postNew'); - Route::post('/view/{id}', 'SubuserController@postView'); - Route::post('/toggle/{id}', 'TaskController@toggleTask')->name('server.tasks.toggle'); + Route::post('/new', 'TaskController@store'); + Route::post('/toggle/{id}', 'TaskController@toggle')->name('server.tasks.toggle'); - Route::delete('/delete/{id}', 'TaskController@deleteTask')->name('server.tasks.delete'); + Route::delete('/delete/{id}', 'TaskController@delete')->name('server.tasks.delete'); }); /*