Skip to content

Commit

Permalink
Add support for allowing subusers to access SFTP. (pterodactyl#918)
Browse files Browse the repository at this point in the history
  • Loading branch information
PurpleIsEverything authored and DaneEveritt committed Feb 17, 2018
1 parent f61a5fa commit d8be167
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 70 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
.vscode/*
storage/framework/*
/.idea
/nbproject

package-lock.json
composer.lock
node_modules

Expand Down
14 changes: 5 additions & 9 deletions app/Models/Permission.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ class Permission extends Model implements CleansAttributes, ValidableContract
'view-startup' => null,
'edit-startup' => null,
],
'sftp' => [
'view-sftp' => null,
'view-sftp-password' => null,
'reset-sftp' => 's:set-password',
'database' => [
'view-databases' => null,
'reset-db-password' => null,
],
'file' => [
'access-sftp' => null,
'list-files' => 's:files:get',
'edit-files' => 's:files:read',
'save-files' => 's:files:post',
Expand All @@ -106,7 +106,7 @@ class Permission extends Model implements CleansAttributes, ValidableContract
'create-files' => 's:files:create',
'upload-files' => 's:files:upload',
'delete-files' => 's:files:delete',
'download-files' => null,
'download-files' => 's:files:download',
],
'task' => [
'list-schedules' => null,
Expand All @@ -117,10 +117,6 @@ class Permission extends Model implements CleansAttributes, ValidableContract
'create-schedule' => null,
'delete-schedule' => null,
],
'database' => [
'view-databases' => null,
'reset-db-password' => null,
],
];

/**
Expand Down
26 changes: 22 additions & 4 deletions app/Services/Sftp/AuthenticateUsingPasswordService.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class AuthenticateUsingPasswordService
Expand All @@ -25,20 +26,28 @@ class AuthenticateUsingPasswordService
*/
private $userRepository;

/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
*/
private $subuserRepository;

/**
* AuthenticateUsingPasswordService constructor.
*
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
*/
public function __construct(
DaemonKeyProviderService $keyProviderService,
ServerRepositoryInterface $repository,
SubuserRepositoryInterface $subuserRepository,
UserRepositoryInterface $userRepository
) {
$this->keyProviderService = $keyProviderService;
$this->repository = $repository;
$this->subuserRepository = $subuserRepository;
$this->userRepository = $userRepository;
}

Expand Down Expand Up @@ -73,10 +82,19 @@ public function handle(string $username, string $password, int $node, string $se
}

$server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->getByUuid($server);
if ($server->node_id !== $node || (! $user->root_admin && $server->owner_id !== $user->id)) {
if ($server->node_id !== $node) {
throw new RecordNotFoundException;
}

if (! $user->root_admin && $server->owner_id !== $user->id) {
$subuser = $this->subuserRepository->getWithPermissionsUsingUserAndServer($user->id, $server->id);
$permissions = $subuser->getRelation('permissions')->pluck('permission')->toArray();

if (! in_array('access-sftp', $permissions)) {
throw new RecordNotFoundException;
}
}

if ($server->installed !== 1 || $server->suspended) {
throw new BadRequestHttpException;
}
Expand Down
22 changes: 6 additions & 16 deletions resources/lang/de/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
'subuser_header' => 'Subuser Verwaltung',
'server_header' => 'Server Verwaltung',
'task_header' => 'Schedule Verwaltung',
'sftp_header' => 'SFTP Verwaltung',
'database_header' => 'Database Verwaltung',
'power_start' => [
'title' => 'Start Server',
Expand All @@ -125,17 +124,21 @@
'title' => 'Send Console Command',
'description' => 'Der User darf die Konsole benutzen.',
],
'view_sftp' => [
'title' => 'SFTP erlaubt',
'description' => 'Ermöglicht dem Benutzer, eine Verbindung mit dem vom Daemon bereitgestellten SFTP-Server herzustellen.',
],
'list_files' => [
'title' => 'List Files',
'description' => 'Der User darf die Server-Dateien sehen.',
],
'edit_files' => [
'title' => 'Edit Files',
'description' => 'Der User darf die Server-Dateien sehen.',
'description' => 'Der User darf die Server-Dateien sehen. SFTP ist von dieser Erlaubnis nicht betroffen.',
],
'save_files' => [
'title' => 'Save Files',
'description' => 'Der User darf die Server-Dateien bearbeiten.',
'description' => 'Der User darf die Server-Dateien bearbeiten. SFTP ist von dieser Erlaubnis nicht betroffen.',
],
'move_files' => [
'title' => 'Rename & Move Files',
Expand Down Expand Up @@ -233,18 +236,6 @@
'title' => 'Delete Schedule',
'description' => 'Der User darf geplante Aktionen für den Server löschen.',
],
'view_sftp' => [
'title' => 'View SFTP Details',
'description' => 'Der User darf die SFTP Informationen sehen (nicht das Passwort).',
],
'view_sftp_password' => [
'title' => 'View SFTP Password',
'description' => 'Der User darf dass SFTP Passwort sehen.',
],
'reset_sftp' => [
'title' => 'Reset SFTP Password',
'description' => 'Der User darf dass SFTP Passwort zurücksetzen.',
],
'view_databases' => [
'title' => 'View Database Details',
'description' => 'Der User darf die Datenbankinformationen sehen.',
Expand Down Expand Up @@ -298,7 +289,6 @@
'sftp' => [
'header' => 'SFTP Information',
'header_sub' => 'Details für eine SFTP verbindung.',
'change_pass' => 'Passwort ändern',
'details' => 'SFTP Details',
'conn_addr' => 'Adresse',
'warning' => 'Bitte benutze SFTP und nicht FTP!.',
Expand Down
22 changes: 6 additions & 16 deletions resources/lang/en/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@
'subuser_header' => 'Subuser Management',
'server_header' => 'Server Management',
'task_header' => 'Schedule Management',
'sftp_header' => 'SFTP Management',
'database_header' => 'Database Management',
'power_start' => [
'title' => 'Start Server',
Expand All @@ -129,17 +128,21 @@
'title' => 'Send Console Command',
'description' => 'Allows sending a command from the console. If the user does not have stop or restart permissions they cannot send the application\'s stop command.',
],
'view_sftp' => [
'title' => 'SFTP Allowed',
'description' => 'Allows user to connect to the SFTP server provided by the daemon.',
],
'list_files' => [
'title' => 'List Files',
'description' => 'Allows user to list all files and folders on the server but not view file contents.',
],
'edit_files' => [
'title' => 'Edit Files',
'description' => 'Allows user to open a file for viewing only.',
'description' => 'Allows user to open a file for viewing only. SFTP is not effected by this permission.',
],
'save_files' => [
'title' => 'Save Files',
'description' => 'Allows user to save modified file contents.',
'description' => 'Allows user to save modified file contents. SFTP is not effected by this permission.',
],
'move_files' => [
'title' => 'Rename & Move Files',
Expand Down Expand Up @@ -237,18 +240,6 @@
'title' => 'Delete Schedule',
'description' => 'Allows a user to delete a schedule from the server.',
],
'view_sftp' => [
'title' => 'View SFTP Details',
'description' => 'Allows user to view the server\'s SFTP information but not the password.',
],
'view_sftp_password' => [
'title' => 'View SFTP Password',
'description' => 'Allows user to view the SFTP password for the server.',
],
'reset_sftp' => [
'title' => 'Reset SFTP Password',
'description' => 'Allows user to change the SFTP password for the server.',
],
'view_databases' => [
'title' => 'View Database Details',
'description' => 'Allows user to view all databases associated with this server including the usernames and passwords for the databases.',
Expand Down Expand Up @@ -302,7 +293,6 @@
'sftp' => [
'header' => 'SFTP Configuration',
'header_sub' => 'Account details for SFTP connections.',
'change_pass' => 'Change SFTP Password',
'details' => 'SFTP Details',
'conn_addr' => 'Connection Address',
'warning' => 'The SFTP password is your account password. Ensure that your client is set to use SFTP and not FTP or FTPS for connections, there is a difference between the protocols.',
Expand Down
22 changes: 6 additions & 16 deletions resources/lang/es/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
'subuser_header' => 'Subuser De Gestión',
'server_header' => 'Administración Del Servidor',
'task_header' => 'La Programación De La Administración',
'sftp_header' => 'SFTP Gestión',
'database_header' => 'Administración De Base De Datos',
'power_start' => [
'title' => 'Inicio Del Servidor',
Expand All @@ -125,17 +124,21 @@
'title' => 'Enviar Comandos De La Consola',
'description' => 'Permite el envío de un comando desde la consola. Si el usuario no tiene permiso para detener o reiniciar, no puede enviar el comando de detención de la aplicación.',
],
'view_sftp' => [
'title' => 'SFTP permitido',
'description' => 'Permite al usuario conectarse al servidor SFTP proporcionado por el daemon.',
],
'list_files' => [
'title' => 'Lista De Archivos',
'description' => 'Permite al usuario a la lista de todos los archivos y carpetas en el servidor, pero no ver el contenido del archivo.',
],
'edit_files' => [
'title' => 'Editar Archivos',
'description' => 'Permite al usuario abrir un archivo solo para visualización.',
'description' => 'Permite al usuario abrir un archivo solo para visualización. SFTP no se ve afectado por este permiso.',
],
'save_files' => [
'title' => 'Guardar Archivos',
'description' => 'Permite que el usuario guarde el archivo modificado contenido.',
'description' => 'Permite que el usuario guarde el archivo modificado contenido. SFTP no se ve afectado por este permiso.',
],
'move_files' => [
'title' => 'Renombrar Y Mover Archivos',
Expand Down Expand Up @@ -229,18 +232,6 @@
'title' => 'Eliminar Horario',
'description' => 'Permite a un usuario para eliminar un programa desde el servidor.',
],
'view_sftp' => [
'title' => 'Ver SFTP Detalles',
'description' => 'Permite al usuario ver la información SFTP del servidor pero no la contraseña.',
],
'view_sftp_password' => [
'title' => 'Ver SFTP Contraseña',
'description' => 'Permite al usuario ver el SFTP contraseña para el servidor.',
],
'reset_sftp' => [
'title' => 'Restablecer Contraseña SFTP',
'description' => 'Le permite al usuario cambiar el SFTP contraseña para el servidor.',
],
'view_databases' => [
'title' => 'Ver Detalles De Base De Datos',
'description' => 'Permite al usuario ver todas las bases de datos asociadas con este servidor, incluidos los nombres de usuario y contraseñas de las bases de datos de.',
Expand Down Expand Up @@ -294,7 +285,6 @@
'sftp' => [
'header' => 'SFTP Configuración',
'header_sub' => 'Detalles de la cuenta para SFTP.',
'change_pass' => 'Cambiar Contraseña SFTP',
'details' => 'SFTP Detalles',
'conn_addr' => 'Dirección De Conexión',
'warning' => 'Asegúrese de que su cliente está configurado para utilizar SFTP y FTP no o FTPS para las conexiones, hay una diferencia entre los protocolos.',
Expand Down
4 changes: 2 additions & 2 deletions resources/themes/pterodactyl/layouts/master.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class="active"
</a>
</li>
@endcan
@if(Gate::allows('view-startup', $server) || Gate::allows('view-sftp', $server) || Gate::allows('view-allocation', $server))
@if(Gate::allows('view-startup', $server) || Gate::allows('access-sftp', $server) || Gate::allows('view-allocation', $server))
<li class="treeview
@if(starts_with(Route::currentRouteName(), 'server.settings'))
active
Expand All @@ -183,7 +183,7 @@ class="active"
@can('view-allocation', $server)
<li class="{{ Route::currentRouteName() !== 'server.settings.allocation' ?: 'active' }}"><a href="{{ route('server.settings.allocation', $server->uuidShort) }}"><i class="fa fa-angle-right"></i> @lang('navigation.server.port_allocations')</a></li>
@endcan
@can('view-sftp', $server)
@can('access-sftp', $server)
<li class="{{ Route::currentRouteName() !== 'server.settings.sftp' ?: 'active' }}"><a href="{{ route('server.settings.sftp', $server->uuidShort) }}"><i class="fa fa-angle-right"></i> @lang('navigation.server.sftp_settings')</a></li>
@endcan
@can('view-startup', $server)
Expand Down
15 changes: 12 additions & 3 deletions tests/Unit/Services/Sftp/AuthenticateUsingPasswordServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;

class AuthenticateUsingPasswordServiceTest extends TestCase
{
Expand All @@ -23,6 +24,11 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface|\Mockery\Mock
*/
private $repository;

/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface|\Mockery\Mock
*/
private $subuserRepository;

/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock
Expand All @@ -38,6 +44,7 @@ public function setUp()

$this->keyProviderService = m::mock(DaemonKeyProviderService::class);
$this->repository = m::mock(ServerRepositoryInterface::class);
$this->subuserRepository = m::mock(SubuserRepositoryInterface::class);
$this->userRepository = m::mock(UserRepositoryInterface::class);
}

Expand Down Expand Up @@ -130,8 +137,8 @@ public function testExceptionIsThrownIfNoUserAccountIsFound()
}

/**
* Test that an exception is thrown if the user is not the owner of the server
* and is not an administrator.
* Test that an exception is thrown if the user is not the owner of the server,
* is not a sub user and is not an administrator.
*
* @expectedException \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
Expand All @@ -146,6 +153,8 @@ public function testExceptionIsThrownIfUserDoesNotOwnServer()
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf();
$this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server);

$this->subuserRepository->shouldReceive('getWithPermissionsUsingUserAndServer')->with($user->id, $server->id)->once()->andThrow(new RecordNotFoundException);

$this->getService()->handle($user->username, 'password', 1, $server->uuidShort);
}

Expand Down Expand Up @@ -214,6 +223,6 @@ public function testNotInstalledServer()
*/
private function getService(): AuthenticateUsingPasswordService
{
return new AuthenticateUsingPasswordService($this->keyProviderService, $this->repository, $this->userRepository);
return new AuthenticateUsingPasswordService($this->keyProviderService, $this->repository, $this->subuserRepository, $this->userRepository);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@ public function setUp()
*/
public function testPermissionsAreAssignedCorrectly()
{
$permissions = ['reset-sftp', 'view-sftp'];
$permissions = ['access-sftp'];

$this->repository->shouldReceive('withoutFreshModel')->withNoArgs()->once()->andReturnSelf()
->shouldReceive('insert')->with([
['subuser_id' => 1, 'permission' => 'reset-sftp'],
['subuser_id' => 1, 'permission' => 'view-sftp'],
['subuser_id' => 1, 'permission' => 'access-sftp'],
])->once()->andReturn(true);

$this->service->handle(1, $permissions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public function testAccountIsCreatedForNewUser()
*/
public function testExistingUserCanBeAddedAsASubuser()
{
$permissions = ['view-sftp', 'reset-sftp'];
$permissions = ['access-sftp'];
$server = factory(Server::class)->make();
$user = factory(User::class)->make();
$subuser = factory(Subuser::class)->make(['user_id' => $user->id, 'server_id' => $server->id]);
Expand Down

0 comments on commit d8be167

Please sign in to comment.