Final adjustments to Daemon <-> Panel communication change

This commit is contained in:
Dane Everitt 2017-09-24 21:12:30 -05:00
parent 8e2b77dc1e
commit 7d1c233c49
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
32 changed files with 528 additions and 538 deletions

View file

@ -104,10 +104,7 @@ class RebuildServerCommand extends Command
];
try {
$this->daemonRepository->setNode($server->node_id)
->setAccessServer($server->uuid)
->setAccessToken($server->node->daemonSecret)
->update($json);
$this->daemonRepository->setNode($server->node_id)->setAccessServer($server->uuid)->update($json);
} catch (RequestException $exception) {
$this->output->error(trans('command/messages.server.rebuild_failed', [
'name' => $server->name,

View file

@ -36,15 +36,6 @@ interface ServerRepositoryInterface extends BaseRepositoryInterface
*/
public function create($id, array $overrides = [], $start = false);
/**
* Set an access token and associated permissions for a server.
*
* @param string $key
* @param array $permissions
* @return \Psr\Http\Message\ResponseInterface
*/
public function setSubuserKey($key, array $permissions);
/**
* Update server details on the daemon.
*
@ -95,4 +86,12 @@ interface ServerRepositoryInterface extends BaseRepositoryInterface
* @return \Psr\Http\Message\ResponseInterface
*/
public function details();
/**
* Revoke an access key on the daemon before the time is expired.
*
* @param string $key
* @return \Psr\Http\Message\ResponseInterface
*/
public function revokeAccessKey($key);
}

View file

@ -26,6 +26,11 @@ namespace Pterodactyl\Contracts\Repository;
interface DaemonKeyRepositoryInterface extends RepositoryInterface
{
/**
* String prepended to keys to identify that they are managed internally and not part of the user API.
*/
const INTERNAL_KEY_IDENTIFIER = 'i_';
/**
* Gets the daemon keys associated with a specific server.
*

View file

@ -30,7 +30,7 @@ interface SubuserRepositoryInterface extends RepositoryInterface
* Return a subuser with the associated server relationship.
*
* @param int $id
* @return \Illuminate\Database\Eloquent\Collection
* @return \Pterodactyl\Models\Subuser
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/

View file

@ -1,31 +0,0 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Exceptions\Service\Server;
use Pterodactyl\Exceptions\PterodactylException;
class UserNotLinkedToServerException extends PterodactylException
{
}

View file

@ -30,7 +30,6 @@ use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Testing\HttpException;
use League\Fractal\Serializer\JsonApiSerializer;
use Pterodactyl\Transformers\Daemon\ApiKeyTransformer;
use Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
class ValidateKeyController extends Controller
@ -78,7 +77,7 @@ class ValidateKeyController extends Controller
*/
public function index($token)
{
if (! starts_with($token, DaemonKeyUpdateService::INTERNAL_TOKEN_IDENTIFIER)) {
if (! starts_with($token, DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER)) {
throw new HttpException(501);
}

View file

@ -29,22 +29,22 @@ use Illuminate\Http\Request;
use GuzzleHttp\Exception\RequestException;
use Pterodactyl\Http\Controllers\Controller;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Pterodactyl\Services\Servers\ServerAccessHelperService;
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
class IndexController extends Controller
{
/**
* @var \Pterodactyl\Services\Servers\ServerAccessHelperService
*/
protected $serverAccessHelper;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
*/
protected $daemonRepository;
/**
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
*/
protected $keyProviderService;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
@ -53,17 +53,17 @@ class IndexController extends Controller
/**
* IndexController constructor.
*
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
* @param \Pterodactyl\Services\Servers\ServerAccessHelperService $serverAccessHelper
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
*/
public function __construct(
DaemonKeyProviderService $keyProviderService,
DaemonServerRepositoryInterface $daemonRepository,
ServerAccessHelperService $serverAccessHelper,
ServerRepositoryInterface $repository
) {
$this->serverAccessHelper = $serverAccessHelper;
$this->daemonRepository = $daemonRepository;
$this->keyProviderService = $keyProviderService;
$this->repository = $repository;
}
@ -93,7 +93,7 @@ class IndexController extends Controller
public function status(Request $request, $uuid)
{
$server = $this->repository->findFirstWhere([['uuidShort', '=', $uuid]]);
$token = $this->serverAccessHelper->handle($server, $request->user());
$token = $this->keyProviderService->handle($server->id, $request->user()->id);
if (! $server->installed) {
return response()->json(['status' => 20]);

View file

@ -28,15 +28,15 @@ use Closure;
use Illuminate\Http\Request;
use Illuminate\Contracts\Session\Session;
use Illuminate\Auth\AuthenticationException;
use Pterodactyl\Services\Servers\ServerAccessHelperService;
use Pterodactyl\Exceptions\Service\Server\UserNotLinkedToServerException;
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
class SubuserAccessAuthenticate
{
/**
* @var \Pterodactyl\Services\Servers\ServerAccessHelperService
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
*/
protected $accessHelperService;
protected $keyProviderService;
/**
* @var \Illuminate\Contracts\Session\Session
@ -46,23 +46,26 @@ class SubuserAccessAuthenticate
/**
* SubuserAccessAuthenticate constructor.
*
* @param \Pterodactyl\Services\Servers\ServerAccessHelperService $accessHelperService
* @param \Illuminate\Contracts\Session\Session $session
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
* @param \Illuminate\Contracts\Session\Session $session
*/
public function __construct(
ServerAccessHelperService $accessHelperService,
DaemonKeyProviderService $keyProviderService,
Session $session
) {
$this->accessHelperService = $accessHelperService;
$this->keyProviderService = $keyProviderService;
$this->session = $session;
}
/**
* Determine if a subuser has permissions to access a server, if so set thier access token.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*
* @throws \Illuminate\Auth\AuthenticationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle(Request $request, Closure $next)
@ -70,9 +73,9 @@ class SubuserAccessAuthenticate
$server = $this->session->get('server_data.model');
try {
$token = $this->accessHelperService->handle($server, $request->user());
$token = $this->keyProviderService->handle($server->id, $request->user()->id);
$this->session->now('server_data.token', $token);
} catch (UserNotLinkedToServerException $exception) {
} catch (RecordNotFoundException $exception) {
throw new AuthenticationException('This account does not have permission to access this server.');
}

View file

@ -110,13 +110,13 @@ class RunTaskJob extends Job implements ShouldQueue
case 'power':
$this->powerRepository->setNode($server->node_id)
->setAccessServer($server->uuid)
->setAccessToken($server->daemonSecret)
->setAccessToken($server->accessToken->secret)
->sendSignal($task->payload);
break;
case 'command':
$this->commandRepository->setNode($server->node_id)
->setAccessServer($server->uuid)
->setAccessToken($server->daemonSecret)
->setAccessToken($server->accessToken->secret)
->send($task->payload);
break;
default:

View file

@ -25,12 +25,14 @@
namespace Pterodactyl\Models;
use Sofa\Eloquence\Eloquence;
use Sofa\Eloquence\Validable;
use Illuminate\Database\Eloquent\Model;
use Sofa\Eloquence\Contracts\CleansAttributes;
use Sofa\Eloquence\Contracts\Validable as ValidableContract;
class Permission extends Model implements CleansAttributes
class Permission extends Model implements CleansAttributes, ValidableContract
{
use Eloquence;
use Eloquence, Validable;
/**
* Should timestamps be used on this model.
@ -62,6 +64,22 @@ class Permission extends Model implements CleansAttributes
'subuser_id' => 'integer',
];
/**
* @var array
*/
protected static $applicationRules = [
'subuser_id' => 'required',
'permission' => 'required',
];
/**
* @var array
*/
protected static $dataIntegrityRules = [
'subuser_id' => 'numeric|min:1',
'permission' => 'string',
];
/**
* A list of all permissions available for a user.
*

View file

@ -58,6 +58,13 @@ class Server extends Model implements CleansAttributes, ValidableContract
*/
protected $dates = ['deleted_at'];
/**
* Always eager load these relationships on the model.
*
* @var array
*/
protected $with = ['key'];
/**
* Fields that are not mass assignable.
*
@ -286,7 +293,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function ownerKey()
public function key()
{
return $this->hasOne(DaemonKey::class, 'user_id', 'owner_id');
}

View file

@ -104,4 +104,14 @@ class Subuser extends Model implements CleansAttributes, ValidableContract
{
return $this->hasMany(Permission::class);
}
/**
* Return the key that belongs to this subuser for the server.
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function key()
{
return $this->hasOne(DaemonKey::class, 'server_id', 'server_id')->where('daemon_keys.user_id', '=', $this->user_id);
}
}

View file

@ -24,14 +24,11 @@
namespace Pterodactyl\Models;
use Hash;
use Google2FA;
use Sofa\Eloquence\Eloquence;
use Sofa\Eloquence\Validable;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Pterodactyl\Exceptions\DisplayException;
use Sofa\Eloquence\Contracts\CleansAttributes;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
@ -69,7 +66,18 @@ class User extends Model implements
*
* @var array
*/
protected $fillable = ['username', 'email', 'name_first', 'name_last', 'password', 'language', 'use_totp', 'totp_secret', 'gravatar', 'root_admin'];
protected $fillable = [
'username',
'email',
'name_first',
'name_last',
'password',
'language',
'use_totp',
'totp_secret',
'gravatar',
'root_admin',
];
/**
* Cast values to correct type.
@ -144,46 +152,6 @@ class User extends Model implements
'totp_secret' => 'nullable|string',
];
/**
* Enables or disables TOTP on an account if the token is valid.
*
* @param int $token
* @return bool
* @deprecated
*/
public function toggleTotp($token)
{
if (! Google2FA::verifyKey($this->totp_secret, $token, 1)) {
return false;
}
$this->use_totp = ! $this->use_totp;
return $this->save();
}
/**
* Set a user password to a new value assuming it meets the following requirements:
* - 8 or more characters in length
* - at least one uppercase character
* - at least one lowercase character
* - at least one number.
*
* @param string $password
* @param string $regex
* @throws \Pterodactyl\Exceptions\DisplayException
* @deprecated
*/
public function setPassword($password, $regex = '((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})')
{
if (! preg_match($regex, $password)) {
throw new DisplayException('The password passed did not meet the minimum password requirements.');
}
$this->password = Hash::make($password);
$this->save();
}
/**
* Send the password reset notification.
*
@ -194,102 +162,6 @@ class User extends Model implements
$this->notify(new ResetPasswordNotification($token));
}
/**
* Return true or false depending on wether the user is root admin or not.
*
* @return bool
* @deprecated
*/
public function isRootAdmin()
{
return $this->root_admin;
}
/**
* Returns the user's daemon secret for a given server.
*
* @param \Pterodactyl\Models\Server $server
* @return null|string
*/
public function daemonToken(Server $server)
{
if ($this->id === $server->owner_id || $this->isRootAdmin()) {
return $server->daemonSecret;
}
$subuser = $this->subuserOf->where('server_id', $server->id)->first();
return ($subuser) ? $subuser->daemonSecret : null;
}
/**
* Returns an array of all servers a user is able to access.
* Note: does not account for user admin status.
*
* @return array
*/
public function serverAccessArray()
{
return Server::select('id')->where('owner_id', $this->id)->union(
Subuser::select('server_id')->where('user_id', $this->id)
)->pluck('id')->all();
}
/**
* Change the access level for a given call to `access()` on the user.
*
* @param string $level can be all, admin, subuser, owner
* @return $this
*/
public function setAccessLevel($level = 'all')
{
if (! in_array($level, ['all', 'admin', 'subuser', 'owner'])) {
$level = 'all';
}
$this->accessLevel = $level;
return $this;
}
/**
* Returns an array of all servers a user is able to access.
* Note: does not account for user admin status.
*
* @param array $load
* @return \Pterodactyl\Models\Server
*/
public function access(...$load)
{
if (count($load) > 0 && is_null($load[0])) {
$query = Server::query();
} else {
$query = Server::with(! empty($load) ? $load : ['service', 'node', 'allocation']);
}
// If access level is set to owner, only display servers
// that the user owns.
if ($this->accessLevel === 'owner') {
$query->where('owner_id', $this->id);
}
// If set to all, display all servers they can access, including
// those they access as an admin.
//
// If set to subuser, only return the servers they can access because
// they are owner, or marked as a subuser of the server.
if (($this->accessLevel === 'all' && ! $this->isRootAdmin()) || $this->accessLevel === 'subuser') {
$query->whereIn('id', $this->serverAccessArray());
}
// If set to admin, only display the servers a user can access
// as an administrator (leaves out owned and subuser of).
if ($this->accessLevel === 'admin' && $this->isRootAdmin()) {
$query->whereNotIn('id', $this->serverAccessArray());
}
return $query;
}
/**
* Store the username as a lowecase string.
*
@ -339,4 +211,14 @@ class User extends Model implements
{
return $this->hasMany(Subuser::class);
}
/**
* Return all of the daemon keys that a user belongs to.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function keys()
{
return $this->hasMany(DaemonKey::class);
}
}

View file

@ -31,8 +31,6 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface as DatabaseServer
class ServerRepository extends BaseRepository implements ServerRepositoryInterface
{
const DAEMON_PERMISSIONS = ['s:*'];
/**
* {@inheritdoc}
*/
@ -73,9 +71,6 @@ class ServerRepository extends BaseRepository implements ServerRepositoryInterfa
],
'rebuild' => false,
'start_on_completion' => $start,
'keys' => [
(string) $server->daemonSecret => self::DAEMON_PERMISSIONS,
],
];
// Loop through overrides.
@ -88,22 +83,6 @@ class ServerRepository extends BaseRepository implements ServerRepositoryInterfa
]);
}
/**
* {@inheritdoc}
*/
public function setSubuserKey($key, array $permissions)
{
Assert::stringNotEmpty($key, 'First argument passed to setSubuserKey must be a non-empty string, received %s.');
return $this->getHttpClient()->request('PATCH', '/server', [
'json' => [
'keys' => [
$key => $permissions,
],
],
]);
}
/**
* {@inheritdoc}
*/
@ -169,4 +148,14 @@ class ServerRepository extends BaseRepository implements ServerRepositoryInterfa
{
return $this->getHttpClient()->request('GET', '/servers');
}
/**
* {@inheritdoc}
*/
public function revokeAccessKey($key)
{
Assert::stringNotEmpty($key, 'First argument passed to revokeAccessKey must be a non-empty string, received %s.');
return $this->getHttpClient()->request('DELETE', '/keys/' . $key);
}
}

View file

@ -0,0 +1,91 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Services\DaemonKeys;
use Carbon\Carbon;
use Webmozart\Assert\Assert;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
class DaemonKeyCreationService
{
/**
* @var \Carbon\Carbon
*/
protected $carbon;
/**
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
*/
protected $repository;
/**
* DaemonKeyCreationService constructor.
*
* @param \Carbon\Carbon $carbon
* @param \Illuminate\Contracts\Config\Repository $config
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository
*/
public function __construct(
Carbon $carbon,
ConfigRepository $config,
DaemonKeyRepositoryInterface $repository
) {
$this->carbon = $carbon;
$this->config = $config;
$this->repository = $repository;
}
/**
* Create a new daemon key to be used when connecting to a daemon.
*
* @param int $server
* @param int $user
* @return string
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function handle($server, $user)
{
Assert::integerish($server, 'First argument passed to handle must be an integer, received %s.');
Assert::integerish($user, 'Second argument passed to handle must be an integer, received %s.');
$secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40);
$this->repository->withoutFresh()->create([
'user_id' => $user,
'server_id' => $server,
'secret' => $secret,
'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time'))->toDateTimeString(),
]);
return $secret;
}
}

View file

@ -0,0 +1,124 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Services\DaemonKeys;
use Illuminate\Log\Writer;
use Webmozart\Assert\Assert;
use Pterodactyl\Models\Server;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
class DaemonKeyDeletionService
{
/**
* @var \Illuminate\Database\ConnectionInterface
*/
protected $connection;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
*/
protected $daemonRepository;
/**
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
protected $serverRepository;
/**
* @var \Illuminate\Log\Writer
*/
protected $writer;
/**
* DaemonKeyDeletionService constructor.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
* @param \Illuminate\Log\Writer $writer
*/
public function __construct(
ConnectionInterface $connection,
DaemonKeyRepositoryInterface $repository,
DaemonServerRepositoryInterface $daemonRepository,
ServerRepositoryInterface $serverRepository,
Writer $writer
) {
$this->connection = $connection;
$this->daemonRepository = $daemonRepository;
$this->repository = $repository;
$this->serverRepository = $serverRepository;
$this->writer = $writer;
}
/**
* @param \Pterodactyl\Models\Server|int $server
* @param int $user
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle($server, $user)
{
Assert::integerish($user, 'Second argument passed to handle must be an integer, received %s.');
if (! $server instanceof Server) {
$server = $this->serverRepository->find($server);
}
$this->connection->beginTransaction();
$key = $this->repository->findFirstWhere([
['user_id', '=', $user],
['server_id', '=', $server->id],
]);
$this->repository->delete($key->id);
try {
$this->daemonRepository->setNode($server->node_id)->revokeAccessKey($key->secret);
} catch (RequestException $exception) {
$response = $exception->getResponse();
$this->connection->rollBack();
$this->writer->warning($exception);
throw new DisplayException(trans('admin/server.exceptions.daemon_exception', [
'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(),
]));
}
$this->connection->commit();
}
}

View file

@ -0,0 +1,92 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Services\DaemonKeys;
use Carbon\Carbon;
use Webmozart\Assert\Assert;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
class DaemonKeyProviderService
{
/**
* @var \Carbon\Carbon
*/
protected $carbon;
/**
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService
*/
protected $keyUpdateService;
/**
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
*/
protected $repository;
/**
* GetDaemonKeyService constructor.
*
* @param \Carbon\Carbon $carbon
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService $keyUpdateService
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository
*/
public function __construct(
Carbon $carbon,
DaemonKeyUpdateService $keyUpdateService,
DaemonKeyRepositoryInterface $repository
) {
$this->carbon = $carbon;
$this->keyUpdateService = $keyUpdateService;
$this->repository = $repository;
}
/**
* Get the access key for a user on a specific server.
*
* @param int $server
* @param int $user
* @param bool $updateIfExpired
* @return string
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle($server, $user, $updateIfExpired = true)
{
Assert::integerish($server, 'First argument passed to handle must be an integer, received %s.');
Assert::integerish($user, 'Second argument passed to handle must be an integer, received %s.');
$key = $this->repository->findFirstWhere([
['user_id', '=', $user],
['server_id', '=', $server],
]);
if (! $updateIfExpired || max($this->carbon->now()->diffInSeconds($key->expires_at, false), 0) > 0) {
return $key->secret;
}
return $this->keyUpdateService->handle($key->id);
}
}

View file

@ -25,21 +25,19 @@
namespace Pterodactyl\Services\DaemonKeys;
use Carbon\Carbon;
use Pterodactyl\Models\DaemonKey;
use Webmozart\Assert\Assert;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
class DaemonKeyUpdateService
{
const INTERNAL_TOKEN_IDENTIFIER = 'i_';
/**
* @var \Carbon\Carbon
*/
protected $carbon;
/**
* @var
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
@ -68,7 +66,7 @@ class DaemonKeyUpdateService
/**
* Update a daemon key to expire the previous one.
*
* @param \Pterodactyl\Models\DaemonKey|int $key
* @param int $key
* @return string
*
* @throws \RuntimeException
@ -77,15 +75,12 @@ class DaemonKeyUpdateService
*/
public function handle($key)
{
if ($key instanceof DaemonKey) {
$key = $key->id;
}
$secret = self::INTERNAL_TOKEN_IDENTIFIER . str_random(40);
Assert::integerish($key, 'First argument passed to handle must be an integer, received %s.');
$secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40);
$this->repository->withoutFresh()->update($key, [
'secret' => $secret,
'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time')),
'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time'))->toDateTimeString(),
]);
return $secret;

View file

@ -90,7 +90,7 @@ class NodeUpdateService
$updateResponse = $this->repository->withoutFresh()->update($node->id, $data);
try {
$this->configRepository->setNode($node->id)->setAccessToken($node->daemonSecret)->update();
$this->configRepository->setNode($node->id)->update();
} catch (RequestException $exception) {
$response = $exception->getResponse();
$this->writer->warning($exception);

View file

@ -26,25 +26,36 @@ namespace Pterodactyl\Services\Servers;
use Illuminate\Log\Writer;
use Pterodactyl\Models\Server;
use Illuminate\Database\DatabaseManager;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Services\Nodes\NodeCreationService;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService;
use Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService;
use Pterodactyl\Repositories\Daemon\ServerRepository as DaemonServerRepository;
class DetailsModificationService
{
/**
* @var \Illuminate\Database\DatabaseManager
* @var \Illuminate\Database\ConnectionInterface
*/
protected $database;
protected $connection;
/**
* @var \Pterodactyl\Repositories\Daemon\ServerRepository
*/
protected $daemonServerRepository;
/**
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService
*/
protected $keyCreationService;
/**
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService
*/
private $keyDeletionService;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
@ -58,19 +69,25 @@ class DetailsModificationService
/**
* DetailsModificationService constructor.
*
* @param \Illuminate\Database\DatabaseManager $database
* @param \Pterodactyl\Repositories\Daemon\ServerRepository $daemonServerRepository
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
* @param \Illuminate\Log\Writer $writer
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService $keyDeletionService
* @param \Pterodactyl\Repositories\Daemon\ServerRepository $daemonServerRepository
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
* @param \Illuminate\Log\Writer $writer
*/
public function __construct(
DatabaseManager $database,
ConnectionInterface $connection,
DaemonKeyCreationService $keyCreationService,
DaemonKeyDeletionService $keyDeletionService,
DaemonServerRepository $daemonServerRepository,
ServerRepository $repository,
Writer $writer
) {
$this->database = $database;
$this->connection = $connection;
$this->daemonServerRepository = $daemonServerRepository;
$this->keyCreationService = $keyCreationService;
$this->keyDeletionService = $keyDeletionService;
$this->repository = $repository;
$this->writer = $writer;
}
@ -80,7 +97,6 @@ class DetailsModificationService
*
* @param int|\Pterodactyl\Models\Server $server
* @param array $data
* @return bool
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
@ -92,46 +108,19 @@ class DetailsModificationService
$server = $this->repository->find($server);
}
$this->database->beginTransaction();
$currentSecret = $server->daemonSecret;
if (
(isset($data['reset_token']) && ! is_null($data['reset_token'])) ||
(isset($data['owner_id']) && $data['owner_id'] != $server->owner_id)
) {
$data['daemonSecret'] = str_random(NodeCreationService::DAEMON_SECRET_LENGTH);
$shouldUpdate = true;
}
$this->connection->beginTransaction();
$this->repository->withoutFresh()->update($server->id, [
'owner_id' => array_get($data, 'owner_id') ?? $server->owner_id,
'name' => array_get($data, 'name') ?? $server->name,
'description' => array_get($data, 'description') ?? $server->description,
'daemonSecret' => array_get($data, 'daemonSecret') ?? $server->daemonSecret,
], true, true);
// If there are no updates, lets save the changes and return.
if (! isset($shouldUpdate)) {
return $this->database->commit();
if (array_get($data, 'owner_id') != $server->owner_id) {
$this->keyDeletionService->handle($server, $server->owner_id);
$this->keyCreationService->handle($server->id, array_get($data, 'owner_id'));
}
try {
$this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->update([
'keys' => [
(string) $currentSecret => [],
(string) $data['daemonSecret'] => $this->daemonServerRepository::DAEMON_PERMISSIONS,
],
]);
return $this->database->commit();
} catch (RequestException $exception) {
$response = $exception->getResponse();
$this->writer->warning($exception);
throw new DisplayException(trans('admin/server.exceptions.daemon_exception', [
'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(),
]));
}
$this->connection->commit();
}
/**
@ -142,6 +131,7 @@ class DetailsModificationService
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function setDockerImage($server, $image)
{
@ -149,7 +139,7 @@ class DetailsModificationService
$server = $this->repository->find($server);
}
$this->database->beginTransaction();
$this->connection->beginTransaction();
$this->repository->withoutFresh()->update($server->id, ['image' => $image]);
try {
@ -158,9 +148,8 @@ class DetailsModificationService
'image' => $image,
],
]);
$this->database->commit();
} catch (RequestException $exception) {
$this->connection->rollBack();
$response = $exception->getResponse();
$this->writer->warning($exception);
@ -168,5 +157,7 @@ class DetailsModificationService
'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(),
]));
}
$this->connection->commit();
}
}

View file

@ -1,131 +0,0 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Services\Servers;
use Carbon\Carbon;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\DaemonKey;
use Illuminate\Cache\Repository as CacheRepository;
use Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
use Pterodactyl\Exceptions\Service\Server\UserNotLinkedToServerException;
class ServerAccessHelperService
{
/**
* @var \Illuminate\Cache\Repository
*/
protected $cache;
/**
* @var \Carbon\Carbon
*/
protected $carbon;
/**
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
*/
protected $daemonKeyRepository;
/**
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService
*/
protected $daemonKeyUpdateService;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
*/
protected $userRepository;
/**
* ServerAccessHelperService constructor.
*
* @param \Illuminate\Cache\Repository $cache
* @param \Carbon\Carbon $carbon
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $daemonKeyRepository
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService $daemonKeyUpdateService
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
*/
public function __construct(
CacheRepository $cache,
Carbon $carbon,
DaemonKeyRepositoryInterface $daemonKeyRepository,
DaemonKeyUpdateService $daemonKeyUpdateService,
ServerRepositoryInterface $repository,
UserRepositoryInterface $userRepository
) {
$this->cache = $cache;
$this->carbon = $carbon;
$this->daemonKeyRepository = $daemonKeyRepository;
$this->daemonKeyUpdateService = $daemonKeyUpdateService;
$this->repository = $repository;
$this->userRepository = $userRepository;
}
/**
* Return the daemon secret to use when making a connection.
*
* @param int|\Pterodactyl\Models\Server $server
* @param int|\Pterodactyl\Models\User $user
* @return string
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Server\UserNotLinkedToServerException
* @throws \RuntimeException
*/
public function handle($server, $user)
{
if (! $server instanceof Server) {
$server = $this->repository->find($server);
}
if (! $user instanceof User) {
$user = $this->userRepository->find($user);
}
$keys = $server->relationLoaded('keys') ? $server->keys : $this->daemonKeyRepository->getServerKeys($server->id);
$key = $keys->where('user_id', $user->root_admin ? $server->owner_id : $user->id)->first();
if (is_null($key)) {
throw new UserNotLinkedToServerException;
}
if (max($this->carbon->now()->diffInSeconds($key->expires_at, false), 0) === 0) {
$key = $this->daemonKeyUpdateService->handle($key);
}
return ($key instanceof DaemonKey) ? $key->secret : $key;
}
}

View file

@ -24,16 +24,12 @@
namespace Pterodactyl\Services\Subusers;
use Webmozart\Assert\Assert;
use Pterodactyl\Models\Permission;
use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface;
class PermissionCreationService
{
const CORE_DAEMON_PERMISSIONS = [
's:get',
's:console',
];
/**
* @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface
*/
@ -54,21 +50,19 @@ class PermissionCreationService
*
* @param int $subuser
* @param array $permissions
* @return array
*
* @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);
$daemonPermissions = self::CORE_DAEMON_PERMISSIONS;
$insertPermissions = [];
foreach ($permissions as $permission) {
if (array_key_exists($permission, $permissionMappings)) {
if (! is_null($permissionMappings[$permission])) {
array_push($daemonPermissions, $permissionMappings[$permission]);
}
Assert::stringNotEmpty($permission, 'Permission argument provided must be a non-empty string, received %s.');
array_push($insertPermissions, [
'subuser_id' => $subuser,
@ -77,8 +71,6 @@ class PermissionCreationService
}
}
$this->repository->insert($insertPermissions);
return $daemonPermissions;
$this->repository->withoutFresh()->insert($insertPermissions);
}
}

View file

@ -24,20 +24,16 @@
namespace Pterodactyl\Services\Subusers;
use Illuminate\Log\Writer;
use Pterodactyl\Models\Server;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Services\Nodes\NodeCreationService;
use Pterodactyl\Services\Users\UserCreationService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService;
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\ServerSubuserExistsException;
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
class SubuserCreationService
{
@ -47,9 +43,9 @@ class SubuserCreationService
protected $connection;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService
*/
protected $daemonRepository;
protected $keyCreationService;
/**
* @var \Pterodactyl\Services\Subusers\PermissionCreationService
@ -76,41 +72,33 @@ class SubuserCreationService
*/
protected $userRepository;
/**
* @var \Illuminate\Log\Writer
*/
protected $writer;
/**
* SubuserCreationService constructor.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Services\Users\UserCreationService $userCreationService
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
* @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
* @param \Illuminate\Log\Writer $writer
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Services\Users\UserCreationService $userCreationService
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService
* @param \Pterodactyl\Services\Subusers\PermissionCreationService $permissionService
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
*/
public function __construct(
ConnectionInterface $connection,
UserCreationService $userCreationService,
DaemonServerRepositoryInterface $daemonRepository,
DaemonKeyCreationService $keyCreationService,
PermissionCreationService $permissionService,
ServerRepositoryInterface $serverRepository,
SubuserRepositoryInterface $subuserRepository,
UserRepositoryInterface $userRepository,
Writer $writer
UserRepositoryInterface $userRepository
) {
$this->connection = $connection;
$this->daemonRepository = $daemonRepository;
$this->keyCreationService = $keyCreationService;
$this->permissionService = $permissionService;
$this->subuserRepository = $subuserRepository;
$this->serverRepository = $serverRepository;
$this->userRepository = $userRepository;
$this->userCreationService = $userCreationService;
$this->writer = $writer;
}
/**
@ -120,7 +108,6 @@ class SubuserCreationService
* @return \Pterodactyl\Models\Subuser
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
@ -154,28 +141,11 @@ class SubuserCreationService
]);
}
$subuser = $this->subuserRepository->create([
'user_id' => $user->id,
'server_id' => $server->id,
'daemonSecret' => str_random(NodeCreationService::DAEMON_SECRET_LENGTH),
]);
$subuser = $this->subuserRepository->create(['user_id' => $user->id, 'server_id' => $server->id]);
$this->keyCreationService->handle($server->id, $user->id);
$this->permissionService->handle($subuser->id, $permissions);
$this->connection->commit();
$daemonPermissions = $this->permissionService->handle($subuser->id, $permissions);
try {
$this->daemonRepository->setNode($server->node_id)->setAccessServer($server->uuid)
->setSubuserKey($subuser->daemonSecret, $daemonPermissions);
$this->connection->commit();
return $subuser;
} catch (RequestException $exception) {
$this->connection->rollBack();
$this->writer->warning($exception);
$response = $exception->getResponse();
throw new DisplayException(trans('exceptions.daemon_connection_failed', [
'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(),
]));
}
return $subuser;
}
}

View file

@ -24,12 +24,9 @@
namespace Pterodactyl\Services\Subusers;
use Illuminate\Log\Writer;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
class SubuserDeletionService
{
@ -39,45 +36,36 @@ class SubuserDeletionService
protected $connection;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService
*/
protected $daemonRepository;
protected $keyDeletionService;
/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
*/
protected $repository;
/**
* @var \Illuminate\Log\Writer
*/
protected $writer;
/**
* SubuserDeletionService constructor.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository
* @param \Illuminate\Log\Writer $writer
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyDeletionService $keyDeletionService
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository
*/
public function __construct(
ConnectionInterface $connection,
DaemonServerRepositoryInterface $daemonRepository,
SubuserRepositoryInterface $repository,
Writer $writer
DaemonKeyDeletionService $keyDeletionService,
SubuserRepositoryInterface $repository
) {
$this->connection = $connection;
$this->daemonRepository = $daemonRepository;
$this->keyDeletionService = $keyDeletionService;
$this->repository = $repository;
$this->writer = $writer;
}
/**
* Delete a subuser and their associated permissions from the Panel and Daemon.
*
* @param int $subuser
* @return int|null
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
@ -87,22 +75,8 @@ class SubuserDeletionService
$subuser = $this->repository->getWithServer($subuser);
$this->connection->beginTransaction();
$response = $this->repository->delete($subuser->id);
try {
$this->daemonRepository->setNode($subuser->server->node_id)->setAccessServer($subuser->server->uuid)
->setSubuserKey($subuser->daemonSecret, []);
$this->connection->commit();
return $response;
} catch (RequestException $exception) {
$this->connection->rollBack();
$this->writer->warning($exception);
$response = $exception->getResponse();
throw new DisplayException(trans('exceptions.daemon_connection_failed', [
'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(),
]));
}
$this->keyDeletionService->handle($subuser->server_id, $subuser->user_id);
$this->repository->delete($subuser->id);
$this->connection->commit();
}
}

View file

@ -28,6 +28,7 @@ use Illuminate\Log\Writer;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface;
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
@ -44,6 +45,11 @@ class SubuserUpdateService
*/
protected $daemonRepository;
/**
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
*/
private $keyProviderService;
/**
* @var \Pterodactyl\Contracts\Repository\PermissionRepositoryInterface
*/
@ -68,6 +74,7 @@ class SubuserUpdateService
* 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
@ -76,6 +83,7 @@ class SubuserUpdateService
*/
public function __construct(
ConnectionInterface $connection,
DaemonKeyProviderService $keyProviderService,
DaemonServerRepositoryInterface $daemonRepository,
PermissionCreationService $permissionService,
PermissionRepositoryInterface $permissionRepository,
@ -84,6 +92,7 @@ class SubuserUpdateService
) {
$this->connection = $connection;
$this->daemonRepository = $daemonRepository;
$this->keyProviderService = $keyProviderService;
$this->permissionRepository = $permissionRepository;
$this->permissionService = $permissionService;
$this->repository = $repository;
@ -106,12 +115,11 @@ class SubuserUpdateService
$this->connection->beginTransaction();
$this->permissionRepository->deleteWhere([['subuser_id', '=', $subuser->id]]);
$daemonPermissions = $this->permissionService->handle($subuser->id, $permissions);
$this->permissionService->handle($subuser->id, $permissions);
try {
$this->daemonRepository->setNode($subuser->server->node_id)->setAccessServer($subuser->server->uuid)
->setSubuserKey($subuser->daemonSecret, $daemonPermissions);
$this->connection->commit();
$token = $this->keyProviderService->handle($subuser->server_id, $subuser->user_id, false);
$this->daemonRepository->setNode($subuser->server->node_id)->revokeAccessKey($token);
} catch (RequestException $exception) {
$this->connection->rollBack();
$this->writer->warning($exception);
@ -121,5 +129,7 @@ class SubuserUpdateService
'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(),
]));
}
$this->connection->commit();
}
}