Add support for allowing subusers to access SFTP. (#918)

This commit is contained in:
ShadowKitten 2018-02-17 15:10:44 -07:00 committed by Dane Everitt
parent f61a5fa0cb
commit d8be167a9c
10 changed files with 64 additions and 70 deletions

2
.gitignore vendored
View file

@ -5,7 +5,9 @@
.vscode/* .vscode/*
storage/framework/* storage/framework/*
/.idea /.idea
/nbproject
package-lock.json
composer.lock composer.lock
node_modules node_modules

View file

@ -90,12 +90,12 @@ class Permission extends Model implements CleansAttributes, ValidableContract
'view-startup' => null, 'view-startup' => null,
'edit-startup' => null, 'edit-startup' => null,
], ],
'sftp' => [ 'database' => [
'view-sftp' => null, 'view-databases' => null,
'view-sftp-password' => null, 'reset-db-password' => null,
'reset-sftp' => 's:set-password',
], ],
'file' => [ 'file' => [
'access-sftp' => null,
'list-files' => 's:files:get', 'list-files' => 's:files:get',
'edit-files' => 's:files:read', 'edit-files' => 's:files:read',
'save-files' => 's:files:post', 'save-files' => 's:files:post',
@ -106,7 +106,7 @@ class Permission extends Model implements CleansAttributes, ValidableContract
'create-files' => 's:files:create', 'create-files' => 's:files:create',
'upload-files' => 's:files:upload', 'upload-files' => 's:files:upload',
'delete-files' => 's:files:delete', 'delete-files' => 's:files:delete',
'download-files' => null, 'download-files' => 's:files:download',
], ],
'task' => [ 'task' => [
'list-schedules' => null, 'list-schedules' => null,
@ -117,10 +117,6 @@ class Permission extends Model implements CleansAttributes, ValidableContract
'create-schedule' => null, 'create-schedule' => null,
'delete-schedule' => null, 'delete-schedule' => null,
], ],
'database' => [
'view-databases' => null,
'reset-db-password' => null,
],
]; ];
/** /**

View file

@ -6,6 +6,7 @@ use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class AuthenticateUsingPasswordService class AuthenticateUsingPasswordService
@ -25,20 +26,28 @@ class AuthenticateUsingPasswordService
*/ */
private $userRepository; private $userRepository;
/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
*/
private $subuserRepository;
/** /**
* AuthenticateUsingPasswordService constructor. * AuthenticateUsingPasswordService constructor.
* *
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository * @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
*/ */
public function __construct( public function __construct(
DaemonKeyProviderService $keyProviderService, DaemonKeyProviderService $keyProviderService,
ServerRepositoryInterface $repository, ServerRepositoryInterface $repository,
SubuserRepositoryInterface $subuserRepository,
UserRepositoryInterface $userRepository UserRepositoryInterface $userRepository
) { ) {
$this->keyProviderService = $keyProviderService; $this->keyProviderService = $keyProviderService;
$this->repository = $repository; $this->repository = $repository;
$this->subuserRepository = $subuserRepository;
$this->userRepository = $userRepository; $this->userRepository = $userRepository;
} }
@ -73,10 +82,19 @@ class AuthenticateUsingPasswordService
} }
$server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->getByUuid($server); $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; 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) { if ($server->installed !== 1 || $server->suspended) {
throw new BadRequestHttpException; throw new BadRequestHttpException;
} }

View file

@ -103,7 +103,6 @@ return [
'subuser_header' => 'Subuser Verwaltung', 'subuser_header' => 'Subuser Verwaltung',
'server_header' => 'Server Verwaltung', 'server_header' => 'Server Verwaltung',
'task_header' => 'Schedule Verwaltung', 'task_header' => 'Schedule Verwaltung',
'sftp_header' => 'SFTP Verwaltung',
'database_header' => 'Database Verwaltung', 'database_header' => 'Database Verwaltung',
'power_start' => [ 'power_start' => [
'title' => 'Start Server', 'title' => 'Start Server',
@ -125,17 +124,21 @@ return [
'title' => 'Send Console Command', 'title' => 'Send Console Command',
'description' => 'Der User darf die Konsole benutzen.', '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' => [ 'list_files' => [
'title' => 'List Files', 'title' => 'List Files',
'description' => 'Der User darf die Server-Dateien sehen.', 'description' => 'Der User darf die Server-Dateien sehen.',
], ],
'edit_files' => [ 'edit_files' => [
'title' => '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' => [ 'save_files' => [
'title' => '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' => [ 'move_files' => [
'title' => 'Rename & Move Files', 'title' => 'Rename & Move Files',
@ -233,18 +236,6 @@ return [
'title' => 'Delete Schedule', 'title' => 'Delete Schedule',
'description' => 'Der User darf geplante Aktionen für den Server löschen.', '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' => [ 'view_databases' => [
'title' => 'View Database Details', 'title' => 'View Database Details',
'description' => 'Der User darf die Datenbankinformationen sehen.', 'description' => 'Der User darf die Datenbankinformationen sehen.',
@ -298,7 +289,6 @@ return [
'sftp' => [ 'sftp' => [
'header' => 'SFTP Information', 'header' => 'SFTP Information',
'header_sub' => 'Details für eine SFTP verbindung.', 'header_sub' => 'Details für eine SFTP verbindung.',
'change_pass' => 'Passwort ändern',
'details' => 'SFTP Details', 'details' => 'SFTP Details',
'conn_addr' => 'Adresse', 'conn_addr' => 'Adresse',
'warning' => 'Bitte benutze SFTP und nicht FTP!.', 'warning' => 'Bitte benutze SFTP und nicht FTP!.',

View file

@ -107,7 +107,6 @@ return [
'subuser_header' => 'Subuser Management', 'subuser_header' => 'Subuser Management',
'server_header' => 'Server Management', 'server_header' => 'Server Management',
'task_header' => 'Schedule Management', 'task_header' => 'Schedule Management',
'sftp_header' => 'SFTP Management',
'database_header' => 'Database Management', 'database_header' => 'Database Management',
'power_start' => [ 'power_start' => [
'title' => 'Start Server', 'title' => 'Start Server',
@ -129,17 +128,21 @@ return [
'title' => 'Send Console Command', '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.', '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' => [ 'list_files' => [
'title' => 'List Files', 'title' => 'List Files',
'description' => 'Allows user to list all files and folders on the server but not view file contents.', 'description' => 'Allows user to list all files and folders on the server but not view file contents.',
], ],
'edit_files' => [ 'edit_files' => [
'title' => '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' => [ 'save_files' => [
'title' => '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' => [ 'move_files' => [
'title' => 'Rename & Move Files', 'title' => 'Rename & Move Files',
@ -237,18 +240,6 @@ return [
'title' => 'Delete Schedule', 'title' => 'Delete Schedule',
'description' => 'Allows a user to delete a schedule from the server.', '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' => [ 'view_databases' => [
'title' => 'View Database Details', 'title' => 'View Database Details',
'description' => 'Allows user to view all databases associated with this server including the usernames and passwords for the databases.', 'description' => 'Allows user to view all databases associated with this server including the usernames and passwords for the databases.',
@ -302,7 +293,6 @@ return [
'sftp' => [ 'sftp' => [
'header' => 'SFTP Configuration', 'header' => 'SFTP Configuration',
'header_sub' => 'Account details for SFTP connections.', 'header_sub' => 'Account details for SFTP connections.',
'change_pass' => 'Change SFTP Password',
'details' => 'SFTP Details', 'details' => 'SFTP Details',
'conn_addr' => 'Connection Address', '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.', '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.',

View file

@ -103,7 +103,6 @@ return [
'subuser_header' => 'Subuser De Gestión', 'subuser_header' => 'Subuser De Gestión',
'server_header' => 'Administración Del Servidor', 'server_header' => 'Administración Del Servidor',
'task_header' => 'La Programación De La Administración', 'task_header' => 'La Programación De La Administración',
'sftp_header' => 'SFTP Gestión',
'database_header' => 'Administración De Base De Datos', 'database_header' => 'Administración De Base De Datos',
'power_start' => [ 'power_start' => [
'title' => 'Inicio Del Servidor', 'title' => 'Inicio Del Servidor',
@ -125,17 +124,21 @@ return [
'title' => 'Enviar Comandos De La Consola', '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.', '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' => [ 'list_files' => [
'title' => 'Lista De Archivos', '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.', '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' => [ 'edit_files' => [
'title' => 'Editar Archivos', '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' => [ 'save_files' => [
'title' => 'Guardar Archivos', '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' => [ 'move_files' => [
'title' => 'Renombrar Y Mover Archivos', 'title' => 'Renombrar Y Mover Archivos',
@ -229,18 +232,6 @@ return [
'title' => 'Eliminar Horario', 'title' => 'Eliminar Horario',
'description' => 'Permite a un usuario para eliminar un programa desde el servidor.', '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' => [ 'view_databases' => [
'title' => 'Ver Detalles De Base De Datos', '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.', '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.',
@ -294,7 +285,6 @@ return [
'sftp' => [ 'sftp' => [
'header' => 'SFTP Configuración', 'header' => 'SFTP Configuración',
'header_sub' => 'Detalles de la cuenta para SFTP.', 'header_sub' => 'Detalles de la cuenta para SFTP.',
'change_pass' => 'Cambiar Contraseña SFTP',
'details' => 'SFTP Detalles', 'details' => 'SFTP Detalles',
'conn_addr' => 'Dirección De Conexión', '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.', '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.',

View file

@ -166,7 +166,7 @@
</a> </a>
</li> </li>
@endcan @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 <li class="treeview
@if(starts_with(Route::currentRouteName(), 'server.settings')) @if(starts_with(Route::currentRouteName(), 'server.settings'))
active active
@ -183,7 +183,7 @@
@can('view-allocation', $server) @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> <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 @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> <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 @endcan
@can('view-startup', $server) @can('view-startup', $server)

View file

@ -11,6 +11,7 @@ use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService; use Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
class AuthenticateUsingPasswordServiceTest extends TestCase class AuthenticateUsingPasswordServiceTest extends TestCase
{ {
@ -24,6 +25,11 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
*/ */
private $repository; private $repository;
/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface|\Mockery\Mock
*/
private $subuserRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock
*/ */
@ -38,6 +44,7 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
$this->keyProviderService = m::mock(DaemonKeyProviderService::class); $this->keyProviderService = m::mock(DaemonKeyProviderService::class);
$this->repository = m::mock(ServerRepositoryInterface::class); $this->repository = m::mock(ServerRepositoryInterface::class);
$this->subuserRepository = m::mock(SubuserRepositoryInterface::class);
$this->userRepository = m::mock(UserRepositoryInterface::class); $this->userRepository = m::mock(UserRepositoryInterface::class);
} }
@ -130,8 +137,8 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
} }
/** /**
* Test that an exception is thrown if the user is not the owner of the server * Test that an exception is thrown if the user is not the owner of the server,
* and is not an administrator. * is not a sub user and is not an administrator.
* *
* @expectedException \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @expectedException \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
@ -146,6 +153,8 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf(); $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->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); $this->getService()->handle($user->username, 'password', 1, $server->uuidShort);
} }
@ -214,6 +223,6 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
*/ */
private function getService(): AuthenticateUsingPasswordService private function getService(): AuthenticateUsingPasswordService
{ {
return new AuthenticateUsingPasswordService($this->keyProviderService, $this->repository, $this->userRepository); return new AuthenticateUsingPasswordService($this->keyProviderService, $this->repository, $this->subuserRepository, $this->userRepository);
} }
} }

View file

@ -42,12 +42,11 @@ class PermissionCreationServiceTest extends TestCase
*/ */
public function testPermissionsAreAssignedCorrectly() public function testPermissionsAreAssignedCorrectly()
{ {
$permissions = ['reset-sftp', 'view-sftp']; $permissions = ['access-sftp'];
$this->repository->shouldReceive('withoutFreshModel')->withNoArgs()->once()->andReturnSelf() $this->repository->shouldReceive('withoutFreshModel')->withNoArgs()->once()->andReturnSelf()
->shouldReceive('insert')->with([ ->shouldReceive('insert')->with([
['subuser_id' => 1, 'permission' => 'reset-sftp'], ['subuser_id' => 1, 'permission' => 'access-sftp'],
['subuser_id' => 1, 'permission' => 'view-sftp'],
])->once()->andReturn(true); ])->once()->andReturn(true);
$this->service->handle(1, $permissions); $this->service->handle(1, $permissions);

View file

@ -121,7 +121,7 @@ class SubuserCreationServiceTest extends TestCase
*/ */
public function testExistingUserCanBeAddedAsASubuser() public function testExistingUserCanBeAddedAsASubuser()
{ {
$permissions = ['view-sftp', 'reset-sftp']; $permissions = ['access-sftp'];
$server = factory(Server::class)->make(); $server = factory(Server::class)->make();
$user = factory(User::class)->make(); $user = factory(User::class)->make();
$subuser = factory(Subuser::class)->make(['user_id' => $user->id, 'server_id' => $server->id]); $subuser = factory(Subuser::class)->make(['user_id' => $user->id, 'server_id' => $server->id]);