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();
}
}

View File

@ -5,6 +5,7 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
class RemoveDaemonSecretFromServersTable extends Migration
{
@ -20,10 +21,10 @@ class RemoveDaemonSecretFromServersTable extends Migration
$inserts[] = [
'user_id' => $server->owner_id,
'server_id' => $server->id,
'secret' => 'i_' . str_random(40),
'expires_at' => Carbon::now()->addHours(24),
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
'secret' => DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40),
'expires_at' => Carbon::now()->addMinutes(config('pterodactyl.api.key_expire_time', 720))->toDateTimeString(),
'created_at' => Carbon::now()->toDateTimeString(),
'updated_at' => Carbon::now()->toDateTimeString(),
];
});

View File

@ -4,6 +4,7 @@ use Carbon\Carbon;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
class RemoveDaemonSecretFromSubusersTable extends Migration
{
@ -18,10 +19,10 @@ class RemoveDaemonSecretFromSubusersTable extends Migration
$inserts[] = [
'user_id' => $subuser->user_id,
'server_id' => $subuser->server_id,
'secret' => 'i_' . str_random(40),
'expires_at' => Carbon::now()->addHours(24),
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
'secret' => DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40),
'expires_at' => Carbon::now()->addMinutes(config('pterodactyl.api.key_expire_time', 720))->toDateTimeString(),
'created_at' => Carbon::now()->toDateTimeString(),
'updated_at' => Carbon::now()->toDateTimeString(),
];
});
@ -46,7 +47,7 @@ class RemoveDaemonSecretFromSubusersTable extends Migration
$subusers = DB::table('subusers')->get();
$subusers->each(function ($subuser) {
DB::table('daemon_keys')->delete($subuser->id);
DB::table('daemon_keys')->where('user_id', $subuser->user_id)->where('server_id', $subuser->server_id)->delete();
});
}
}

View File

@ -98,9 +98,9 @@ return [
'using_redis' => 'You\'ve selected the Redis driver for one or more options, please provide valid connection information below. In most cases you can use the defaults provided unless you have modified your setup.',
'redis_host' => 'Redis Host',
'redis_password' => 'Redis Password',
'redis_pass_help' => 'By default a Redis server instance has no password as it is running locally and inaccessable to the outside world. If this is the case, simply hit enter without entering a value.',
'redis_port' => 'Redis Port',
'redis_pass_defined' => 'It seems a password is already defined for Redis, would you like to change it?',
'redis_pass_help' => 'By default a Redis server instance has no password as it is running locally and inaccessable to the outside world. If this is the case, simply hit enter without entering a value.',
],
],
];

View File

@ -72,7 +72,7 @@
<td class="text-center">
@if($server->user->id === Auth::user()->id)
<span class="label bg-purple">@lang('strings.owner')</span>
@elseif(Auth::user()->isRootAdmin())
@elseif(Auth::user()->root_admin)
<span class="label bg-maroon">@lang('strings.admin')</span>
@else
<span class="label bg-blue">@lang('strings.subuser')</span>

View File

@ -192,7 +192,7 @@
{!! Theme::js('js/admin/functions.js') !!}
{!! Theme::js('js/autocomplete.js') !!}
@if(Auth::user()->isRootAdmin())
@if(Auth::user()->root_admin)
<script>
$('#logoutButton').on('click', function (event) {
event.preventDefault();

View File

@ -285,7 +285,7 @@
{!! Theme::js('vendor/phraseapp/phraseapp.js') !!}
@endif
@if(Auth::user()->isRootAdmin())
@if(Auth::user()->root_admin)
<script>
$('#logoutButton').on('click', function (event) {
event.preventDefault();

View File

@ -21,6 +21,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use Pterodactyl\Http\Middleware\Server\SubuserAccess;
use Pterodactyl\Http\Middleware\Server\ScheduleAccess;
Route::get('/', 'ConsoleController@index')->name('server.index');