diff --git a/app/Http/Controllers/Admin/APIController.php b/app/Http/Controllers/Admin/APIController.php index 77b60adc0..ecee5adc1 100644 --- a/app/Http/Controllers/Admin/APIController.php +++ b/app/Http/Controllers/Admin/APIController.php @@ -4,9 +4,14 @@ namespace Pterodactyl\Http\Controllers\Admin; use Alert; use Log; -use Pterodactyl\Models; +use Pterodactyl\Models; +use Pterodactyl\Repositories\APIRepository; use Pterodactyl\Http\Controllers\Controller; + +use Pterodactyl\Exceptions\DisplayValidationException; +use Pterodactyl\Exceptions\DisplayException; + use Illuminate\Http\Request; class APIController extends Controller @@ -29,4 +34,40 @@ class APIController extends Controller ]); } + public function getNew(Request $request) + { + return view('admin.api.new'); + } + + public function postNew(Request $request) + { + try { + $api = new APIRepository; + $secret = $api->new($request->except(['_token'])); + Alert::info('An API Keypair has successfully been generated. The API secret for this public key is shown below and will not be shown again.

Secret: ' . $secret . '')->flash(); + return redirect()->route('admin.api'); + } catch (DisplayValidationException $ex) { + return redirect()->route('admin.api.new')->withErrors(json_decode($ex->getMessage()))->withInput(); + } catch (DisplayException $ex) { + Alert::danger($ex->getMessage())->flash(); + } catch (\Exception $ex) { + Log::error($ex); + Alert::danger('An unhandled exception occured while attempting to add this API key.')->flash(); + } + return redirect()->route('admin.api.new')->withInput(); + } + + public function deleteRevokeKey(Request $request, $key) + { + try { + $api = new APIRepository; + $api->revoke($key); + return response('', 204); + } catch (\Exception $ex) { + return response()->json([ + 'error' => 'An error occured while attempting to remove this key.' + ], 503); + } + } + } diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index 0eecf410c..5f45a25a1 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -235,9 +235,12 @@ class AdminRoutes { 'as' => 'admin.api.new', 'uses' => 'Admin\APIController@getNew' ]); + $router->post('/new', [ + 'uses' => 'Admin\APIController@postNew' + ]); $router->delete('/revoke/{key?}', [ 'as' => 'admin.api.revoke', - 'uses' => 'Admin\APIController@deleteKey' + 'uses' => 'Admin\APIController@deleteRevokeKey' ]); }); diff --git a/app/Models/APIPermission.php b/app/Models/APIPermission.php index 487951b17..3571a1c09 100644 --- a/app/Models/APIPermission.php +++ b/app/Models/APIPermission.php @@ -21,5 +21,11 @@ class APIPermission extends Model */ protected $guarded = ['id']; + /** + * Disable timestamps for this table. + * + * @var boolean + */ + public $timestamps = false; } diff --git a/app/Repositories/APIRepository.php b/app/Repositories/APIRepository.php new file mode 100644 index 000000000..92dd876e8 --- /dev/null +++ b/app/Repositories/APIRepository.php @@ -0,0 +1,151 @@ + 'required|array' + ]); + + $validator->after(function($validator) use ($data) { + if (array_key_exists('allowed_ips', $data) && !empty($data['allowed_ips'])) { + foreach(explode("\n", $data['allowed_ips']) as $ip) { + $ip = trim($ip); + try { + Network::parse($ip); + array_push($this->allowed, $ip); + } catch (\Exception $ex) { + $validator->errors()->add('allowed_ips', 'Could not parse IP <' . $ip . '> because it is in an invalid format.'); + } + } + } + }); + + // Run validator, throw catchable and displayable exception if it fails. + // Exception includes a JSON result of failed validation rules. + if ($validator->fails()) { + throw new DisplayValidationException($validator->errors()); + } + + DB::beginTransaction(); + + $key = new Models\APIKey; + $key->fill([ + 'public' => str_random(16), + 'secret' => str_random(16) . '.' . str_random(15), + 'allowed_ips' => empty($this->allowed) ? null : json_encode($this->allowed) + ]); + $key->save(); + + foreach($data['permissions'] as $permission) { + if (in_array($permission, $this->permissions)) { + $model = new Models\APIPermission; + $model->fill([ + 'key_id' => $key->id, + 'permission' => $permission + ]); + $model->save(); + } + } + + try { + DB::commit(); + return $key->secret; + } catch (\Exception $ex) { + throw $ex; + } + + } + + /** + * Revokes an API key and associated permissions. + * + * @param string $key The public key. + * + * @throws Illuminate\Database\Eloquent\ModelNotFoundException + * + * @return void + */ + public function revoke(string $key) + { + DB::beginTransaction(); + + $model = Models\APIKey::where('public', $key)->firstOrFail(); + $permissions = Models\APIPermission::where('key_id', $model->id)->delete(); + $model->delete(); + + DB::commit(); + } + +} diff --git a/public/css/pterodactyl.css b/public/css/pterodactyl.css index a8929e6be..6ec54d79e 100755 --- a/public/css/pterodactyl.css +++ b/public/css/pterodactyl.css @@ -112,6 +112,10 @@ li.btn.btn-default.pill:active,li.btn.btn-default.pill:focus,li.btn.btn-default. margin: 0 !important; } +.fuelux .checkbox.highlight.checked label, .fuelux .checkbox.highlight label { + width: 100%; +} + .btn-allocate-delete { height:34px; width:34px; diff --git a/resources/views/admin/api/index.blade.php b/resources/views/admin/api/index.blade.php index d6779c46a..a33114816 100644 --- a/resources/views/admin/api/index.blade.php +++ b/resources/views/admin/api/index.blade.php @@ -61,8 +61,10 @@ $(document).ready(function () { text: 'Once this API key is revoked any applications currently using it will stop working.', showCancelButton: true, allowOutsideClick: true, + closeOnConfirm: false, confirmButtonText: 'Revoke', confirmButtonColor: '#d9534f', + showLoaderOnConfirm: true }, function () { $.ajax({ method: 'DELETE', @@ -73,6 +75,8 @@ $(document).ready(function () { }).done(function (data) { swal({ type: 'success', + title: '', + text: 'API Key has been revoked.' }); self.parent().parent().slideUp(); }).fail(function (jqXHR) { diff --git a/resources/views/admin/api/new.blade.php b/resources/views/admin/api/new.blade.php index e69de29bb..85225bb4d 100644 --- a/resources/views/admin/api/new.blade.php +++ b/resources/views/admin/api/new.blade.php @@ -0,0 +1,182 @@ +@extends('layouts.admin') + +@section('title') + API Management +@endsection + +@section('content') +
+ +

Add New API Key


+
+
+
+
+
+
+
+
+
+

User Management


+
+
+
+
+
+
+
+
+
+
+
+
+

Server Management


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Node Management


+
+
+
+
+
+
+
+
+
+
+
+
+

Service Management


+
+
+
+
+

Location Management


+
+
+
+
+
+
+
+ +
+ +

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.

+
+
+
+
+
+ {!! csrf_field() !!} + +
+
+
+
+
+ +@endsection diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index 69ca6edfe..34184ffaf 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -106,7 +106,7 @@ @foreach ($messages as $message) @endforeach @endforeach diff --git a/resources/views/layouts/master.blade.php b/resources/views/layouts/master.blade.php index b71bd10c0..9df2f5c82 100644 --- a/resources/views/layouts/master.blade.php +++ b/resources/views/layouts/master.blade.php @@ -219,7 +219,7 @@ @foreach ($messages as $message) @endforeach @endforeach