Skip to content

Commit bf9708f

Browse files
committed
Add permissions checking to API middleware list
1 parent 49379bd commit bf9708f

File tree

8 files changed

+206
-210
lines changed

8 files changed

+206
-210
lines changed

app/Contracts/Repository/ApiKeyRepositoryInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99

1010
namespace Pterodactyl\Contracts\Repository;
1111

12+
use Pterodactyl\Models\APIKey;
13+
1214
interface ApiKeyRepositoryInterface extends RepositoryInterface
1315
{
16+
/**
17+
* Load permissions for a key onto the model.
18+
*
19+
* @param \Pterodactyl\Models\APIKey $model
20+
* @param bool $refresh
21+
* @return \Pterodactyl\Models\APIKey
22+
*/
23+
public function loadPermissions(APIKey $model, bool $refresh = false): APIKey;
1424
}

app/Http/Kernel.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Pterodactyl\Http\Middleware\API\AuthenticateIPAccess;
2525
use Pterodactyl\Http\Middleware\Daemon\DaemonAuthenticate;
2626
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
27+
use Pterodactyl\Http\Middleware\API\HasPermissionToResource;
2728
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
2829
use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer;
2930
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
@@ -95,6 +96,9 @@ class Kernel extends HttpKernel
9596
'bindings' => SubstituteBindings::class,
9697
'recaptcha' => VerifyReCaptcha::class,
9798

99+
// API specific middleware.
100+
'api..user_level' => HasPermissionToResource::class,
101+
98102
// Server specific middleware (used for authenticating access to resources)
99103
//
100104
// These are only used for individual server authentication, and not gloabl
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Middleware\API;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
8+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
9+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
10+
11+
class HasPermissionToResource
12+
{
13+
/**
14+
* @var \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface
15+
*/
16+
private $repository;
17+
18+
/**
19+
* HasPermissionToResource constructor.
20+
*
21+
* @param \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface $repository
22+
*/
23+
public function __construct(ApiKeyRepositoryInterface $repository)
24+
{
25+
$this->repository = $repository;
26+
}
27+
28+
/**
29+
* Determine if an API key has permission to access the given route.
30+
*
31+
* @param \Illuminate\Http\Request $request
32+
* @param \Closure $next
33+
* @param string $role
34+
* @return mixed
35+
*/
36+
public function handle(Request $request, Closure $next, string $role = 'admin')
37+
{
38+
/** @var \Pterodactyl\Models\APIKey $model */
39+
$model = $request->attributes->get('api_key');
40+
41+
if ($role === 'admin' && ! $request->user()->root_admin) {
42+
throw new NotFoundHttpException;
43+
}
44+
45+
$this->repository->loadPermissions($model);
46+
$routeKey = str_replace(['api.', 'admin.'], '', $request->route()->getName());
47+
48+
$count = $model->getRelation('permissions')->filter(function ($permission) use ($routeKey) {
49+
return $routeKey === str_replace('-', '.', $permission->permission);
50+
})->count();
51+
52+
if ($count === 1) {
53+
return $next($request);
54+
}
55+
56+
throw new AccessDeniedHttpException('Cannot access resource without required `' . $routeKey . '` permission.');
57+
}
58+
}

app/Http/Middleware/HMACAuthorization.php

Lines changed: 0 additions & 209 deletions
This file was deleted.

app/Providers/RouteServiceProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function map()
4949
->namespace($this->namespace . '\Server')
5050
->group(base_path('routes/server.php'));
5151

52-
Route::middleware(['api'])->prefix('/api/admin')
52+
Route::middleware(['api', 'api..user_level:admin'])->prefix('/api/admin')
5353
->namespace($this->namespace . '\API\Admin')
5454
->group(base_path('routes/api-admin.php'));
5555

app/Repositories/Eloquent/ApiKeyRepository.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,20 @@ public function model()
2121
{
2222
return APIKey::class;
2323
}
24+
25+
/**
26+
* Load permissions for a key onto the model.
27+
*
28+
* @param \Pterodactyl\Models\APIKey $model
29+
* @param bool $refresh
30+
* @return \Pterodactyl\Models\APIKey
31+
*/
32+
public function loadPermissions(APIKey $model, bool $refresh = false): APIKey
33+
{
34+
if (! $model->relationLoaded('permissions') || $refresh) {
35+
$model->load('permissions');
36+
}
37+
38+
return $model;
39+
}
2440
}

database/factories/ModelFactory.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,11 @@
232232
'updated_at' => \Carbon\Carbon::now()->toDateTimeString(),
233233
];
234234
});
235+
236+
$factory->define(Pterodactyl\Models\APIPermission::class, function (Faker\Generator $faker) {
237+
return [
238+
'id' => $faker->unique()->randomNumber(),
239+
'key_id' => $faker->randomNumber(),
240+
'permission' => mb_strtolower($faker->word),
241+
];
242+
});

0 commit comments

Comments
 (0)