Skip to content

Commit 4ee9d38

Browse files
committed
Add ApiKey service, cleanup old API key methods
https://zube.io/pterodactyl/panel/c/525
1 parent 2235481 commit 4ee9d38

File tree

10 files changed

+569
-85
lines changed

10 files changed

+569
-85
lines changed

app/Http/Controllers/Base/APIController.php

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,48 @@
2525

2626
namespace Pterodactyl\Http\Controllers\Base;
2727

28-
use Log;
29-
use Alert;
3028
use Illuminate\Http\Request;
3129
use Pterodactyl\Models\APIKey;
30+
use Prologue\Alerts\AlertsMessageBag;
3231
use Pterodactyl\Models\APIPermission;
33-
use Pterodactyl\Repositories\APIRepository;
34-
use Pterodactyl\Exceptions\DisplayException;
32+
use Pterodactyl\Services\ApiKeyService;
3533
use Pterodactyl\Http\Controllers\Controller;
36-
use Pterodactyl\Exceptions\DisplayValidationException;
34+
use Pterodactyl\Http\Requests\ApiKeyRequest;
3735

3836
class APIController extends Controller
3937
{
38+
/**
39+
* @var \Prologue\Alerts\AlertsMessageBag
40+
*/
41+
protected $alert;
42+
43+
/**
44+
* @var \Pterodactyl\Models\APIKey
45+
*/
46+
protected $model;
47+
48+
/**
49+
* @var \Pterodactyl\Services\ApiKeyService
50+
*/
51+
protected $service;
52+
53+
/**
54+
* APIController constructor.
55+
*
56+
* @param \Prologue\Alerts\AlertsMessageBag $alert
57+
* @param \Pterodactyl\Services\ApiKeyService $service
58+
*/
59+
public function __construct(AlertsMessageBag $alert, ApiKeyService $service, APIKey $model)
60+
{
61+
$this->alert = $alert;
62+
$this->model = $model;
63+
$this->service = $service;
64+
}
65+
4066
/**
4167
* Display base API index page.
4268
*
43-
* @param \Illuminate\Http\Request $request
69+
* @param \Illuminate\Http\Request $request
4470
* @return \Illuminate\View\View
4571
*/
4672
public function index(Request $request)
@@ -53,68 +79,61 @@ public function index(Request $request)
5379
/**
5480
* Display API key creation page.
5581
*
56-
* @param \Illuminate\Http\Request $request
5782
* @return \Illuminate\View\View
5883
*/
59-
public function create(Request $request)
84+
public function create()
6085
{
6186
return view('base.api.new', [
6287
'permissions' => [
63-
'user' => collect(APIPermission::permissions())->pull('_user'),
64-
'admin' => collect(APIPermission::permissions())->except('_user')->toArray(),
88+
'user' => collect(APIPermission::PERMISSIONS)->pull('_user'),
89+
'admin' => collect(APIPermission::PERMISSIONS)->except('_user')->toArray(),
6590
],
6691
]);
6792
}
6893

6994
/**
7095
* Handle saving new API key.
7196
*
72-
* @param \Illuminate\Http\Request $request
97+
* @param \Pterodactyl\Http\Requests\ApiKeyRequest $request
7398
* @return \Illuminate\Http\RedirectResponse
99+
*
100+
* @throws \Exception
101+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
74102
*/
75-
public function store(Request $request)
103+
public function store(ApiKeyRequest $request)
76104
{
77-
try {
78-
$repo = new APIRepository($request->user());
79-
$secret = $repo->create($request->intersect([
80-
'memo', 'allowed_ips',
81-
'admin_permissions', 'permissions',
82-
]));
83-
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.<br /><br /><code>' . $secret . '</code>')->flash();
84-
85-
return redirect()->route('account.api');
86-
} catch (DisplayValidationException $ex) {
87-
return redirect()->route('account.api.new')->withErrors(json_decode($ex->getMessage()))->withInput();
88-
} catch (DisplayException $ex) {
89-
Alert::danger($ex->getMessage())->flash();
90-
} catch (\Exception $ex) {
91-
Log::error($ex);
92-
Alert::danger('An unhandled exception occured while attempting to add this API key.')->flash();
105+
$adminPermissions = [];
106+
if ($request->user()->isRootAdmin()) {
107+
$adminPermissions = $request->input('admin_permissions') ?? [];
93108
}
94109

95-
return redirect()->route('account.api.new')->withInput();
110+
$secret = $this->service->create([
111+
'user_id' => $request->user()->id,
112+
'allowed_ips' => $request->input('allowed_ips'),
113+
'memo' => $request->input('memo'),
114+
], $request->input('permissions') ?? [], $adminPermissions);
115+
116+
$this->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.<br /><br /><code>' . $secret . '</code>')->flash();
117+
118+
return redirect()->route('account.api');
96119
}
97120

98121
/**
99-
* Handle revoking API key.
100-
*
101122
* @param \Illuminate\Http\Request $request
102123
* @param string $key
103-
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
124+
* @return \Illuminate\Http\Response
125+
*
126+
* @throws \Exception
104127
*/
105128
public function revoke(Request $request, $key)
106129
{
107-
try {
108-
$repo = new APIRepository($request->user());
109-
$repo->revoke($key);
130+
$key = $this->model->newQuery()
131+
->where('user_id', $request->user()->id)
132+
->where('public', $key)
133+
->firstOrFail();
110134

111-
return response('', 204);
112-
} catch (\Exception $ex) {
113-
Log::error($ex);
135+
$this->service->revoke($key);
114136

115-
return response()->json([
116-
'error' => 'An error occured while attempting to remove this key.',
117-
], 503);
118-
}
137+
return response('', 204);
119138
}
120139
}

app/Http/Requests/Admin/AdminFormRequest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
namespace Pterodactyl\Http\Requests\Admin;
2626

27-
use Pterodactyl\Models\User;
2827
use Illuminate\Foundation\Http\FormRequest;
2928

3029
abstract class AdminFormRequest extends FormRequest
@@ -37,7 +36,7 @@ abstract class AdminFormRequest extends FormRequest
3736
*/
3837
public function authorize()
3938
{
40-
if (! $this->user() instanceof User) {
39+
if (is_null($this->user())) {
4140
return false;
4241
}
4342

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
/*
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
namespace Pterodactyl\Http\Requests;
26+
27+
use IPTools\Network;
28+
29+
class ApiKeyRequest extends BaseFormRequest
30+
{
31+
/**
32+
* Rules applied to data passed in this request.
33+
*
34+
* @return array
35+
*/
36+
public function rules()
37+
{
38+
$this->parseAllowedIntoArray();
39+
40+
return [
41+
'memo' => 'required|nullable|string|max:500',
42+
'permissions' => 'sometimes|present|array',
43+
'admin_permissions' => 'sometimes|present|array',
44+
'allowed_ips' => 'present',
45+
'allowed_ips.*' => 'sometimes|string',
46+
];
47+
}
48+
49+
/**
50+
* Parse the string of allowed IPs into an array.
51+
*/
52+
protected function parseAllowedIntoArray()
53+
{
54+
$loop = [];
55+
if (! empty($this->input('allowed_ips'))) {
56+
foreach (explode(PHP_EOL, $this->input('allowed_ips')) as $ip) {
57+
$loop[] = trim($ip);
58+
}
59+
}
60+
61+
$this->merge(['allowed_ips' => $loop], $this->except('allowed_ips'));
62+
}
63+
64+
/**
65+
* Run additional validation rules on the request to ensure all of the data is good.
66+
*
67+
* @param \Illuminate\Validation\Validator $validator
68+
*/
69+
public function withValidator($validator)
70+
{
71+
$validator->after(function ($validator) {
72+
if (empty($this->input('permissions')) && empty($this->input('admin_permissions'))) {
73+
$validator->errors()->add('permissions', 'At least one permission must be selected.');
74+
}
75+
});
76+
77+
$validator->after(function ($validator) {
78+
foreach ($this->input('allowed_ips') as $ip) {
79+
$ip = trim($ip);
80+
81+
try {
82+
Network::parse($ip);
83+
} catch (\Exception $ex) {
84+
$validator->errors()->add('allowed_ips', 'Could not parse IP ' . $ip . ' because it is in an invalid format.');
85+
}
86+
}
87+
});
88+
}
89+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/*
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
namespace Pterodactyl\Http\Requests;
26+
27+
use Illuminate\Foundation\Http\FormRequest;
28+
29+
class BaseFormRequest extends FormRequest
30+
{
31+
/**
32+
* Determine if a user is authorized to access this endpoint.
33+
*
34+
* @return bool
35+
*/
36+
public function authorize()
37+
{
38+
return ! is_null($this->user());
39+
}
40+
41+
/**
42+
* Return only the fields that we are interested in from the request.
43+
* This will include empty fields as a null value.
44+
*
45+
* @return array
46+
*/
47+
public function normalize()
48+
{
49+
return $this->only(
50+
array_keys($this->rules())
51+
);
52+
}
53+
}

app/Models/APIKey.php

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,14 @@
2424

2525
namespace Pterodactyl\Models;
2626

27+
use Sofa\Eloquence\Eloquence;
28+
use Sofa\Eloquence\Validable;
2729
use Illuminate\Database\Eloquent\Model;
30+
use Sofa\Eloquence\Contracts\Validable as ValidableContract;
2831

29-
class APIKey extends Model
32+
class APIKey extends Model implements ValidableContract
3033
{
31-
/**
32-
* Public key defined length used in verification methods.
33-
*
34-
* @var int
35-
*/
36-
const PUBLIC_KEY_LEN = 16;
34+
use Eloquence, Validable;
3735

3836
/**
3937
* The table associated with the model.
@@ -65,6 +63,32 @@ class APIKey extends Model
6563
*/
6664
protected $guarded = ['id', 'created_at', 'updated_at'];
6765

66+
/**
67+
* Rules defining what fields must be passed when making a model.
68+
*
69+
* @var array
70+
*/
71+
protected static $applicationRules = [
72+
'memo' => 'required',
73+
'user_id' => 'required',
74+
'secret' => 'required',
75+
'public' => 'required',
76+
];
77+
78+
/**
79+
* Rules to protect aganist invalid data entry to DB.
80+
*
81+
* @var array
82+
*/
83+
protected static $dataIntegrityRules = [
84+
'user_id' => 'exists:users,id',
85+
'public' => 'string|size:16',
86+
'secret' => 'string',
87+
'memo' => 'nullable|string|max:500',
88+
'allowed_ips' => 'nullable|json',
89+
'expires_at' => 'nullable|datetime',
90+
];
91+
6892
/**
6993
* Gets the permissions associated with a key.
7094
*

0 commit comments

Comments
 (0)