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/*
storage/framework/*
/.idea
/nbproject
package-lock.json
composer.lock
node_modules

View file

@ -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',
@ -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,
@ -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,
],
];
/**

View file

@ -6,6 +6,7 @@ use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
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
@ -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;
}
@ -73,10 +82,19 @@ class AuthenticateUsingPasswordService
}
$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;
}

View file

@ -103,7 +103,6 @@ return [
'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',
@ -125,17 +124,21 @@ return [
'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',
@ -233,18 +236,6 @@ return [
'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.',
@ -298,7 +289,6 @@ return [
'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!.',

View file

@ -107,7 +107,6 @@ return [
'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',
@ -129,17 +128,21 @@ return [
'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',
@ -237,18 +240,6 @@ return [
'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.',
@ -302,7 +293,6 @@ return [
'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.',

View file

@ -103,7 +103,6 @@ return [
'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',
@ -125,17 +124,21 @@ return [
'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',
@ -229,18 +232,6 @@ return [
'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.',
@ -294,7 +285,6 @@ return [
'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.',

View file

@ -166,7 +166,7 @@
</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
@ -183,7 +183,7 @@
@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)

View file

@ -11,6 +11,7 @@ use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
class AuthenticateUsingPasswordServiceTest extends TestCase
{
@ -24,6 +25,11 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
*/
private $repository;
/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface|\Mockery\Mock
*/
private $subuserRepository;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface|\Mockery\Mock
*/
@ -38,6 +44,7 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
$this->keyProviderService = m::mock(DaemonKeyProviderService::class);
$this->repository = m::mock(ServerRepositoryInterface::class);
$this->subuserRepository = m::mock(SubuserRepositoryInterface::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
* 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
*/
@ -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('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);
}
@ -214,6 +223,6 @@ class AuthenticateUsingPasswordServiceTest extends TestCase
*/
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()
{
$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);

View file

@ -121,7 +121,7 @@ class SubuserCreationServiceTest extends TestCase
*/
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]);