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')
+