diff --git a/app/Http/Controllers/Api/Client/Servers/ServerController.php b/app/Http/Controllers/Api/Client/Servers/ServerController.php index 4cf91416d..96e276324 100644 --- a/app/Http/Controllers/Api/Client/Servers/ServerController.php +++ b/app/Http/Controllers/Api/Client/Servers/ServerController.php @@ -5,7 +5,7 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Pterodactyl\Models\Server; use Pterodactyl\Repositories\Eloquent\SubuserRepository; use Pterodactyl\Transformers\Api\Client\ServerTransformer; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; +use Pterodactyl\Services\Servers\GetUserPermissionsService; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest; @@ -16,16 +16,23 @@ class ServerController extends ClientApiController */ private $repository; + /** + * @var \Pterodactyl\Services\Servers\GetUserPermissionsService + */ + private $permissionsService; + /** * ServerController constructor. * + * @param \Pterodactyl\Services\Servers\GetUserPermissionsService $permissionsService * @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository */ - public function __construct(SubuserRepository $repository) + public function __construct(GetUserPermissionsService $permissionsService, SubuserRepository $repository) { parent::__construct(); $this->repository = $repository; + $this->permissionsService = $permissionsService; } /** @@ -38,20 +45,11 @@ class ServerController extends ClientApiController */ public function index(GetServerRequest $request, Server $server): array { - try { - $permissions = $this->repository->findFirstWhere([ - 'server_id' => $server->id, - 'user_id' => $request->user()->id, - ])->permissions; - } catch (RecordNotFoundException $exception) { - $permissions = []; - } - return $this->fractal->item($server) ->transformWith($this->getTransformer(ServerTransformer::class)) ->addMeta([ 'is_server_owner' => $request->user()->id === $server->owner_id, - 'user_permissions' => $permissions, + 'user_permissions' => $this->permissionsService->handle($server, $request->user()), ]) ->toArray(); } diff --git a/app/Http/Controllers/Api/Client/Servers/SubuserController.php b/app/Http/Controllers/Api/Client/Servers/SubuserController.php index ccfea5e65..05e82358a 100644 --- a/app/Http/Controllers/Api/Client/Servers/SubuserController.php +++ b/app/Http/Controllers/Api/Client/Servers/SubuserController.php @@ -123,6 +123,6 @@ class SubuserController extends ClientApiController */ protected function getDefaultPermissions(Request $request): array { - return array_unique(array_merge($request->input('permissions') ?? [], ['websocket.*'])); + return array_unique(array_merge($request->input('permissions') ?? [], ['websocket.connect'])); } } diff --git a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php index 11d03b382..7cbe31631 100644 --- a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php +++ b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php @@ -5,13 +5,13 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Carbon\CarbonImmutable; use Illuminate\Http\Response; use Pterodactyl\Models\Server; -use Pterodactyl\Models\Subuser; use Illuminate\Http\JsonResponse; use Pterodactyl\Models\Permission; use Illuminate\Contracts\Cache\Repository; use Pterodactyl\Services\Nodes\NodeJWTService; use Symfony\Component\HttpKernel\Exception\HttpException; use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest; +use Pterodactyl\Services\Servers\GetUserPermissionsService; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; class WebsocketController extends ClientApiController @@ -26,18 +26,28 @@ class WebsocketController extends ClientApiController */ private $jwtService; + /** + * @var \Pterodactyl\Services\Servers\GetUserPermissionsService + */ + private $permissionsService; + /** * WebsocketController constructor. * * @param \Pterodactyl\Services\Nodes\NodeJWTService $jwtService + * @param \Pterodactyl\Services\Servers\GetUserPermissionsService $permissionsService * @param \Illuminate\Contracts\Cache\Repository $cache */ - public function __construct(NodeJWTService $jwtService, Repository $cache) - { + public function __construct( + NodeJWTService $jwtService, + GetUserPermissionsService $permissionsService, + Repository $cache + ) { parent::__construct(); $this->cache = $cache; $this->jwtService = $jwtService; + $this->permissionsService = $permissionsService; } /** @@ -53,32 +63,16 @@ class WebsocketController extends ClientApiController public function __invoke(ClientApiRequest $request, Server $server) { $user = $request->user(); - if ($user->cannot(Permission::ACTION_WEBSOCKET, $server)) { + if ($user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $server)) { throw new HttpException(Response::HTTP_FORBIDDEN, 'You do not have permission to connect to this server\'s websocket.'); } - if ($user->root_admin || $user->id === $server->owner_id) { - $permissions = ['*']; - - if ($user->root_admin) { - $permissions[] = 'admin.errors'; - $permissions[] = 'admin.install'; - } - } else { - /** @var \Pterodactyl\Models\Subuser|null $subuserPermissions */ - $subuserPermissions = $server->subusers->first(function (Subuser $subuser) use ($user) { - return $subuser->user_id === $user->id; - }); - - $permissions = $subuserPermissions ? $subuserPermissions->permissions : []; - } - $token = $this->jwtService ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) ->setClaims([ 'user_id' => $request->user()->id, 'server_uuid' => $server->uuid, - 'permissions' => $permissions ?? [], + 'permissions' => $this->permissionsService->handle($server, $user), ]) ->handle($server->node, $user->id . $server->uuid); diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 15d70c4ee..772ae5e49 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -15,7 +15,7 @@ class Permission extends Model /** * Constants defining different permissions available. */ - const ACTION_WEBSOCKET = 'websocket.*'; + const ACTION_WEBSOCKET_CONNECT = 'websocket.connect'; const ACTION_CONTROL_CONSOLE = 'control.console'; const ACTION_CONTROL_START = 'control.start'; const ACTION_CONTROL_STOP = 'control.stop'; @@ -105,7 +105,7 @@ class Permission extends Model 'websocket' => [ 'description' => 'Allows the user to connect to the server websocket, giving them access to view console output and realtime server stats.', 'keys' => [ - '*' => 'Gives user full read access to the websocket.', + 'connect' => 'Allows a user to connect to the websocket instance for a server to stream the console.', ], ], diff --git a/app/Services/Servers/GetUserPermissionsService.php b/app/Services/Servers/GetUserPermissionsService.php new file mode 100644 index 000000000..98dcf6c34 --- /dev/null +++ b/app/Services/Servers/GetUserPermissionsService.php @@ -0,0 +1,37 @@ +root_admin || $user->id === $server->owner_id) { + $permissions = ['*']; + + if ($user->root_admin) { + $permissions[] = 'admin.websocket.errors'; + $permissions[] = 'admin.websocket.install'; + } + + return $permissions; + } + + /** @var \Pterodactyl\Models\Subuser|null $subuserPermissions */ + $subuserPermissions = $server->subusers->where('user_id', $user->id)->first(); + + return $subuserPermissions ? $subuserPermissions->permissions : []; + } +} diff --git a/resources/scripts/components/server/users/UserRow.tsx b/resources/scripts/components/server/users/UserRow.tsx index d12b7aef1..0f2260d2a 100644 --- a/resources/scripts/components/server/users/UserRow.tsx +++ b/resources/scripts/components/server/users/UserRow.tsx @@ -47,7 +47,7 @@ export default ({ subuser }: Props) => {

- {subuser.permissions.filter(permission => permission !== 'websocket.*').length} + {subuser.permissions.filter(permission => permission !== 'websocket.connect').length}

Permissions

diff --git a/resources/scripts/plugins/usePermissions.ts b/resources/scripts/plugins/usePermissions.ts index 89dbd64bc..1b383114c 100644 --- a/resources/scripts/plugins/usePermissions.ts +++ b/resources/scripts/plugins/usePermissions.ts @@ -15,7 +15,6 @@ export const usePermissions = (action: string | string[]): boolean[] => { // will return if the user has any permission under the file.XYZ namespace. ( permission.endsWith('.*') && - permission !== 'websocket.*' && userPermissions.filter(p => p.startsWith(permission.split('.')[0])).length > 0 ) || // Otherwise just check if the entire permission exists in the array or not. diff --git a/resources/scripts/state/server/subusers.ts b/resources/scripts/state/server/subusers.ts index 5a6224853..7f8a6ef06 100644 --- a/resources/scripts/state/server/subusers.ts +++ b/resources/scripts/state/server/subusers.ts @@ -1,7 +1,7 @@ import { action, Action } from 'easy-peasy'; export type SubuserPermission = - 'websocket.*' | + 'websocket.connect' | 'control.console' | 'control.start' | 'control.stop' | 'control.restart' | 'user.create' | 'user.read' | 'user.update' | 'user.delete' | 'file.create' | 'file.read' | 'file.update' | 'file.delete' | 'file.archive' | 'file.sftp' |