diff --git a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php index 7d68b5a3..dd12ce8c 100644 --- a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php +++ b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php @@ -45,7 +45,7 @@ class WebsocketController extends ClientApiController */ public function __invoke(Request $request, Server $server) { - if (! $request->user()->can('connect-to-ws', $server)) { + if (! $request->user()->can('websocket.*', $server)) { throw new HttpException( Response::HTTP_FORBIDDEN, 'You do not have permission to connect to this server\'s websocket.' ); diff --git a/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php b/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php index b43dd0d5..6d3efafb 100644 --- a/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Databases/DeleteDatabaseRequest.php @@ -14,7 +14,7 @@ class DeleteDatabaseRequest extends ClientApiRequest implements ClientPermission */ public function permission(): string { - return 'delete-database'; + return 'database.delete'; } /** diff --git a/app/Http/Requests/Api/Client/Servers/Databases/GetDatabasesRequest.php b/app/Http/Requests/Api/Client/Servers/Databases/GetDatabasesRequest.php index 0e90d80b..50610d74 100644 --- a/app/Http/Requests/Api/Client/Servers/Databases/GetDatabasesRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Databases/GetDatabasesRequest.php @@ -12,6 +12,6 @@ class GetDatabasesRequest extends ClientApiRequest implements ClientPermissionsR */ public function permission(): string { - return 'view-databases'; + return 'database.read'; } } diff --git a/app/Http/Requests/Api/Client/Servers/Databases/RotatePasswordRequest.php b/app/Http/Requests/Api/Client/Servers/Databases/RotatePasswordRequest.php index 424e3460..e07a476b 100644 --- a/app/Http/Requests/Api/Client/Servers/Databases/RotatePasswordRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Databases/RotatePasswordRequest.php @@ -14,6 +14,6 @@ class RotatePasswordRequest extends ClientApiRequest */ public function authorize(): bool { - return $this->user()->can('reset-db-password', $this->getModel(Server::class)); + return $this->user()->can('database.update', $this->getModel(Server::class)); } } diff --git a/app/Http/Requests/Api/Client/Servers/Databases/StoreDatabaseRequest.php b/app/Http/Requests/Api/Client/Servers/Databases/StoreDatabaseRequest.php index d8fc7849..1f8e18c3 100644 --- a/app/Http/Requests/Api/Client/Servers/Databases/StoreDatabaseRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Databases/StoreDatabaseRequest.php @@ -12,7 +12,7 @@ class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissions */ public function permission(): string { - return 'create-database'; + return 'database.create'; } /** diff --git a/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php index 4382dd01..416eaa27 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/CopyFileRequest.php @@ -12,7 +12,7 @@ class CopyFileRequest extends ClientApiRequest implements ClientPermissionsReque */ public function permission(): string { - return 'copy-files'; + return 'file.create'; } /** diff --git a/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php b/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php index 71ec0f94..ea4354de 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/CreateFolderRequest.php @@ -14,7 +14,7 @@ class CreateFolderRequest extends ClientApiRequest */ public function authorize(): bool { - return $this->user()->can('create-files', $this->getModel(Server::class)); + return $this->user()->can('file.create', $this->getModel(Server::class)); } /** diff --git a/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php index 62820b42..3c94fd7e 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/DeleteFileRequest.php @@ -12,7 +12,7 @@ class DeleteFileRequest extends ClientApiRequest implements ClientPermissionsReq */ public function permission(): string { - return 'delete-files'; + return 'file.delete'; } /** diff --git a/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php index a67a6efb..0eb9791c 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/DownloadFileRequest.php @@ -15,6 +15,6 @@ class DownloadFileRequest extends ClientApiRequest */ public function authorize(): bool { - return $this->user()->can('download-files', $this->getModel(Server::class)); + return $this->user()->can('file.read', $this->getModel(Server::class)); } } diff --git a/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php index 7c596c8d..79484d77 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php @@ -16,7 +16,7 @@ class GetFileContentsRequest extends ClientApiRequest implements ClientPermissio */ public function permission(): string { - return 'edit-files'; + return 'file.read'; } /** diff --git a/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php b/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php index 306447cd..2342360d 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/ListFilesRequest.php @@ -15,7 +15,7 @@ class ListFilesRequest extends ClientApiRequest */ public function authorize(): bool { - return $this->user()->can('list-files', $this->getModel(Server::class)); + return $this->user()->can('file.read', $this->getModel(Server::class)); } /** diff --git a/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php b/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php index a1c39945..6aa29be6 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/RenameFileRequest.php @@ -15,7 +15,7 @@ class RenameFileRequest extends ClientApiRequest implements ClientPermissionsReq */ public function permission(): string { - return 'move-files'; + return 'file.update'; } /** diff --git a/app/Http/Requests/Api/Client/Servers/Files/WriteFileContentRequest.php b/app/Http/Requests/Api/Client/Servers/Files/WriteFileContentRequest.php index 31404938..b4de3d93 100644 --- a/app/Http/Requests/Api/Client/Servers/Files/WriteFileContentRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Files/WriteFileContentRequest.php @@ -16,7 +16,7 @@ class WriteFileContentRequest extends ClientApiRequest implements ClientPermissi */ public function permission(): string { - return 'save-files'; + return 'file.create'; } /** diff --git a/app/Http/Requests/Api/Client/Servers/Network/GetNetworkRequest.php b/app/Http/Requests/Api/Client/Servers/Network/GetNetworkRequest.php index 14fd1833..6da73b59 100644 --- a/app/Http/Requests/Api/Client/Servers/Network/GetNetworkRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Network/GetNetworkRequest.php @@ -15,6 +15,6 @@ class GetNetworkRequest extends ClientApiRequest */ public function authorize(): bool { - return $this->user()->can('view-allocations', $this->getModel(Server::class)); + return $this->user()->can('allocation.read', $this->getModel(Server::class)); } } diff --git a/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php b/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php index 22feb2af..966363ae 100644 --- a/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php +++ b/app/Http/Requests/Api/Client/Servers/SendCommandRequest.php @@ -13,7 +13,7 @@ class SendCommandRequest extends GetServerRequest */ public function authorize(): bool { - return $this->user()->can('send-command', $this->getModel(Server::class)); + return $this->user()->can('control.console', $this->getModel(Server::class)); } /** diff --git a/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php index 7a32c117..1f0bff48 100644 --- a/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php +++ b/app/Http/Requests/Api/Client/Servers/SendPowerRequest.php @@ -14,7 +14,7 @@ class SendPowerRequest extends ClientApiRequest */ public function authorize(): bool { - return $this->user()->can('power-' . $this->input('signal', '_undefined'), $this->getModel(Server::class)); + return $this->user()->can('control.' . $this->input('signal', ''), $this->getModel(Server::class)); } /** diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/GetSubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/GetSubuserRequest.php index 810afdfb..729d6d0d 100644 --- a/app/Http/Requests/Api/Client/Servers/Subusers/GetSubuserRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Subusers/GetSubuserRequest.php @@ -13,6 +13,6 @@ class GetSubuserRequest extends ClientApiRequest */ public function authorize(): bool { - return $this->user()->can('view-subusers', $this->route()->parameter('server')); + return $this->user()->can('user.read', $this->route()->parameter('server')); } } diff --git a/app/Http/Requests/Server/Database/DeleteServerDatabaseRequest.php b/app/Http/Requests/Server/Database/DeleteServerDatabaseRequest.php deleted file mode 100644 index eed54e2e..00000000 --- a/app/Http/Requests/Server/Database/DeleteServerDatabaseRequest.php +++ /dev/null @@ -1,40 +0,0 @@ - 'required|string|min:1', - 'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/', - ]; - } -} diff --git a/app/Http/Requests/Server/ScheduleCreationFormRequest.php b/app/Http/Requests/Server/ScheduleCreationFormRequest.php deleted file mode 100644 index 6291d3cb..00000000 --- a/app/Http/Requests/Server/ScheduleCreationFormRequest.php +++ /dev/null @@ -1,79 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Server; - -class ScheduleCreationFormRequest extends ServerFormRequest -{ - /** - * Permission to validate this request against. - * - * @return string - */ - protected function permission(): string - { - if ($this->method() === 'PATCH') { - return 'edit-schedule'; - } - - return 'create-schedule'; - } - - /** - * Validation rules to apply to the request. - * - * @return array - */ - public function rules() - { - return [ - 'name' => 'nullable|string|max:255', - 'cron_day_of_week' => 'required|string', - 'cron_day_of_month' => 'required|string', - 'cron_hour' => 'required|string', - 'cron_minute' => 'required|string', - 'tasks' => 'sometimes|array|size:4', - 'tasks.time_value' => 'required_with:tasks|max:5', - 'tasks.time_interval' => 'required_with:tasks|max:5', - 'tasks.action' => 'required_with:tasks|max:5', - 'tasks.payload' => 'required_with:tasks|max:5', - 'tasks.time_value.*' => 'numeric|between:0,59', - 'tasks.time_interval.*' => 'string|in:s,m', - 'tasks.action.*' => 'string|in:power,command', - 'tasks.payload.*' => 'string', - ]; - } - - /** - * Normalize the request into a format that can be used by the application. - * - * @return array - */ - public function normalize() - { - return $this->only('name', 'cron_day_of_week', 'cron_day_of_month', 'cron_hour', 'cron_minute'); - } - - /** - * Return the tasks provided in the request that are associated with this schedule. - * - * @return array|null - */ - public function getTasks() - { - $restructured = []; - foreach (array_get($this->all(), 'tasks', []) as $key => $values) { - for ($i = 0; $i < count($values); $i++) { - $restructured[$i][$key] = $values[$i]; - } - } - - return empty($restructured) ? null : $restructured; - } -} diff --git a/app/Http/Requests/Server/ServerFormRequest.php b/app/Http/Requests/Server/ServerFormRequest.php deleted file mode 100644 index c0ca370b..00000000 --- a/app/Http/Requests/Server/ServerFormRequest.php +++ /dev/null @@ -1,35 +0,0 @@ -user()->can($this->permission(), $this->getServer()); - } - - public function getServer(): Server - { - return $this->attributes->get('server'); - } -} diff --git a/app/Http/Requests/Server/Settings/ChangeServerNameRequest.php b/app/Http/Requests/Server/Settings/ChangeServerNameRequest.php deleted file mode 100644 index 6ea8d21c..00000000 --- a/app/Http/Requests/Server/Settings/ChangeServerNameRequest.php +++ /dev/null @@ -1,31 +0,0 @@ - Server::getRules()['name'], - ]; - } -} diff --git a/app/Http/Requests/Server/Subuser/SubuserStoreFormRequest.php b/app/Http/Requests/Server/Subuser/SubuserStoreFormRequest.php deleted file mode 100644 index 9b7c6ce4..00000000 --- a/app/Http/Requests/Server/Subuser/SubuserStoreFormRequest.php +++ /dev/null @@ -1,31 +0,0 @@ - 'required|email', - 'permissions' => 'sometimes|array', - ]; - } -} diff --git a/app/Http/Requests/Server/Subuser/SubuserUpdateFormRequest.php b/app/Http/Requests/Server/Subuser/SubuserUpdateFormRequest.php deleted file mode 100644 index 7ff82abc..00000000 --- a/app/Http/Requests/Server/Subuser/SubuserUpdateFormRequest.php +++ /dev/null @@ -1,30 +0,0 @@ - 'present|array', - ]; - } -} diff --git a/app/Http/Requests/Server/UpdateFileContentsFormRequest.php b/app/Http/Requests/Server/UpdateFileContentsFormRequest.php deleted file mode 100644 index 24f67199..00000000 --- a/app/Http/Requests/Server/UpdateFileContentsFormRequest.php +++ /dev/null @@ -1,101 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Http\Requests\Server; - -use GuzzleHttp\Exception\RequestException; -use Illuminate\Contracts\Config\Repository; -use Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; -use Pterodactyl\Exceptions\Http\Server\FileTypeNotEditableException; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; - -class UpdateFileContentsFormRequest extends ServerFormRequest -{ - /** - * Return the permission string to validate this request against. - * - * @return string - */ - protected function permission(): string - { - return 'edit-files'; - } - - /** - * Authorize a request to edit a file. - * - * @return bool - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException - * @throws \Pterodactyl\Exceptions\Http\Server\FileTypeNotEditableException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function authorize() - { - if (! parent::authorize()) { - return false; - } - - $server = $this->attributes->get('server'); - $token = $this->attributes->get('server_token'); - - return $this->checkFileCanBeEdited($server, $token); - } - - /** - * @return array - */ - public function rules() - { - return []; - } - - /** - * Checks if a given file can be edited by a user on this server. - * - * @param \Pterodactyl\Models\Server $server - * @param string $token - * @return bool - * - * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException - * @throws \Pterodactyl\Exceptions\Http\Server\FileTypeNotEditableException - */ - private function checkFileCanBeEdited($server, $token) - { - $config = app()->make(Repository::class); - $repository = app()->make(FileRepositoryInterface::class); - - try { - $stats = $repository->setServer($server)->setToken($token)->getFileStat($this->route()->parameter('file')); - } catch (RequestException $exception) { - switch ($exception->getCode()) { - case 404: - throw new NotFoundHttpException; - default: - throw new DaemonConnectionException($exception); - } - } - - if ((! $stats->file && ! $stats->symlink) || ! in_array($stats->mime, $config->get('pterodactyl.files.editable'))) { - throw new FileTypeNotEditableException(trans('server.files.exceptions.invalid_mime')); - } - - if ($stats->size > $config->get('pterodactyl.files.max_edit_size')) { - throw new FileSizeTooLargeException(trans('server.files.exceptions.max_size')); - } - - $this->attributes->set('file_stats', $stats); - - return true; - } -} diff --git a/app/Http/Requests/Server/UpdateStartupParametersFormRequest.php b/app/Http/Requests/Server/UpdateStartupParametersFormRequest.php deleted file mode 100644 index 41c15103..00000000 --- a/app/Http/Requests/Server/UpdateStartupParametersFormRequest.php +++ /dev/null @@ -1,61 +0,0 @@ -user()->can('edit-startup', $this->attributes->get('server')); - } - - /** - * Validate that all of the required fields were passed and that the environment - * variable values meet the defined criteria for those fields. - * - * @return array - */ - public function rules() - { - $repository = $this->container->make(EggVariableRepositoryInterface::class); - - $variables = $repository->getEditableVariables($this->attributes->get('server')->egg_id); - $rules = $variables->mapWithKeys(function ($variable) { - $this->validationAttributes['environment.' . $variable->env_variable] = $variable->name; - - return ['environment.' . $variable->env_variable => $variable->rules]; - })->toArray(); - - return array_merge($rules, [ - 'environment' => 'required|array', - ]); - } - - /** - * Return attributes to provide better naming conventions for error messages. - * - * @return array - */ - public function attributes() - { - return $this->validationAttributes; - } -} diff --git a/app/Models/Permission.php b/app/Models/Permission.php index fcd20b7c..813075b5 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -2,6 +2,8 @@ namespace Pterodactyl\Models; +use Illuminate\Support\Collection; + class Permission extends Validable { /** @@ -48,12 +50,130 @@ class Permission extends Validable 'permission' => 'required|string', ]; + /** + * All of the permissions available on the system. You should use self::permissions() + * to retrieve them, and not directly access this array as it is subject to change. + * + * @var array + * @see \Pterodactyl\Models\Permission::permissions() + */ + protected static $permissions = [ + 'websocket' => [ + // Allows the user to connect to the server websocket, this will give them + // access to view the console output as well as realtime server stats (CPU + // and Memory usage). + '*', + ], + + 'control' => [ + // Allows the user to send data to the server console process. A user with this + // permission will not be able to stop the server directly by issuing the specified + // stop command for the Egg, however depending on plugins and server configuration + // they may still be able to control the server power state. + 'console', // power.send-command + + // Allows the user to start/stop/restart/kill the server process. + 'start', // power.power-start + 'stop', // power.power-stop + 'restart', // power.power-restart + 'kill', // power.power-kill + ], + + 'user' => [ + // Allows a user to create a new user assigned to the server. They will not be able + // to assign any permissions they do not already have on their account as well. + 'create', // subuser.create-subuser + 'read', // subuser.list-subusers, subuser.view-subuser + 'update', // subuser.edit-subuser + 'delete', // subuser.delete-subuser + ], + + 'file' => [ + // Allows a user to create additional files and folders either via the Panel, + // or via a direct upload. + 'create', // files.create-files, files.upload-files, files.copy-files, files.move-files + + // Allows a user to view the contents of a directory as well as read the contents + // of a given file. A user with this permission will be able to download files + // as well. + 'read', // files.list-files, files.download-files + + // Allows a user to update the contents of an existing file or directory. + 'update', // files.edit-files, files.save-files + + // Allows a user to delete a file or directory. + 'delete', // files.delete-files + + // Allows a user to archive the contents of a directory as well as decompress existing + // archives on the system. + 'archive', // files.compress-files, files.decompress-files + + // Allows the user to connect and manage server files using their account + // credentials and a SFTP client. + 'sftp', // files.access-sftp + ], + + // Controls permissions for editing or viewing a server's allocations. + 'allocation' => [ + 'read', // server.view-allocations + 'update', // server.edit-allocation + ], + + // Controls permissions for editing or viewing a server's startup parameters. + 'startup' => [ + 'read', // server.view-startup + 'update', // server.edit-startup + ], + + 'database' => [ + // Allows a user to create a new database for a server. + 'create', // database.create-database + + // Allows a user to view the databases associated with the server. If they do not also + // have the view_password permission they will only be able to see the connection address + // and the name of the user. + 'read', // database.view-databases + + // Allows a user to rotate the password on a database instance. If the user does not + // alow have the view_password permission they will not be able to see the updated password + // anywhere, but it will still be rotated. + 'update', // database.reset-db-password + + // Allows a user to delete a database instance. + 'delete', // database.delete-database + + // Allows a user to view the password associated with a database instance for the + // server. Note that a user without this permission may still be able to access these + // credentials by viewing files or the console. + 'view_password', // database.reset-db-password + ], + + 'schedule' => [ + 'create', // task.create-schedule + 'read', // task.view-schedule, task.list-schedules + 'update', // task.edit-schedule, task.queue-schedule, task.toggle-schedule + 'delete', // task.delete-schedule + ], + ]; + + /** + * Returns all of the permissions available on the system for a user to + * have when controlling a server. + * + * @return \Illuminate\Support\Collection + */ + public static function permissions(): Collection + { + return Collection::make(self::$permissions); + } + /** * A list of all permissions available for a user. * * @var array + * @deprecated */ - protected static $permissions = [ + protected static $deprecatedPermissions = [ 'power' => [ 'power-start' => 's:power:start', 'power-stop' => 's:power:stop', @@ -110,16 +230,17 @@ class Permission extends Validable * * @param bool $array * @return array|\Illuminate\Support\Collection + * @deprecated */ public static function getPermissions($array = false) { if ($array) { - return collect(self::$permissions)->mapWithKeys(function ($item) { + return collect(self::$deprecatedPermissions)->mapWithKeys(function ($item) { return $item; })->all(); } - return collect(self::$permissions); + return collect(self::$deprecatedPermissions); } /** diff --git a/tests/Unit/Http/Controllers/Server/ConsoleControllerTest.php b/tests/Unit/Http/Controllers/Server/ConsoleControllerTest.php deleted file mode 100644 index ef633465..00000000 --- a/tests/Unit/Http/Controllers/Server/ConsoleControllerTest.php +++ /dev/null @@ -1,77 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Tests\Unit\Http\Controllers\Server; - -use Mockery as m; -use Pterodactyl\Models\Server; -use Illuminate\Contracts\Config\Repository; -use Tests\Unit\Http\Controllers\ControllerTestCase; -use Pterodactyl\Http\Controllers\Server\ConsoleController; - -class ConsoleControllerTest extends ControllerTestCase -{ - /** - * @var \Illuminate\Contracts\Config\Repository|\Mockery\Mock - */ - protected $config; - - /** - * Setup tests. - */ - public function setUp() - { - parent::setUp(); - - $this->config = m::mock(Repository::class); - } - - /** - * Test both controllers as they do effectively the same thing. - * - * @dataProvider controllerDataProvider - */ - public function testAllControllers($function, $view) - { - $controller = $this->getController(); - $server = factory(Server::class)->make(); - $this->setRequestAttribute('server', $server); - $this->mockInjectJavascript(); - - $this->config->shouldReceive('get')->with('pterodactyl.console.count')->once()->andReturn(100); - $this->config->shouldReceive('get')->with('pterodactyl.console.frequency')->once()->andReturn(10); - - $response = $controller->$function($this->request); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals($view, $response); - } - - /** - * Provide data for the tests. - * - * @return array - */ - public function controllerDataProvider() - { - return [ - ['index', 'server.index'], - ['console', 'server.console'], - ]; - } - - /** - * Return a mocked instance of the controller to allow access to authorization functionality. - * - * @return \Pterodactyl\Http\Controllers\Server\ConsoleController|\Mockery\Mock - */ - private function getController() - { - return $this->buildMockedController(ConsoleController::class, [$this->config]); - } -} diff --git a/tests/Unit/Http/Controllers/Server/Files/DownloadControllerTest.php b/tests/Unit/Http/Controllers/Server/Files/DownloadControllerTest.php deleted file mode 100644 index b76cbfb8..00000000 --- a/tests/Unit/Http/Controllers/Server/Files/DownloadControllerTest.php +++ /dev/null @@ -1,70 +0,0 @@ -cache = m::mock(Repository::class); - } - - /** - * Test the download controller redirects correctly. - */ - public function testIndexController() - { - $controller = $this->getController(); - $server = factory(Server::class)->make(); - $server->setRelation('node', factory(Node::class)->make()); - - $this->setRequestAttribute('server', $server); - - $controller->shouldReceive('authorize')->with('download-files', $server)->once()->andReturnNull(); - - $this->cache->shouldReceive('put') - ->once() - ->with('Server:Downloads:' . $this->getKnownUuid(), ['server' => $server->uuid, 'path' => '/my/file.txt'], 5) - ->andReturnNull(); - - $response = $controller->index($this->request, $server->uuidShort, '/my/file.txt'); - $this->assertIsRedirectResponse($response); - $this->assertRedirectUrlEquals(sprintf( - '%s://%s:%s/v1/server/file/download/%s', - $server->node->scheme, - $server->node->fqdn, - $server->node->daemonListen, - $this->getKnownUuid() - ), $response); - } - - /** - * Return a mocked instance of the controller to allow access to authorization functionality. - * - * @return \Pterodactyl\Http\Controllers\Server\Files\DownloadController|\Mockery\Mock - */ - private function getController() - { - return $this->buildMockedController(DownloadController::class, [$this->cache]); - } -} diff --git a/tests/Unit/Http/Controllers/Server/Files/FileActionsControllerTest.php b/tests/Unit/Http/Controllers/Server/Files/FileActionsControllerTest.php deleted file mode 100644 index feebea84..00000000 --- a/tests/Unit/Http/Controllers/Server/Files/FileActionsControllerTest.php +++ /dev/null @@ -1,185 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Tests\Unit\Http\Controllers\Server\Files; - -use Mockery as m; -use Pterodactyl\Models\Server; -use Tests\Traits\MocksRequestException; -use GuzzleHttp\Exception\RequestException; -use Pterodactyl\Exceptions\PterodactylException; -use Tests\Unit\Http\Controllers\ControllerTestCase; -use Pterodactyl\Http\Requests\Server\UpdateFileContentsFormRequest; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; -use Pterodactyl\Http\Controllers\Server\Files\FileActionsController; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; - -class FileActionsControllerTest extends ControllerTestCase -{ - use MocksRequestException; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface|\Mockery\Mock - */ - protected $repository; - - /** - * Setup tests. - */ - public function setUp() - { - parent::setUp(); - - $this->repository = m::mock(FileRepositoryInterface::class); - } - - /** - * Test the index view controller. - */ - public function testIndexController() - { - $controller = $this->getController(); - $server = factory(Server::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->mockInjectJavascript(); - - $controller->shouldReceive('authorize')->with('list-files', $server)->once()->andReturnNull(); - $this->request->shouldReceive('user->can')->andReturn(true); - - $response = $controller->index($this->request); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals('server.files.index', $response); - } - - /** - * Test the file creation view controller. - * - * @dataProvider directoryNameProvider - */ - public function testCreateController($directory, $expected) - { - $controller = $this->getController(); - $server = factory(Server::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->mockInjectJavascript(); - - $controller->shouldReceive('authorize')->with('create-files', $server)->once()->andReturnNull(); - $this->request->shouldReceive('get')->with('dir')->andReturn($directory); - - $response = $controller->create($this->request); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals('server.files.add', $response); - $this->assertViewHasKey('directory', $response); - $this->assertViewKeyEquals('directory', $expected, $response); - } - - /** - * Test the update controller. - * - * @dataProvider fileNameProvider - */ - public function testUpdateController($file, $expected) - { - $this->setRequestMockClass(UpdateFileContentsFormRequest::class); - - $controller = $this->getController(); - $server = factory(Server::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->setRequestAttribute('server_token', 'abc123'); - $this->setRequestAttribute('file_stats', 'fileStatsObject'); - $this->mockInjectJavascript(['stat' => 'fileStatsObject']); - - $this->repository->shouldReceive('setServer')->with($server)->once()->andReturnSelf() - ->shouldReceive('setToken')->with('abc123')->once()->andReturnSelf() - ->shouldReceive('getContent')->with($file)->once()->andReturn('test'); - - $response = $controller->view($this->request, '1234', $file); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals('server.files.edit', $response); - $this->assertViewHasKey('file', $response); - $this->assertViewHasKey('stat', $response); - $this->assertViewHasKey('contents', $response); - $this->assertViewHasKey('directory', $response); - $this->assertViewKeyEquals('file', $file, $response); - $this->assertViewKeyEquals('stat', 'fileStatsObject', $response); - $this->assertViewKeyEquals('contents', 'test', $response); - $this->assertViewKeyEquals('directory', $expected, $response); - } - - /** - * Test that an exception is handled correctly in the controller. - */ - public function testExceptionRenderedByUpdateController() - { - $this->setRequestMockClass(UpdateFileContentsFormRequest::class); - $this->configureExceptionMock(); - - $controller = $this->getController(); - $server = factory(Server::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->setRequestAttribute('server_token', 'abc123'); - $this->setRequestAttribute('file_stats', 'fileStatsObject'); - - $this->repository->shouldReceive('setServer')->with($server)->once()->andThrow($this->getExceptionMock()); - - try { - $controller->view($this->request, '1234', 'file.txt'); - } catch (PterodactylException $exception) { - $this->assertInstanceOf(DaemonConnectionException::class, $exception); - $this->assertInstanceOf(RequestException::class, $exception->getPrevious()); - } - } - - /** - * Provides a list of directory names and the expected output from formatting. - * - * @return array - */ - public function directoryNameProvider() - { - return [ - [null, ''], - ['/', ''], - ['', ''], - ['my/directory', 'my/directory/'], - ['/my/directory/', 'my/directory/'], - ['/////my/directory////', 'my/directory/'], - ]; - } - - /** - * Provides a list of file names and the expected output from formatting. - * - * @return array - */ - public function fileNameProvider() - { - return [ - ['/my/file.txt', 'my/'], - ['my/file.txt', 'my/'], - ['file.txt', '/'], - ['/file.txt', '/'], - ['./file.txt', '/'], - ]; - } - - /** - * Return a mocked instance of the controller to allow access to authorization functionality. - * - * @return \Pterodactyl\Http\Controllers\Server\Files\FileActionsController|\Mockery\Mock - */ - private function getController() - { - return $this->buildMockedController(FileActionsController::class, [$this->repository]); - } -} diff --git a/tests/Unit/Http/Controllers/Server/Files/RemoteRequestControllerTest.php b/tests/Unit/Http/Controllers/Server/Files/RemoteRequestControllerTest.php deleted file mode 100644 index 486e38f3..00000000 --- a/tests/Unit/Http/Controllers/Server/Files/RemoteRequestControllerTest.php +++ /dev/null @@ -1,158 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Tests\Unit\Http\Controllers\Server\Files; - -use Mockery as m; -use GuzzleHttp\Psr7\Response; -use Pterodactyl\Models\Server; -use Tests\Traits\MocksRequestException; -use GuzzleHttp\Exception\RequestException; -use Illuminate\Contracts\Config\Repository; -use Pterodactyl\Exceptions\PterodactylException; -use Tests\Unit\Http\Controllers\ControllerTestCase; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; -use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; -use Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController; - -class RemoteRequestControllerTest extends ControllerTestCase -{ - use MocksRequestException; - - /** - * @var \Illuminate\Contracts\Config\Repository|\Mockery\Mock - */ - protected $config; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface|\Mockery\Mock - */ - protected $repository; - - /** - * Setup tests. - */ - public function setUp() - { - parent::setUp(); - - $this->config = m::mock(Repository::class); - $this->repository = m::mock(FileRepositoryInterface::class); - } - - /** - * Test the directory listing controller. - */ - public function testDirectoryController() - { - $controller = $this->getController(); - - $server = factory(Server::class)->make(); - $this->setRequestAttribute('server', $server); - $this->setRequestAttribute('server_token', 'abc123'); - - $controller->shouldReceive('authorize')->with('list-files', $server)->once()->andReturnNull(); - $this->request->shouldReceive('input')->with('directory', '/')->once()->andReturn('/'); - $this->repository->shouldReceive('setServer')->with($server)->once()->andReturnSelf() - ->shouldReceive('setToken')->with('abc123')->once()->andReturnSelf() - ->shouldReceive('getDirectory')->with('/')->once()->andReturn(['folders' => 1, 'files' => 2]); - $this->config->shouldReceive('get')->with('pterodactyl.files.editable')->once()->andReturn([]); - - $response = $controller->directory($this->request); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals('server.files.list', $response); - $this->assertViewHasKey('files', $response); - $this->assertViewHasKey('folders', $response); - $this->assertViewHasKey('editableMime', $response); - $this->assertViewHasKey('directory', $response); - $this->assertViewKeyEquals('files', 2, $response); - $this->assertViewKeyEquals('folders', 1, $response); - $this->assertViewKeyEquals('editableMime', [], $response); - $this->assertViewKeyEquals('directory.first', false, $response); - $this->assertViewKeyEquals('directory.header', '', $response); - } - - /** - * Test that the controller properly handles an exception thrown by the daemon connection. - */ - public function testExceptionThrownByDaemonConnectionIsHandledByDisplayController() - { - $this->configureExceptionMock(); - $controller = $this->getController(); - - $server = factory(Server::class)->make(); - $this->setRequestAttribute('server', $server); - - $controller->shouldReceive('authorize')->with('list-files', $server)->once()->andReturnNull(); - $this->request->shouldReceive('input')->with('directory', '/')->once()->andReturn('/'); - $this->repository->shouldReceive('setServer')->with($server)->once()->andThrow($this->getExceptionMock()); - - try { - $controller->directory($this->request); - } catch (PterodactylException $exception) { - $this->assertInstanceOf(DaemonConnectionException::class, $exception); - $this->assertInstanceOf(RequestException::class, $exception->getPrevious()); - } - } - - /** - * Test the store controller. - */ - public function testStoreController() - { - $controller = $this->getController(); - - $server = factory(Server::class)->make(); - $this->setRequestAttribute('server', $server); - $this->setRequestAttribute('server_token', 'abc123'); - - $controller->shouldReceive('authorize')->with('save-files', $server)->once()->andReturnNull(); - $this->request->shouldReceive('input')->with('file')->once()->andReturn('file.txt'); - $this->request->shouldReceive('input')->with('contents')->once()->andReturn('file contents'); - $this->repository->shouldReceive('setServer')->with($server)->once()->andReturnSelf() - ->shouldReceive('setToken')->with('abc123')->once()->andReturnSelf() - ->shouldReceive('putContent')->with('file.txt', 'file contents')->once()->andReturn(new Response); - - $response = $controller->store($this->request); - $this->assertIsResponse($response); - $this->assertResponseCodeEquals(204, $response); - } - - /** - * Test that the controller properly handles an exception thrown by the daemon connection. - */ - public function testExceptionThrownByDaemonConnectionIsHandledByStoreController() - { - $this->configureExceptionMock(); - $controller = $this->getController(); - - $server = factory(Server::class)->make(); - $this->setRequestAttribute('server', $server); - - $controller->shouldReceive('authorize')->with('save-files', $server)->once()->andReturnNull(); - $this->repository->shouldReceive('setServer')->with($server)->once()->andThrow($this->getExceptionMock()); - - try { - $controller->store($this->request); - } catch (PterodactylException $exception) { - $this->assertInstanceOf(DaemonConnectionException::class, $exception); - $this->assertInstanceOf(RequestException::class, $exception->getPrevious()); - } - } - - /** - * Return a mocked instance of the controller to allow access to authorization functionality. - * - * @return \Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController|\Mockery\Mock - */ - private function getController() - { - return $this->buildMockedController(RemoteRequestController::class, [$this->config, $this->repository]); - } -} diff --git a/tests/Unit/Http/Controllers/Server/SubuserControllerTest.php b/tests/Unit/Http/Controllers/Server/SubuserControllerTest.php deleted file mode 100644 index ab308819..00000000 --- a/tests/Unit/Http/Controllers/Server/SubuserControllerTest.php +++ /dev/null @@ -1,226 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Tests\Unit\Http\Controllers\Server; - -use Mockery as m; -use Pterodactyl\Models\Server; -use Pterodactyl\Models\Subuser; -use Pterodactyl\Models\Permission; -use Prologue\Alerts\AlertsMessageBag; -use Tests\Unit\Http\Controllers\ControllerTestCase; -use Pterodactyl\Services\Subusers\SubuserUpdateService; -use Pterodactyl\Services\Subusers\SubuserCreationService; -use Pterodactyl\Services\Subusers\SubuserDeletionService; -use Pterodactyl\Http\Controllers\Server\SubuserController; -use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; -use Pterodactyl\Http\Requests\Server\Subuser\SubuserStoreFormRequest; -use Pterodactyl\Http\Requests\Server\Subuser\SubuserUpdateFormRequest; - -class SubuserControllerTest extends ControllerTestCase -{ - /** - * @var \Prologue\Alerts\AlertsMessageBag|\Mockery\Mock - */ - protected $alert; - - /** - * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface|\Mockery\Mock - */ - protected $repository; - - /** - * @var \Pterodactyl\Services\Subusers\SubuserCreationService|\Mockery\Mock - */ - protected $subuserCreationService; - - /** - * @var \Pterodactyl\Services\Subusers\SubuserDeletionService|\Mockery\Mock - */ - protected $subuserDeletionService; - - /** - * @var \Pterodactyl\Services\Subusers\SubuserUpdateService|\Mockery\Mock - */ - protected $subuserUpdateService; - - /** - * Setup tests. - */ - public function setUp() - { - parent::setUp(); - - $this->alert = m::mock(AlertsMessageBag::class); - $this->repository = m::mock(SubuserRepositoryInterface::class); - $this->subuserCreationService = m::mock(SubuserCreationService::class); - $this->subuserDeletionService = m::mock(SubuserDeletionService::class); - $this->subuserUpdateService = m::mock(SubuserUpdateService::class); - } - - /* - * Test index controller. - */ - public function testIndexController() - { - $controller = $this->getController(); - $server = factory(Server::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->mockInjectJavascript(); - - $controller->shouldReceive('authorize')->with('list-subusers', $server)->once()->andReturnNull(); - $this->repository->shouldReceive('findWhere')->with([['server_id', '=', $server->id]])->once()->andReturn(collect()); - - $response = $controller->index($this->request); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals('server.users.index', $response); - $this->assertViewHasKey('subusers', $response); - } - - /** - * Test view controller. - */ - public function testViewController() - { - $controller = $this->getController(); - $subuser = factory(Subuser::class)->make(); - $subuser->setRelation('permissions', collect([ - (object) ['permission' => 'some.permission'], - (object) ['permission' => 'another.permission'], - ])); - $server = factory(Server::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->setRequestAttribute('subuser', $subuser); - $this->mockInjectJavascript(); - - $controller->shouldReceive('authorize')->with('view-subuser', $server)->once()->andReturnNull(); - $this->repository->shouldReceive('getWithPermissions')->with($subuser)->once()->andReturn($subuser); - - $response = $controller->view($this->request); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals('server.users.view', $response); - $this->assertViewHasKey('subuser', $response); - $this->assertViewHasKey('permlist', $response); - $this->assertViewHasKey('permissions', $response); - $this->assertViewKeyEquals('subuser', $subuser, $response); - $this->assertViewKeyEquals('permlist', Permission::getPermissions(), $response); - $this->assertViewKeyEquals('permissions', collect([ - 'some.permission' => true, - 'another.permission' => true, - ]), $response); - } - - /** - * Test the update controller. - */ - public function testUpdateController() - { - $this->setRequestMockClass(SubuserUpdateFormRequest::class); - - $controller = $this->getController(); - $subuser = factory(Subuser::class)->make(); - - $this->setRequestAttribute('subuser', $subuser); - - $this->request->shouldReceive('input')->with('permissions', [])->once()->andReturn(['some.permission']); - $this->subuserUpdateService->shouldReceive('handle')->with($subuser, ['some.permission'])->once()->andReturnNull(); - $this->alert->shouldReceive('success')->with(trans('server.users.user_updated'))->once()->andReturnSelf(); - $this->alert->shouldReceive('flash')->withNoArgs()->once()->andReturnNull(); - - $response = $controller->update($this->request, 'abcd1234', $subuser->hashid); - $this->assertIsRedirectResponse($response); - $this->assertRedirectRouteEquals('server.subusers.view', $response, ['uuid' => 'abcd1234', 'id' => $subuser->hashid]); - } - - /** - * Test the create controller. - */ - public function testCreateController() - { - $controller = $this->getController(); - $server = factory(Server::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->mockInjectJavascript(); - - $controller->shouldReceive('authorize')->with('create-subuser', $server)->once()->andReturnNull(); - - $response = $controller->create($this->request); - $this->assertIsViewResponse($response); - $this->assertViewNameEquals('server.users.new', $response); - $this->assertViewHasKey('permissions', $response); - $this->assertViewKeyEquals('permissions', Permission::getPermissions(), $response); - } - - /** - * Test the store controller. - */ - public function testStoreController() - { - $this->setRequestMockClass(SubuserStoreFormRequest::class); - $controller = $this->getController(); - - $server = factory(Server::class)->make(); - $subuser = factory(Subuser::class)->make(); - - $this->setRequestAttribute('server', $server); - - $this->request->shouldReceive('input')->with('email')->once()->andReturn('user@test.com'); - $this->request->shouldReceive('input')->with('permissions', [])->once()->andReturn(['some.permission']); - $this->subuserCreationService->shouldReceive('handle')->with($server, 'user@test.com', ['some.permission'])->once()->andReturn($subuser); - $this->alert->shouldReceive('success')->with(trans('server.users.user_assigned'))->once()->andReturnSelf(); - $this->alert->shouldReceive('flash')->withNoArgs()->once()->andReturnNull(); - - $response = $controller->store($this->request); - $this->assertIsRedirectResponse($response); - $this->assertRedirectRouteEquals('server.subusers.view', $response, [ - 'uuid' => $server->uuidShort, - 'id' => $subuser->hashid, - ]); - } - - /** - * Test the delete controller. - */ - public function testDeleteController() - { - $controller = $this->getController(); - - $server = factory(Server::class)->make(); - $subuser = factory(Subuser::class)->make(); - - $this->setRequestAttribute('server', $server); - $this->setRequestAttribute('subuser', $subuser); - - $controller->shouldReceive('authorize')->with('delete-subuser', $server)->once()->andReturnNull(); - $this->subuserDeletionService->shouldReceive('handle')->with($subuser)->once()->andReturnNull(); - - $response = $controller->delete($this->request); - $this->assertIsResponse($response); - $this->assertResponseCodeEquals(204, $response); - } - - /** - * Return a mocked instance of the controller to allow access to authorization functionality. - * - * @return \Pterodactyl\Http\Controllers\Server\SubuserController|\Mockery\Mock - */ - private function getController() - { - return $this->buildMockedController(SubuserController::class, [ - $this->alert, - $this->subuserCreationService, - $this->subuserDeletionService, - $this->repository, - $this->subuserUpdateService, - ]); - } -}