Final touches to new key-rotation service

This commit is contained in:
Dane Everitt 2017-09-24 12:34:00 -05:00
parent 0f0c319ec0
commit 8e2b77dc1e
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
6 changed files with 154 additions and 18 deletions

View file

@ -30,6 +30,7 @@ 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
@ -77,7 +78,7 @@ class ValidateKeyController extends Controller
*/
public function index($token)
{
if (! starts_with($token, 'i_')) {
if (! starts_with($token, DaemonKeyUpdateService::INTERNAL_TOKEN_IDENTIFIER)) {
throw new HttpException(501);
}

View file

@ -26,7 +26,9 @@
namespace Pterodactyl\Http\Controllers\Base;
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\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface;
@ -36,7 +38,7 @@ class IndexController extends Controller
/**
* @var \Pterodactyl\Services\Servers\ServerAccessHelperService
*/
protected $access;
protected $serverAccessHelper;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
@ -52,15 +54,15 @@ class IndexController extends Controller
* IndexController constructor.
*
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
* @param \Pterodactyl\Services\Servers\ServerAccessHelperService $access
* @param \Pterodactyl\Services\Servers\ServerAccessHelperService $serverAccessHelper
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
*/
public function __construct(
DaemonServerRepositoryInterface $daemonRepository,
ServerAccessHelperService $access,
ServerAccessHelperService $serverAccessHelper,
ServerRepositoryInterface $repository
) {
$this->access = $access;
$this->serverAccessHelper = $serverAccessHelper;
$this->daemonRepository = $daemonRepository;
$this->repository = $repository;
}
@ -90,7 +92,8 @@ class IndexController extends Controller
*/
public function status(Request $request, $uuid)
{
$server = $this->access->handle($uuid, $request->user());
$server = $this->repository->findFirstWhere([['uuidShort', '=', $uuid]]);
$token = $this->serverAccessHelper->handle($server, $request->user());
if (! $server->installed) {
return response()->json(['status' => 20]);
@ -98,10 +101,14 @@ class IndexController extends Controller
return response()->json(['status' => 30]);
}
$response = $this->daemonRepository->setNode($server->node_id)
->setAccessServer($server->uuid)
->setAccessToken($server->daemonSecret)
->details();
try {
$response = $this->daemonRepository->setNode($server->node_id)
->setAccessServer($server->uuid)
->setAccessToken($token)
->details();
} catch (RequestException $exception) {
throw new HttpException(500, $exception->getMessage());
}
return response()->json(json_decode($response->getBody()));
}

View file

@ -0,0 +1,93 @@
<?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 Pterodactyl\Models\DaemonKey;
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
*/
protected $config;
/**
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
*/
protected $repository;
/**
* DaemonKeyUpdateService 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;
}
/**
* Update a daemon key to expire the previous one.
*
* @param \Pterodactyl\Models\DaemonKey|int $key
* @return string
*
* @throws \RuntimeException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle($key)
{
if ($key instanceof DaemonKey) {
$key = $key->id;
}
$secret = self::INTERNAL_TOKEN_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')),
]);
return $secret;
}
}

View file

@ -24,9 +24,12 @@
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;
@ -39,11 +42,21 @@ class ServerAccessHelperService
*/
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
*/
@ -58,18 +71,24 @@ class ServerAccessHelperService
* 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;
}
@ -81,8 +100,10 @@ class ServerAccessHelperService
* @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)
{
@ -95,16 +116,16 @@ class ServerAccessHelperService
}
$keys = $server->relationLoaded('keys') ? $server->keys : $this->daemonKeyRepository->getServerKeys($server->id);
$key = array_get($keys->where('user_id', $user->id)->first(null, []), 'secret');
if ($user->root_admin) {
$key = array_get($keys->where('user_id', $server->owner_id)->first(null, []), 'secret');
}
$key = $keys->where('user_id', $user->root_admin ? $server->owner_id : $user->id)->first();
if (is_null($key)) {
throw new UserNotLinkedToServerException;
}
return $key;
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,6 +24,7 @@
namespace Pterodactyl\Transformers\Daemon;
use Carbon\Carbon;
use Pterodactyl\Models\DaemonKey;
use Pterodactyl\Models\Permission;
use League\Fractal\TransformerAbstract;
@ -31,6 +32,11 @@ use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
class ApiKeyTransformer extends TransformerAbstract
{
/**
* @var \Carbon\Carbon
*/
protected $carbon;
/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
*/
@ -39,10 +45,12 @@ class ApiKeyTransformer extends TransformerAbstract
/**
* ApiKeyTransformer constructor.
*
* @param \Carbon\Carbon $carbon
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository
*/
public function __construct(SubuserRepositoryInterface $repository)
public function __construct(Carbon $carbon, SubuserRepositoryInterface $repository)
{
$this->carbon = $carbon;
$this->repository = $repository;
}
@ -59,6 +67,8 @@ class ApiKeyTransformer extends TransformerAbstract
if ($key->user_id === $key->server->owner_id) {
return [
'id' => $key->server->uuid,
'is_temporary' => true,
'expires_in' => max($this->carbon->now()->diffInSeconds($key->expires_at, false), 0),
'permissions' => ['s:*'],
];
}
@ -76,7 +86,10 @@ class ApiKeyTransformer extends TransformerAbstract
}
return [
$key->server->uuid => $daemonPermissions,
'id' => $key->server->uuid,
'is_temporary' => true,
'expires_in' => max($this->carbon->now()->diffInSeconds($key->expires_at, false), 0),
'permissions' => $daemonPermissions,
];
}
}

View file

@ -59,6 +59,7 @@ return [
*/
'api' => [
'include_on_list' => env('API_INCLUDE_ON_LIST', false),
'key_expire_time' => env('API_KEY_EXPIRE_TIME', 60 * 12),
],
/*