diff --git a/app/Http/Controllers/API/Admin/NodeController.php b/app/Http/Controllers/API/Admin/NodeController.php index 256f12771..a88d5054b 100644 --- a/app/Http/Controllers/API/Admin/NodeController.php +++ b/app/Http/Controllers/API/Admin/NodeController.php @@ -28,7 +28,10 @@ use Fractal; use Illuminate\Http\Request; use Pterodactyl\Models\Node; use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Repositories\NodeRepository; use Pterodactyl\Transformers\Admin\NodeTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; class NodeController extends Controller { @@ -72,4 +75,93 @@ class NodeController extends Controller ->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/UserController.php b/app/Http/Controllers/API/Admin/UserController.php new file mode 100644 index 000000000..1f0dfb69a --- /dev/null +++ b/app/Http/Controllers/API/Admin/UserController.php @@ -0,0 +1,177 @@ +. + * + * 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\Http\Controllers\Controller; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Repositories\UserRepository; +use Pterodactyl\Transformers\Admin\UserTransformer; +use Pterodactyl\Exceptions\DisplayValidationException; + +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()); + + $fractal = Fractal::create()->collection(User::all()); + if ($request->input('include')) { + $fractal->parseIncludes(explode(',', $request->input('include'))); + } + + return $fractal->transformWith(new UserTransformer($request)) + ->withResourceName('user') + ->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/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 8439cc2ab..f7324456b 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -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 updateUser(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); } /** diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 5619f26d5..902cebf15 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/LocationTransformer.php b/app/Transformers/Admin/LocationTransformer.php index 86980917b..0ef0e8875 100644 --- a/app/Transformers/Admin/LocationTransformer.php +++ b/app/Transformers/Admin/LocationTransformer.php @@ -79,7 +79,11 @@ class LocationTransformer extends TransformerAbstract */ public function includeServers(Location $location) { - return $this->collection($location->servers, new ServerTransformer, 'server'); + if ($this->request && ! $this->request->apiKeyHasPermission('server-list')) { + return; + } + + return $this->collection($location->servers, new ServerTransformer($this->request), 'server'); } /** @@ -89,6 +93,10 @@ class LocationTransformer extends TransformerAbstract */ public function includeNodes(Location $location) { - return $this->collection($location->nodes, new NodeTransformer, 'node'); + if ($this->request && ! $this->request->apiKeyHasPermission('location-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 index 5f55e425f..da49a17dd 100644 --- a/app/Transformers/Admin/NodeTransformer.php +++ b/app/Transformers/Admin/NodeTransformer.php @@ -84,7 +84,7 @@ class NodeTransformer extends TransformerAbstract return; } - return $this->collection($node->allocations, new AllocationTransformer, 'allocation'); + return $this->collection($node->allocations, new AllocationTransformer($this->request), 'allocation'); } /** @@ -98,7 +98,7 @@ class NodeTransformer extends TransformerAbstract return; } - return $this->item($node->location, new LocationTransformer, 'location'); + return $this->item($node->location, new LocationTransformer($this->request), 'location'); } /** @@ -112,6 +112,6 @@ class NodeTransformer extends TransformerAbstract return; } - return $this->collection($node->servers, new ServerTransformer, 'server'); + return $this->collection($node->servers, new ServerTransformer($this->request), 'server'); } } diff --git a/app/Transformers/Admin/OptionTransformer.php b/app/Transformers/Admin/OptionTransformer.php index 1d38b4a94..a5dd9a4b8 100644 --- a/app/Transformers/Admin/OptionTransformer.php +++ b/app/Transformers/Admin/OptionTransformer.php @@ -81,7 +81,11 @@ class OptionTransformer extends TransformerAbstract */ public function includeService(ServiceOption $option) { - return $this->item($option->service, new ServiceTransformer, 'service'); + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->item($option->service, new ServiceTransformer($this->request), 'service'); } /** @@ -91,7 +95,11 @@ class OptionTransformer extends TransformerAbstract */ public function includePacks(ServiceOption $option) { - return $this->collection($option->packs, new PackTransformer, 'pack'); + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->collection($option->packs, new PackTransformer($this->request), 'pack'); } /** @@ -101,7 +109,11 @@ class OptionTransformer extends TransformerAbstract */ public function includeServers(ServiceOption $option) { - return $this->collection($option->servers, new ServerTransformer, 'server'); + if ($this->request && ! $this->request->apiKeyHasPermission('option-view')) { + return; + } + + return $this->collection($option->servers, new ServerTransformer($this->request), 'server'); } /** @@ -111,6 +123,10 @@ class OptionTransformer extends TransformerAbstract */ public function includeVariables(ServiceOption $option) { - return $this->collection($option->variables, new ServiceVariableTransformer, 'variable'); + 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 index 50160744e..99603b6de 100644 --- a/app/Transformers/Admin/PackTransformer.php +++ b/app/Transformers/Admin/PackTransformer.php @@ -83,7 +83,11 @@ class PackTransformer extends TransformerAbstract */ public function includeOption(Pack $pack) { - return $this->item($pack->option, new OptionTransformer, 'option'); + if ($this->request && ! $this->request->apiKeyHasPermission('pack-view')) { + return; + } + + return $this->item($pack->option, new OptionTransformer($this->request), 'option'); } /** @@ -93,6 +97,10 @@ class PackTransformer extends TransformerAbstract */ public function includeServers(Pack $pack) { - return $this->collection($pack->servers, new ServerTransformer, 'server'); + if ($this->request && ! $this->request->apiKeyHasPermission('pack-view')) { + 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 index 23c754b5d..9bb23896c 100644 --- a/app/Transformers/Admin/ServerTransformer.php +++ b/app/Transformers/Admin/ServerTransformer.php @@ -86,6 +86,10 @@ class ServerTransformer extends TransformerAbstract */ 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'); } @@ -96,6 +100,10 @@ class ServerTransformer extends TransformerAbstract */ public function includeSubusers(Server $server) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + return $this->collection($server->subusers, new SubuserTransformer($this->request), 'subuser'); } @@ -106,6 +114,10 @@ class ServerTransformer extends TransformerAbstract */ public function includeUser(Server $server) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + return $this->item($server->user, new UserTransformer($this->request), 'user'); } @@ -116,6 +128,10 @@ class ServerTransformer extends TransformerAbstract */ public function includePack(Server $server) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + return $this->item($server->pack, new PackTransformer($this->request), 'pack'); } @@ -126,6 +142,10 @@ class ServerTransformer extends TransformerAbstract */ public function includeService(Server $server) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + return $this->item($server->service, new ServiceTransformer($this->request), 'service'); } @@ -136,6 +156,10 @@ class ServerTransformer extends TransformerAbstract */ public function includeOption(Server $server) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + return $this->item($server->option, new OptionTransformer($this->request), 'option'); } @@ -146,6 +170,10 @@ class ServerTransformer extends TransformerAbstract */ 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'); } @@ -156,6 +184,10 @@ class ServerTransformer extends TransformerAbstract */ public function includeLocation(Server $server) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + return $this->item($server->location, new LocationTransformer($this->request), 'location'); } @@ -166,6 +198,10 @@ class ServerTransformer extends TransformerAbstract */ public function includeNode(Server $server) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-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 index 3521e0840..835ba41be 100644 --- a/app/Transformers/Admin/ServerVariableTransformer.php +++ b/app/Transformers/Admin/ServerVariableTransformer.php @@ -76,6 +76,10 @@ class ServerVariableTransformer extends TransformerAbstract */ public function includeParent(ServerVariable $variable) { - return $this->item($variable->variable, new ServiceVariableTransformer, 'variable'); + if ($this->request && ! $this->request->apiKeyHasPermission('server-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 index 15e8859b2..61d544fa6 100644 --- a/app/Transformers/Admin/ServiceTransformer.php +++ b/app/Transformers/Admin/ServiceTransformer.php @@ -80,7 +80,11 @@ class ServiceTransformer extends TransformerAbstract */ public function includeOptions(Service $service) { - return $this->collection($service->options, new OptionTransformer, 'option'); + if ($this->request && ! $this->request->apiKeyHasPermission('service-view')) { + return; + } + + return $this->collection($service->options, new OptionTransformer($this->request), 'option'); } /** @@ -90,7 +94,11 @@ class ServiceTransformer extends TransformerAbstract */ public function includeServers(Service $service) { - return $this->collection($service->servers, new ServerTransformer, 'server'); + if ($this->request && ! $this->request->apiKeyHasPermission('service-view')) { + return; + } + + return $this->collection($service->servers, new ServerTransformer($this->request), 'server'); } /** @@ -100,6 +108,10 @@ class ServiceTransformer extends TransformerAbstract */ public function includePacks(Service $service) { - return $this->collection($service->packs, new PackTransformer, 'pack'); + if ($this->request && ! $this->request->apiKeyHasPermission('service-view')) { + 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 index 9f87a1a81..c9105f68f 100644 --- a/app/Transformers/Admin/ServiceVariableTransformer.php +++ b/app/Transformers/Admin/ServiceVariableTransformer.php @@ -76,6 +76,10 @@ class ServiceVariableTransformer extends TransformerAbstract */ public function includeVariables(ServiceVariable $variable) { - return $this->collection($variable->serverVariable, new ServerVariableTransformer, 'server_variable'); + if ($this->request && ! $this->request->apiKeyHasPermission('option-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 index ab95ceb27..129da7ad3 100644 --- a/app/Transformers/Admin/SubuserTransformer.php +++ b/app/Transformers/Admin/SubuserTransformer.php @@ -60,6 +60,10 @@ class SubuserTransformer extends TransformerAbstract */ public function transform(Subuser $subuser) { + if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) { + return; + } + return [ 'id' => $subuser->id, 'username' => $subuser->user->username, diff --git a/app/Transformers/Admin/UserTransformer.php b/app/Transformers/Admin/UserTransformer.php index 95e5bef09..5349aa144 100644 --- a/app/Transformers/Admin/UserTransformer.php +++ b/app/Transformers/Admin/UserTransformer.php @@ -30,6 +30,16 @@ 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. * @@ -61,4 +71,32 @@ class UserTransformer extends TransformerAbstract { 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('user-view')) { + 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('user-view')) { + return; + } + + return $this->collection($user->access()->get(), new ServerTransformer($this->request), 'server'); + } } diff --git a/routes/api-admin.php b/routes/api-admin.php index ae7de6f09..d62510c94 100644 --- a/routes/api-admin.php +++ b/routes/api-admin.php @@ -74,4 +74,26 @@ Route::group(['prefix' => '/locations'], function () { 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::delete('/{id}', 'UserController@delete'); });