Skip to content

Commit cb945b1

Browse files
committed
Fix permissions handling; do not allow a subuser to assign permissions they do not have
1 parent 39f79a8 commit cb945b1

File tree

8 files changed

+137
-78
lines changed

8 files changed

+137
-78
lines changed

app/Http/Controllers/Api/Client/Servers/SubuserController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public function store(StoreSubuserRequest $request, Server $server)
9191
*/
9292
public function update(UpdateSubuserRequest $request, Server $server): array
9393
{
94-
$subuser = $request->subuser();
94+
$subuser = $request->endpointSubuser();
9595
$this->repository->update($subuser->id, [
9696
'permissions' => $this->getDefaultPermissions($request),
9797
]);
@@ -110,7 +110,7 @@ public function update(UpdateSubuserRequest $request, Server $server): array
110110
*/
111111
public function delete(DeleteSubuserRequest $request, Server $server)
112112
{
113-
$this->repository->delete($request->subuser()->id);
113+
$this->repository->delete($request->endpointSubuser()->id);
114114

115115
return JsonResponse::create([], JsonResponse::HTTP_NO_CONTENT);
116116
}

app/Http/Requests/Api/Client/Servers/Subusers/AbstractSubuserRequest.php

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

app/Http/Requests/Api/Client/Servers/Subusers/DeleteSubuserRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Pterodactyl\Models\Permission;
66

7-
class DeleteSubuserRequest extends AbstractSubuserRequest
7+
class DeleteSubuserRequest extends SubuserRequest
88
{
99
/**
1010
* @return string

app/Http/Requests/Api/Client/Servers/Subusers/GetSubuserRequest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Subusers;
44

55
use Pterodactyl\Models\Permission;
6-
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
76

8-
class GetSubuserRequest extends ClientApiRequest
7+
class GetSubuserRequest extends SubuserRequest
98
{
109
/**
1110
* Confirm that a user is able to view subusers for the specified server.

app/Http/Requests/Api/Client/Servers/Subusers/StoreSubuserRequest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Subusers;
44

55
use Pterodactyl\Models\Permission;
6-
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
76

8-
class StoreSubuserRequest extends ClientApiRequest
7+
class StoreSubuserRequest extends SubuserRequest
98
{
109
/**
1110
* @return string
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Subusers;
4+
5+
use Illuminate\Http\Request;
6+
use Pterodactyl\Models\Server;
7+
use Pterodactyl\Exceptions\Http\HttpForbiddenException;
8+
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
9+
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
10+
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
11+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
12+
13+
abstract class SubuserRequest extends ClientApiRequest
14+
{
15+
/**
16+
* @var \Pterodactyl\Models\Subuser|null
17+
*/
18+
protected $model;
19+
20+
/**
21+
* Authorize the request and ensure that a user is not trying to modify themselves.
22+
*
23+
* @return bool
24+
*/
25+
public function authorize(): bool
26+
{
27+
if (! parent::authorize()) {
28+
return false;
29+
}
30+
31+
// If there is a subuser present in the URL, validate that it is not the same as the
32+
// current request user. You're not allowed to modify yourself.
33+
if ($this->route()->hasParameter('subuser')) {
34+
if ($this->endpointSubuser()->user_id === $this->user()->id) {
35+
return false;
36+
}
37+
}
38+
39+
// If this is a POST request, validate that the user can even assign the permissions they
40+
// have selected to assign.
41+
if ($this->method() === Request::METHOD_POST && $this->has('permissions')) {
42+
$this->validatePermissionsCanBeAssigned(
43+
$this->input('permissions') ?? []
44+
);
45+
}
46+
47+
return true;
48+
}
49+
50+
/**
51+
* Validates that the permissions we are trying to assign can actually be assigned
52+
* by the user making the request.
53+
*
54+
* @param array $permissions
55+
*/
56+
protected function validatePermissionsCanBeAssigned(array $permissions)
57+
{
58+
$user = $this->user();
59+
/** @var \Pterodactyl\Models\Server $server */
60+
$server = $this->route()->parameter('server');
61+
62+
// If we are a root admin or the server owner, no need to perform these checks.
63+
if ($user->root_admin || $user->id === $server->owner_id) {
64+
return;
65+
}
66+
67+
// Otherwise, get the current subuser's permission set, and ensure that the
68+
// permissions they are trying to assign are not _more_ than the ones they
69+
// already have.
70+
if (count(array_diff($permissions, $this->currentUserPermissions())) > 0) {
71+
throw new HttpForbiddenException(
72+
'Cannot assign permissions to a subuser that your account does not actively possess.'
73+
);
74+
}
75+
}
76+
77+
/**
78+
* Returns the currently authenticated user's permissions.
79+
*
80+
* @return array
81+
*/
82+
public function currentUserPermissions(): array
83+
{
84+
/** @var \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository */
85+
$repository = $this->container->make(SubuserRepository::class);
86+
87+
/* @var \Pterodactyl\Models\Subuser $model */
88+
try {
89+
$model = $repository->findFirstWhere([
90+
['server_id', $this->route()->parameter('server')->id],
91+
['user_id' => $this->user()->id],
92+
]);
93+
} catch (RecordNotFoundException $exception) {
94+
return [];
95+
}
96+
97+
return $model->permissions;
98+
}
99+
100+
/**
101+
* Return the subuser model for the given request which can then be validated. If
102+
* required request parameters are missing a 404 error will be returned, otherwise
103+
* a model exception will be returned if the model is not found.
104+
*
105+
* This returns the subuser based on the endpoint being hit, not the actual subuser
106+
* for the account making the request.
107+
*
108+
* @return \Pterodactyl\Models\Subuser
109+
*
110+
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
111+
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
112+
*/
113+
public function endpointSubuser()
114+
{
115+
/** @var \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository */
116+
$repository = $this->container->make(SubuserRepository::class);
117+
118+
$parameters = $this->route()->parameters();
119+
if (
120+
! isset($parameters['server'], $parameters['server'])
121+
|| ! is_string($parameters['subuser'])
122+
|| ! $parameters['server'] instanceof Server
123+
) {
124+
throw new NotFoundHttpException;
125+
}
126+
127+
return $this->model ?: $this->model = $repository->getUserForServer(
128+
$parameters['server']->id, $parameters['subuser']
129+
);
130+
}
131+
}

app/Http/Requests/Api/Client/Servers/Subusers/UpdateSubuserRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Pterodactyl\Models\Permission;
66

7-
class UpdateSubuserRequest extends AbstractSubuserRequest
7+
class UpdateSubuserRequest extends SubuserRequest
88
{
99
/**
1010
* @return string

app/Services/Subusers/SubuserCreationService.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
11
<?php
2-
/**
3-
* Pterodactyl - Panel
4-
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5-
*
6-
* This software is licensed under the terms of the MIT license.
7-
* https://opensource.org/licenses/MIT
8-
*/
92

103
namespace Pterodactyl\Services\Subusers;
114

0 commit comments

Comments
 (0)