Node and user API routes implemented.

More attempts at the logic for API permissions, most likely will need
continued tweaking in the future, but base is there.
This commit is contained in:
Dane Everitt 2017-04-09 15:31:10 -04:00
parent f24b238e30
commit 820d2bf172
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
15 changed files with 447 additions and 26 deletions

View file

@ -28,7 +28,10 @@ use Fractal;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Pterodactyl\Models\Node; use Pterodactyl\Models\Node;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\NodeRepository;
use Pterodactyl\Transformers\Admin\NodeTransformer; use Pterodactyl\Transformers\Admin\NodeTransformer;
use Pterodactyl\Exceptions\DisplayValidationException;
class NodeController extends Controller class NodeController extends Controller
{ {
@ -72,4 +75,93 @@ class NodeController extends Controller
->withResourceName('node') ->withResourceName('node')
->toArray(); ->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);
}
}
} }

View file

@ -0,0 +1,177 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* 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);
}
}
}

View file

@ -136,26 +136,26 @@ class UserController extends Controller
* Update a user. * Update a user.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param int $user * @param int $id
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function updateUser(Request $request, $user) public function updateUser(Request $request, $id)
{ {
try { try {
$repo = new UserRepository; $repo = new UserRepository;
$repo->update($user, $request->only([ $user = $repo->update($user, $request->intersect([
'email', 'password', 'name_first', 'email', 'password', 'name_first',
'name_last', 'username', 'root_admin', 'name_last', 'username', 'root_admin',
])); ]));
Alert::success('User account was successfully updated.')->flash(); Alert::success('User account was successfully updated.')->flash();
} catch (DisplayValidationException $ex) { } catch (DisplayValidationException $ex) {
return redirect()->route('admin.users.view', $user)->withErrors(json_decode($ex->getMessage())); return redirect()->route('admin.users.view', $id)->withErrors(json_decode($ex->getMessage()));
} catch (\Exception $e) { } catch (\Exception $ex) {
Log::error($e); Log::error($ex);
Alert::danger('An error occured while attempting to update this user.')->flash(); 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);
} }
/** /**

View file

@ -114,7 +114,7 @@ class UserRepository
* *
* @param int $id * @param int $id
* @param array $data * @param array $data
* @return bool * @return \Pterodactyl\Models\User
* *
* @throws \Pterodactyl\Exceptions\DisplayValidationException * @throws \Pterodactyl\Exceptions\DisplayValidationException
*/ */
@ -147,9 +147,9 @@ class UserRepository
unset($data['password']); unset($data['password']);
} }
$user->fill($data); $user->fill($data)->save();;
return $user->save(); return $user;
} }
/** /**

View file

@ -79,7 +79,11 @@ class LocationTransformer extends TransformerAbstract
*/ */
public function includeServers(Location $location) 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) 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');
} }
} }

View file

@ -84,7 +84,7 @@ class NodeTransformer extends TransformerAbstract
return; 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;
} }
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;
} }
return $this->collection($node->servers, new ServerTransformer, 'server'); return $this->collection($node->servers, new ServerTransformer($this->request), 'server');
} }
} }

View file

@ -81,7 +81,11 @@ class OptionTransformer extends TransformerAbstract
*/ */
public function includeService(ServiceOption $option) 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) 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) 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) 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');
} }
} }

View file

@ -83,7 +83,11 @@ class PackTransformer extends TransformerAbstract
*/ */
public function includeOption(Pack $pack) 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) 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');
} }
} }

View file

@ -86,6 +86,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeAllocations(Server $server) 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 $this->collection($server->allocations, new AllocationTransformer($this->request, 'server'), 'allocation');
} }
@ -96,6 +100,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeSubusers(Server $server) 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 $this->collection($server->subusers, new SubuserTransformer($this->request), 'subuser');
} }
@ -106,6 +114,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeUser(Server $server) public function includeUser(Server $server)
{ {
if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) {
return;
}
return $this->item($server->user, new UserTransformer($this->request), 'user'); return $this->item($server->user, new UserTransformer($this->request), 'user');
} }
@ -116,6 +128,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includePack(Server $server) public function includePack(Server $server)
{ {
if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) {
return;
}
return $this->item($server->pack, new PackTransformer($this->request), 'pack'); return $this->item($server->pack, new PackTransformer($this->request), 'pack');
} }
@ -126,6 +142,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeService(Server $server) public function includeService(Server $server)
{ {
if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) {
return;
}
return $this->item($server->service, new ServiceTransformer($this->request), 'service'); return $this->item($server->service, new ServiceTransformer($this->request), 'service');
} }
@ -136,6 +156,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeOption(Server $server) public function includeOption(Server $server)
{ {
if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) {
return;
}
return $this->item($server->option, new OptionTransformer($this->request), 'option'); return $this->item($server->option, new OptionTransformer($this->request), 'option');
} }
@ -146,6 +170,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeVariables(Server $server) 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 $this->collection($server->variables, new ServerVariableTransformer($this->request), 'server_variable');
} }
@ -156,6 +184,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeLocation(Server $server) public function includeLocation(Server $server)
{ {
if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) {
return;
}
return $this->item($server->location, new LocationTransformer($this->request), 'location'); return $this->item($server->location, new LocationTransformer($this->request), 'location');
} }
@ -166,6 +198,10 @@ class ServerTransformer extends TransformerAbstract
*/ */
public function includeNode(Server $server) public function includeNode(Server $server)
{ {
if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) {
return;
}
return $this->item($server->node, new NodeTransformer($this->request), 'node'); return $this->item($server->node, new NodeTransformer($this->request), 'node');
} }
} }

View file

@ -76,6 +76,10 @@ class ServerVariableTransformer extends TransformerAbstract
*/ */
public function includeParent(ServerVariable $variable) 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');
} }
} }

View file

@ -80,7 +80,11 @@ class ServiceTransformer extends TransformerAbstract
*/ */
public function includeOptions(Service $service) 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) 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) 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');
} }
} }

View file

@ -76,6 +76,10 @@ class ServiceVariableTransformer extends TransformerAbstract
*/ */
public function includeVariables(ServiceVariable $variable) 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');
} }
} }

View file

@ -60,6 +60,10 @@ class SubuserTransformer extends TransformerAbstract
*/ */
public function transform(Subuser $subuser) public function transform(Subuser $subuser)
{ {
if ($this->request && ! $this->request->apiKeyHasPermission('server-view')) {
return;
}
return [ return [
'id' => $subuser->id, 'id' => $subuser->id,
'username' => $subuser->user->username, 'username' => $subuser->user->username,

View file

@ -30,6 +30,16 @@ use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract class UserTransformer extends TransformerAbstract
{ {
/**
* List of resources that can be included.
*
* @var array
*/
protected $availableIncludes = [
'access',
'servers',
];
/** /**
* The Illuminate Request object if provided. * The Illuminate Request object if provided.
* *
@ -61,4 +71,32 @@ class UserTransformer extends TransformerAbstract
{ {
return $user->toArray(); 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');
}
} }

View file

@ -74,4 +74,26 @@ Route::group(['prefix' => '/locations'], function () {
Route::group(['prefix' => '/nodes'], function () { Route::group(['prefix' => '/nodes'], function () {
Route::get('/', 'NodeController@index'); Route::get('/', 'NodeController@index');
Route::get('/{id}', 'NodeController@view'); 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');
}); });