From 722fd614a147455b8cc67dff847f41529f8f561f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 9 Apr 2017 18:59:54 -0400 Subject: [PATCH] Add new dynamic view for creating API keys --- .../Controllers/API/User/CoreController.php | 2 +- .../Controllers/API/User/ServerController.php | 6 +- app/Http/Controllers/Base/APIController.php | 16 +- app/Models/APIPermission.php | 37 +- app/Policies/APIKeyPolicy.php | 5 + app/Repositories/APIRepository.php | 145 +++----- resources/lang/en/base.php | 262 ++++++++------ .../themes/pterodactyl/base/api/new.blade.php | 324 +++--------------- routes/api-admin.php | 2 + routes/base.php | 4 +- 10 files changed, 311 insertions(+), 492 deletions(-) diff --git a/app/Http/Controllers/API/User/CoreController.php b/app/Http/Controllers/API/User/CoreController.php index 926cb3a62..5c1d07406 100644 --- a/app/Http/Controllers/API/User/CoreController.php +++ b/app/Http/Controllers/API/User/CoreController.php @@ -39,7 +39,7 @@ class CoreController extends Controller */ public function index(Request $request) { - $this->authorize('user-server-list', $request->apiKey()); + $this->authorize('user.server-list', $request->apiKey()); $servers = $request->user()->access('service', 'node', 'allocation', 'option')->get(); diff --git a/app/Http/Controllers/API/User/ServerController.php b/app/Http/Controllers/API/User/ServerController.php index 32f18eb2f..4064ad8c1 100644 --- a/app/Http/Controllers/API/User/ServerController.php +++ b/app/Http/Controllers/API/User/ServerController.php @@ -43,7 +43,7 @@ class ServerController extends Controller */ public function index(Request $request, $uuid) { - $this->authorize('user-server-view', $request->apiKey()); + $this->authorize('user.server-view', $request->apiKey()); $server = Server::byUuid($uuid); $fractal = Fractal::create()->item($server); @@ -66,7 +66,7 @@ class ServerController extends Controller */ public function power(Request $request, $uuid) { - $this->authorize('user-server-power', $request->apiKey()); + $this->authorize('user.server-power', $request->apiKey()); $server = Server::byUuid($uuid); $request->user()->can('power-' . $request->input('action'), $server); @@ -86,7 +86,7 @@ class ServerController extends Controller */ public function command(Request $request, $uuid) { - $this->authorize('user-server-command', $request->apiKey()); + $this->authorize('user.server-command', $request->apiKey()); $server = Server::byUuid($uuid); $request->user()->can('send-command', $server); 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/Models/APIPermission.php b/app/Models/APIPermission.php index 7450392d8..67eec35be 100644 --- a/app/Models/APIPermission.php +++ b/app/Models/APIPermission.php @@ -77,16 +77,45 @@ class APIPermission extends Model // All other pemissions below are administrative actions. 'server' => [ 'list', - 'view', - 'delete', 'create', + 'view', 'edit-details', 'edit-container', + 'edit-build', + 'edit-startup', 'suspend', 'install', 'rebuild', - 'edit-build', - 'edit-startup', + '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', ], ]; diff --git a/app/Policies/APIKeyPolicy.php b/app/Policies/APIKeyPolicy.php index e946433e5..58b187b48 100644 --- a/app/Policies/APIKeyPolicy.php +++ b/app/Policies/APIKeyPolicy.php @@ -42,6 +42,11 @@ class APIKeyPolicy */ 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) { diff --git a/app/Repositories/APIRepository.php b/app/Repositories/APIRepository.php index ff2c15c87..561266c3f 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\Models\APIPermission as Permission; use Pterodactyl\Exceptions\DisplayException; 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/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/api-admin.php b/routes/api-admin.php index b4aff6c53..fcd0a0a70 100644 --- a/routes/api-admin.php +++ b/routes/api-admin.php @@ -95,6 +95,8 @@ Route::group(['prefix' => '/users'], function () { Route::post('/', 'UserController@store'); + Route::put('/{id}', 'UserController@update'); + Route::delete('/{id}', 'UserController@delete'); }); diff --git a/routes/base.php b/routes/base.php index cea722192..e63f5b43b 100644 --- a/routes/base.php +++ b/routes/base.php @@ -51,9 +51,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'); });