Implement basic code for creating/updating a subuser
This commit is contained in:
parent
51c5cf4dbb
commit
a6f46d36ba
17 changed files with 347 additions and 322 deletions
|
@ -2,11 +2,17 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
|
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
|
||||||
|
use Pterodactyl\Services\Subusers\SubuserCreationService;
|
||||||
use Pterodactyl\Transformers\Api\Client\SubuserTransformer;
|
use Pterodactyl\Transformers\Api\Client\SubuserTransformer;
|
||||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest;
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest;
|
||||||
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest;
|
||||||
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest;
|
||||||
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest;
|
||||||
|
|
||||||
class SubuserController extends ClientApiController
|
class SubuserController extends ClientApiController
|
||||||
{
|
{
|
||||||
|
@ -15,16 +21,25 @@ class SubuserController extends ClientApiController
|
||||||
*/
|
*/
|
||||||
private $repository;
|
private $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Services\Subusers\SubuserCreationService
|
||||||
|
*/
|
||||||
|
private $creationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SubuserController constructor.
|
* SubuserController constructor.
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository
|
* @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository
|
||||||
|
* @param \Pterodactyl\Services\Subusers\SubuserCreationService $creationService
|
||||||
*/
|
*/
|
||||||
public function __construct(SubuserRepository $repository)
|
public function __construct(
|
||||||
{
|
SubuserRepository $repository,
|
||||||
|
SubuserCreationService $creationService
|
||||||
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->repository = $repository;
|
$this->repository = $repository;
|
||||||
|
$this->creationService = $creationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,10 +51,76 @@ class SubuserController extends ClientApiController
|
||||||
*/
|
*/
|
||||||
public function index(GetSubuserRequest $request, Server $server)
|
public function index(GetSubuserRequest $request, Server $server)
|
||||||
{
|
{
|
||||||
$server->subusers->load('user');
|
|
||||||
|
|
||||||
return $this->fractal->collection($server->subusers)
|
return $this->fractal->collection($server->subusers)
|
||||||
->transformWith($this->getTransformer(SubuserTransformer::class))
|
->transformWith($this->getTransformer(SubuserTransformer::class))
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new subuser for the given server.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest $request
|
||||||
|
* @param \Pterodactyl\Models\Server $server
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
|
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
|
||||||
|
* @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function store(StoreSubuserRequest $request, Server $server)
|
||||||
|
{
|
||||||
|
$response = $this->creationService->handle(
|
||||||
|
$server, $request->input('email'), $this->getDefaultPermissions($request)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->fractal->item($response)
|
||||||
|
->transformWith($this->getTransformer(SubuserTransformer::class))
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a given subuser in the system for the server.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest $request
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
|
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||||
|
*/
|
||||||
|
public function update(UpdateSubuserRequest $request)
|
||||||
|
{
|
||||||
|
$subuser = $request->subuser();
|
||||||
|
$this->repository->update($subuser->id, [
|
||||||
|
'permissions' => $this->getDefaultPermissions($request),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->fractal->item($subuser->refresh())
|
||||||
|
->transformWith($this->getTransformer(SubuserTransformer::class))
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a subusers from a server's assignment.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest $request
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function delete(DeleteSubuserRequest $request)
|
||||||
|
{
|
||||||
|
$this->repository->delete($request->subuser()->id);
|
||||||
|
|
||||||
|
return JsonResponse::create([], JsonResponse::HTTP_NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default permissions for all subusers to ensure none are ever removed wrongly.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getDefaultPermissions(Request $request): array
|
||||||
|
{
|
||||||
|
return array_merge($request->input('permissions') ?? [], ['websocket.*']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Subusers;
|
||||||
|
|
||||||
|
use Pterodactyl\Models\Server;
|
||||||
|
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
|
||||||
|
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
|
abstract class AbstractSubuserRequest extends ClientApiRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Models\Subuser|null
|
||||||
|
*/
|
||||||
|
protected $model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize the request and ensure that a user is not trying to modify themselves.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
if (! parent::authorize()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->subuser()->user_id === $this->user()->id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the subuser model for the given request which can then be validated. If
|
||||||
|
* required request parameters are missing a 404 error will be returned, otherwise
|
||||||
|
* a model exception will be returned if the model is not found.
|
||||||
|
*
|
||||||
|
* @return \Pterodactyl\Models\Subuser
|
||||||
|
*
|
||||||
|
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||||
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function subuser()
|
||||||
|
{
|
||||||
|
/** @var \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository */
|
||||||
|
$repository = $this->container->make(SubuserRepository::class);
|
||||||
|
|
||||||
|
$parameters = $this->route()->parameters();
|
||||||
|
if (
|
||||||
|
! isset($parameters['server'], $parameters['server'])
|
||||||
|
|| ! is_string($parameters['subuser'])
|
||||||
|
|| ! $parameters['server'] instanceof Server
|
||||||
|
) {
|
||||||
|
throw new NotFoundHttpException;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->model ?: $this->model = $repository->getUserForServer(
|
||||||
|
$this->route()->parameter('subuser'), $this->route()->parameter('server')->id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Subusers;
|
||||||
|
|
||||||
|
use Pterodactyl\Models\Permission;
|
||||||
|
|
||||||
|
class DeleteSubuserRequest extends AbstractSubuserRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function permission()
|
||||||
|
{
|
||||||
|
return Permission::ACTION_USER_DELETE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Subusers;
|
||||||
|
|
||||||
|
use Pterodactyl\Models\Permission;
|
||||||
|
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||||
|
|
||||||
|
class StoreSubuserRequest extends ClientApiRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function permission()
|
||||||
|
{
|
||||||
|
return Permission::ACTION_USER_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email' => 'required|email',
|
||||||
|
'permissions' => 'required|array',
|
||||||
|
'permissions.*' => 'string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Subusers;
|
||||||
|
|
||||||
|
use Pterodactyl\Models\Permission;
|
||||||
|
|
||||||
|
class UpdateSubuserRequest extends AbstractSubuserRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function permission()
|
||||||
|
{
|
||||||
|
return Permission::ACTION_USER_UPDATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'permissions' => 'required|array',
|
||||||
|
'permissions.*' => 'string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,7 +140,7 @@ abstract class Validable extends Model
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->getValidator()->setData(
|
return $this->getValidator()->setData(
|
||||||
$this->getAttributes()
|
$this->toArray()
|
||||||
)->passes();
|
)->passes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
namespace Pterodactyl\Repositories\Eloquent;
|
namespace Pterodactyl\Repositories\Eloquent;
|
||||||
|
|
||||||
use Pterodactyl\Models\Subuser;
|
use Pterodactyl\Models\Subuser;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||||
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
||||||
|
|
||||||
|
@ -20,19 +19,27 @@ class SubuserRepository extends EloquentRepository implements SubuserRepositoryI
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the subusers for the given server instance with the associated user
|
* Returns a subuser model for the given user and server combination. If no record
|
||||||
* and permission relationships pre-loaded.
|
* exists an exception will be thrown.
|
||||||
*
|
*
|
||||||
* @param int $server
|
* @param int $server
|
||||||
* @return \Illuminate\Support\Collection
|
* @param string $uuid
|
||||||
|
* @return \Pterodactyl\Models\Subuser
|
||||||
|
*
|
||||||
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||||
*/
|
*/
|
||||||
public function getSubusersForServer(int $server): Collection
|
public function getUserForServer(int $server, string $uuid): Subuser
|
||||||
{
|
{
|
||||||
return $this->getBuilder()
|
/** @var \Pterodactyl\Models\Subuser $model */
|
||||||
->with('user', 'permissions')
|
$model = $this->getBuilder()
|
||||||
->where('server_id', $server)
|
->with('server', 'user')
|
||||||
->get()
|
->select('subusers.*')
|
||||||
->toBase();
|
->join('users', 'users.id', '=', 'subusers.user_id')
|
||||||
|
->where('subusers.server_id', $server)
|
||||||
|
->where('users.uuid', $uuid)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Pterodactyl - Panel
|
|
||||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
|
||||||
*
|
|
||||||
* This software is licensed under the terms of the MIT license.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Pterodactyl\Services\Subusers;
|
|
||||||
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
use Pterodactyl\Models\Permission;
|
|
||||||
use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface;
|
|
||||||
|
|
||||||
class PermissionCreationService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PermissionCreationService constructor.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface $repository
|
|
||||||
*/
|
|
||||||
public function __construct(PermissionRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
$this->repository = $repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign permissions to a given subuser.
|
|
||||||
*
|
|
||||||
* @param int $subuser
|
|
||||||
* @param array $permissions
|
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
*/
|
|
||||||
public function handle($subuser, array $permissions)
|
|
||||||
{
|
|
||||||
Assert::integerish($subuser, 'First argument passed to handle must be an integer, received %s.');
|
|
||||||
|
|
||||||
$permissionMappings = Permission::getPermissions(true);
|
|
||||||
$insertPermissions = [];
|
|
||||||
|
|
||||||
foreach ($permissions as $permission) {
|
|
||||||
if (array_key_exists($permission, $permissionMappings)) {
|
|
||||||
Assert::stringNotEmpty($permission, 'Permission argument provided must be a non-empty string, received %s.');
|
|
||||||
|
|
||||||
array_push($insertPermissions, [
|
|
||||||
'subuser_id' => $subuser,
|
|
||||||
'permission' => $permission,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! empty($insertPermissions)) {
|
|
||||||
$this->repository->withoutFreshModel()->insert($insertPermissions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,13 +10,12 @@
|
||||||
namespace Pterodactyl\Services\Subusers;
|
namespace Pterodactyl\Services\Subusers;
|
||||||
|
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use Pterodactyl\Models\Subuser;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Pterodactyl\Services\Users\UserCreationService;
|
use Pterodactyl\Services\Users\UserCreationService;
|
||||||
|
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
|
||||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||||
use Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService;
|
|
||||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
|
||||||
use Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException;
|
use Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException;
|
||||||
use Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException;
|
use Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException;
|
||||||
|
|
||||||
|
@ -25,86 +24,61 @@ class SubuserCreationService
|
||||||
/**
|
/**
|
||||||
* @var \Illuminate\Database\ConnectionInterface
|
* @var \Illuminate\Database\ConnectionInterface
|
||||||
*/
|
*/
|
||||||
protected $connection;
|
private $connection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService
|
* @var \Pterodactyl\Repositories\Eloquent\SubuserRepository
|
||||||
*/
|
*/
|
||||||
protected $keyCreationService;
|
private $subuserRepository;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Services\Subusers\PermissionCreationService
|
|
||||||
*/
|
|
||||||
protected $permissionService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $subuserRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $serverRepository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Services\Users\UserCreationService
|
* @var \Pterodactyl\Services\Users\UserCreationService
|
||||||
*/
|
*/
|
||||||
protected $userCreationService;
|
private $userCreationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
||||||
*/
|
*/
|
||||||
protected $userRepository;
|
private $userRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SubuserCreationService constructor.
|
* SubuserCreationService constructor.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService
|
* @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $subuserRepository
|
||||||
* @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository
|
|
||||||
* @param \Pterodactyl\Services\Users\UserCreationService $userCreationService
|
* @param \Pterodactyl\Services\Users\UserCreationService $userCreationService
|
||||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
|
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ConnectionInterface $connection,
|
ConnectionInterface $connection,
|
||||||
DaemonKeyCreationService $keyCreationService,
|
SubuserRepository $subuserRepository,
|
||||||
PermissionCreationService $permissionService,
|
|
||||||
ServerRepositoryInterface $serverRepository,
|
|
||||||
SubuserRepositoryInterface $subuserRepository,
|
|
||||||
UserCreationService $userCreationService,
|
UserCreationService $userCreationService,
|
||||||
UserRepositoryInterface $userRepository
|
UserRepositoryInterface $userRepository
|
||||||
) {
|
) {
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
$this->keyCreationService = $keyCreationService;
|
|
||||||
$this->permissionService = $permissionService;
|
|
||||||
$this->serverRepository = $serverRepository;
|
|
||||||
$this->subuserRepository = $subuserRepository;
|
$this->subuserRepository = $subuserRepository;
|
||||||
$this->userRepository = $userRepository;
|
$this->userRepository = $userRepository;
|
||||||
$this->userCreationService = $userCreationService;
|
$this->userCreationService = $userCreationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|\Pterodactyl\Models\Server $server
|
* Creates a new user on the system and assigns them access to the provided server.
|
||||||
|
* If the email address already belongs to a user on the system a new user will not
|
||||||
|
* be created.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Models\Server $server
|
||||||
* @param string $email
|
* @param string $email
|
||||||
* @param array $permissions
|
* @param array $permissions
|
||||||
* @return \Pterodactyl\Models\Subuser
|
* @return \Pterodactyl\Models\Subuser
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
|
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException
|
* @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException
|
||||||
|
* @throws \Throwable
|
||||||
*/
|
*/
|
||||||
public function handle($server, $email, array $permissions)
|
public function handle(Server $server, string $email, array $permissions): Subuser
|
||||||
{
|
{
|
||||||
if (! $server instanceof Server) {
|
return $this->connection->transaction(function () use ($server, $email, $permissions) {
|
||||||
$server = $this->serverRepository->find($server);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->connection->beginTransaction();
|
|
||||||
try {
|
try {
|
||||||
$user = $this->userRepository->findFirstWhere([['email', '=', $email]]);
|
$user = $this->userRepository->findFirstWhere([['email', '=', $email]]);
|
||||||
|
|
||||||
|
@ -117,21 +91,20 @@ class SubuserCreationService
|
||||||
throw new ServerSubuserExistsException(trans('exceptions.subusers.subuser_exists'));
|
throw new ServerSubuserExistsException(trans('exceptions.subusers.subuser_exists'));
|
||||||
}
|
}
|
||||||
} catch (RecordNotFoundException $exception) {
|
} catch (RecordNotFoundException $exception) {
|
||||||
$username = preg_replace('/([^\w\.-]+)/', '', strtok($email, '@'));
|
|
||||||
$user = $this->userCreationService->handle([
|
$user = $this->userCreationService->handle([
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'username' => $username . str_random(3),
|
'username' => preg_replace('/([^\w\.-]+)/', '', strtok($email, '@')) . str_random(3),
|
||||||
'name_first' => 'Server',
|
'name_first' => 'Server',
|
||||||
'name_last' => 'Subuser',
|
'name_last' => 'Subuser',
|
||||||
'root_admin' => false,
|
'root_admin' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$subuser = $this->subuserRepository->create(['user_id' => $user->id, 'server_id' => $server->id]);
|
return $this->subuserRepository->create([
|
||||||
$this->keyCreationService->handle($server->id, $user->id);
|
'user_id' => $user->id,
|
||||||
$this->permissionService->handle($subuser->id, $permissions);
|
'server_id' => $server->id,
|
||||||
$this->connection->commit();
|
'permissions' => $permissions,
|
||||||
|
]);
|
||||||
return $subuser;
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Services\Subusers;
|
|
||||||
|
|
||||||
use Pterodactyl\Models\Subuser;
|
|
||||||
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
|
||||||
|
|
||||||
class SubuserDeletionService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SubuserDeletionService constructor.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
SubuserRepositoryInterface $repository
|
|
||||||
) {
|
|
||||||
$this->repository = $repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a subuser and their associated permissions from the Panel and Daemon.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Models\Subuser $subuser
|
|
||||||
*/
|
|
||||||
public function handle(Subuser $subuser)
|
|
||||||
{
|
|
||||||
$this->repository->delete($subuser->id);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Pterodactyl - Panel
|
|
||||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
|
||||||
*
|
|
||||||
* This software is licensed under the terms of the MIT license.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Pterodactyl\Services\Subusers;
|
|
||||||
|
|
||||||
use Pterodactyl\Models\Subuser;
|
|
||||||
use GuzzleHttp\Exception\RequestException;
|
|
||||||
use Illuminate\Database\ConnectionInterface;
|
|
||||||
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
|
|
||||||
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
|
||||||
use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface;
|
|
||||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
|
|
||||||
|
|
||||||
class SubuserUpdateService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Illuminate\Database\ConnectionInterface
|
|
||||||
*/
|
|
||||||
private $connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $daemonRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
|
|
||||||
*/
|
|
||||||
private $keyProviderService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $permissionRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Services\Subusers\PermissionCreationService
|
|
||||||
*/
|
|
||||||
private $permissionService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SubuserUpdateService constructor.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
|
||||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
|
|
||||||
* @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface $permissionRepository
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
ConnectionInterface $connection,
|
|
||||||
DaemonKeyProviderService $keyProviderService,
|
|
||||||
DaemonServerRepositoryInterface $daemonRepository,
|
|
||||||
PermissionCreationService $permissionService,
|
|
||||||
PermissionRepositoryInterface $permissionRepository,
|
|
||||||
SubuserRepositoryInterface $repository
|
|
||||||
) {
|
|
||||||
$this->connection = $connection;
|
|
||||||
$this->daemonRepository = $daemonRepository;
|
|
||||||
$this->keyProviderService = $keyProviderService;
|
|
||||||
$this->permissionRepository = $permissionRepository;
|
|
||||||
$this->permissionService = $permissionService;
|
|
||||||
$this->repository = $repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update permissions for a given subuser.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Models\Subuser $subuser
|
|
||||||
* @param array $permissions
|
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function handle(Subuser $subuser, array $permissions)
|
|
||||||
{
|
|
||||||
$subuser = $this->repository->loadServerAndUserRelations($subuser);
|
|
||||||
|
|
||||||
$this->connection->beginTransaction();
|
|
||||||
$this->permissionRepository->deleteWhere([['subuser_id', '=', $subuser->id]]);
|
|
||||||
$this->permissionService->handle($subuser->id, $permissions);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$token = $this->keyProviderService->handle($subuser->getRelation('server'), $subuser->getRelation('user'), false);
|
|
||||||
$this->daemonRepository->setServer($subuser->getRelation('server'))->revokeAccessKey($token);
|
|
||||||
} catch (RequestException $exception) {
|
|
||||||
$this->connection->rollBack();
|
|
||||||
throw new DaemonConnectionException($exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->connection->commit();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,16 +2,10 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Transformers\Api\Client;
|
namespace Pterodactyl\Transformers\Api\Client;
|
||||||
|
|
||||||
use Pterodactyl\Models\User;
|
|
||||||
use Pterodactyl\Models\Subuser;
|
use Pterodactyl\Models\Subuser;
|
||||||
|
|
||||||
class SubuserTransformer extends BaseClientTransformer
|
class SubuserTransformer extends BaseClientTransformer
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $defaultIncludes = ['user'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the resource name for the JSONAPI output.
|
* Return the resource name for the JSONAPI output.
|
||||||
*
|
*
|
||||||
|
@ -27,23 +21,13 @@ class SubuserTransformer extends BaseClientTransformer
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Models\Subuser $model
|
* @param \Pterodactyl\Models\Subuser $model
|
||||||
* @return array|void
|
* @return array|void
|
||||||
|
* @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException
|
||||||
*/
|
*/
|
||||||
public function transform(Subuser $model)
|
public function transform(Subuser $model)
|
||||||
{
|
{
|
||||||
return [
|
return array_merge(
|
||||||
'permissions' => $model->permissions->pluck('permission'),
|
$this->makeTransformer(UserTransformer::class)->transform($model->user),
|
||||||
];
|
['permissions' => $model->permissions]
|
||||||
}
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Include the permissions associated with this subuser.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Models\Subuser $model
|
|
||||||
* @return \League\Fractal\Resource\Item
|
|
||||||
* @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException
|
|
||||||
*/
|
|
||||||
public function includeUser(Subuser $model)
|
|
||||||
{
|
|
||||||
return $this->item($model->user, $this->makeTransformer(UserTransformer::class), User::RESOURCE_NAME);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
resources/scripts/api/server/users/createOrUpdateSubuser.ts
Normal file
18
resources/scripts/api/server/users/createOrUpdateSubuser.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import http from '@/api/http';
|
||||||
|
import { rawDataToServerSubuser } from '@/api/server/users/getServerSubusers';
|
||||||
|
import { Subuser } from '@/state/server/subusers';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
email: string;
|
||||||
|
permissions: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (uuid: string, params: Params, subuser?: Subuser): Promise<Subuser> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
http.post(`/api/client/servers/${uuid}/users${subuser ? `/${subuser.uuid}` : ''}`, {
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
.then(data => resolve(rawDataToServerSubuser(data.data)))
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
|
@ -8,13 +8,13 @@ export const rawDataToServerSubuser = (data: FractalResponseData): Subuser => ({
|
||||||
image: data.attributes.image,
|
image: data.attributes.image,
|
||||||
twoFactorEnabled: data.attributes['2fa_enabled'],
|
twoFactorEnabled: data.attributes['2fa_enabled'],
|
||||||
createdAt: new Date(data.attributes.created_at),
|
createdAt: new Date(data.attributes.created_at),
|
||||||
permissions: data.attributes.relationships!.permissions.attributes.permissions,
|
permissions: data.attributes.permissions || [],
|
||||||
can: permission => data.attributes.relationships!.permissions.attributes.permissions.indexOf(permission) >= 0,
|
can: permission => (data.attributes.permissions || []).indexOf(permission) >= 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default (uuid: string): Promise<Subuser[]> => {
|
export default (uuid: string): Promise<Subuser[]> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.get(`/api/client/servers/${uuid}/users`, { params: { include: [ 'permissions' ] } })
|
http.get(`/api/client/servers/${uuid}/users`)
|
||||||
.then(({ data }) => resolve((data.data || []).map(rawDataToServerSubuser)))
|
.then(({ data }) => resolve((data.data || []).map(rawDataToServerSubuser)))
|
||||||
.catch(reject);
|
.catch(reject);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
import React from 'react';
|
import React, { forwardRef, MutableRefObject, useRef } from 'react';
|
||||||
import { Subuser } from '@/state/server/subusers';
|
import { Subuser } from '@/state/server/subusers';
|
||||||
import { Formik, FormikHelpers, useFormikContext } from 'formik';
|
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||||
import { array, object, string } from 'yup';
|
import { array, object, string } from 'yup';
|
||||||
import Modal, { RequiredModalProps } from '@/components/elements/Modal';
|
import Modal, { RequiredModalProps } from '@/components/elements/Modal';
|
||||||
import Field from '@/components/elements/Field';
|
import Field from '@/components/elements/Field';
|
||||||
import { useStoreState } from 'easy-peasy';
|
import { Actions, useStoreActions, useStoreState } from 'easy-peasy';
|
||||||
import { ApplicationStore } from '@/state';
|
import { ApplicationStore } from '@/state';
|
||||||
import TitledGreyBox from '@/components/elements/TitledGreyBox';
|
import TitledGreyBox from '@/components/elements/TitledGreyBox';
|
||||||
import Checkbox from '@/components/elements/Checkbox';
|
import Checkbox from '@/components/elements/Checkbox';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import createOrUpdateSubuser from '@/api/server/users/createOrUpdateSubuser';
|
||||||
|
import { ServerContext } from '@/state/server';
|
||||||
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
subuser?: Subuser;
|
subuser?: Subuser;
|
||||||
|
@ -29,13 +33,14 @@ const PermissionLabel = styled.label`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const EditSubuserModal = ({ subuser, ...props }: Props) => {
|
const EditSubuserModal = forwardRef<HTMLHeadingElement, Props>(({ subuser, ...props }, ref) => {
|
||||||
const { values, isSubmitting, setFieldValue } = useFormikContext<Values>();
|
const { values, isSubmitting, setFieldValue } = useFormikContext<Values>();
|
||||||
const permissions = useStoreState((state: ApplicationStore) => state.permissions.data);
|
const permissions = useStoreState((state: ApplicationStore) => state.permissions.data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal {...props} showSpinnerOverlay={isSubmitting}>
|
<Modal {...props} showSpinnerOverlay={isSubmitting}>
|
||||||
<h3>{subuser ? 'Edit subuser' : 'Create new subuser'}</h3>
|
<h3 ref={ref}>{subuser ? 'Edit subuser' : 'Create new subuser'}</h3>
|
||||||
|
<FlashMessageRender byKey={'user:edit'} className={'mt-4'}/>
|
||||||
<div className={'mt-6'}>
|
<div className={'mt-6'}>
|
||||||
<Field
|
<Field
|
||||||
name={'email'}
|
name={'email'}
|
||||||
|
@ -115,25 +120,48 @@ const EditSubuserModal = ({ subuser, ...props }: Props) => {
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default (props: Props) => {
|
export default ({ subuser, ...props }: Props) => {
|
||||||
const submit = (values: Values, helpers: FormikHelpers<Values>) => {
|
const ref = useRef<HTMLHeadingElement>(null);
|
||||||
|
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
||||||
|
const appendSubuser = ServerContext.useStoreActions(actions => actions.subusers.appendSubuser);
|
||||||
|
|
||||||
|
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
||||||
|
|
||||||
|
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
||||||
|
clearFlashes('user:edit');
|
||||||
|
createOrUpdateSubuser(uuid, values, subuser)
|
||||||
|
.then(subuser => {
|
||||||
|
appendSubuser(subuser);
|
||||||
|
props.onDismissed();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
setSubmitting(false);
|
||||||
|
addError({ key: 'user:edit', message: httpErrorToHuman(error) });
|
||||||
|
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.scrollIntoView();
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Formik
|
<Formik
|
||||||
onSubmit={submit}
|
onSubmit={submit}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
email: '',
|
email: subuser?.email || '',
|
||||||
permissions: [],
|
permissions: subuser?.permissions || [],
|
||||||
} as Values}
|
} as Values}
|
||||||
validationSchema={object().shape({
|
validationSchema={object().shape({
|
||||||
email: string().email('A valid email address must be provided.').required('A valid email address must be provided.'),
|
email: string().email('A valid email address must be provided.').required('A valid email address must be provided.'),
|
||||||
permissions: array().of(string()),
|
permissions: array().of(string()),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<EditSubuserModal {...props}/>
|
<Form>
|
||||||
|
<EditSubuserModal ref={ref} subuser={subuser} {...props}/>
|
||||||
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,7 +39,7 @@ const subusers: ServerSubuserStore = {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
appendSubuser: action((state, payload) => {
|
appendSubuser: action((state, payload) => {
|
||||||
state.data = [ ...state.data, payload ];
|
state.data = [ ...state.data.filter(user => user.uuid !== payload.uuid), payload ];
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getSubusers: thunk(async (actions, payload) => {
|
getSubusers: thunk(async (actions, payload) => {
|
||||||
|
|
|
@ -81,6 +81,10 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ
|
||||||
|
|
||||||
Route::group(['prefix' => '/users'], function () {
|
Route::group(['prefix' => '/users'], function () {
|
||||||
Route::get('/', 'Servers\SubuserController@index');
|
Route::get('/', 'Servers\SubuserController@index');
|
||||||
|
Route::post('/', 'Servers\SubuserController@store');
|
||||||
|
Route::get('/{subuser}', 'Servers\SubuserController@view');
|
||||||
|
Route::post('/{subuser}', 'Servers\SubuserController@update');
|
||||||
|
Route::delete('/{subuser}', 'Servers\SubuserController@delete');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group(['prefix' => '/settings'], function () {
|
Route::group(['prefix' => '/settings'], function () {
|
||||||
|
|
Loading…
Reference in a new issue