Implement application API Keys

This commit is contained in:
Dane Everitt 2018-01-18 21:36:15 -06:00
parent f9fc3f4370
commit c3b9738364
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
13 changed files with 454 additions and 3 deletions

View file

@ -15,6 +15,14 @@ interface ApiKeyRepositoryInterface extends RepositoryInterface
*/
public function getAccountKeys(User $user): Collection;
/**
* Get all of the application API keys that exist for a specific user.
*
* @param \Pterodactyl\Models\User $user
* @return \Illuminate\Support\Collection
*/
public function getApplicationKeys(User $user): Collection;
/**
* Delete an account API key from the panel for a specific user.
*
@ -23,4 +31,13 @@ interface ApiKeyRepositoryInterface extends RepositoryInterface
* @return int
*/
public function deleteAccountKey(User $user, string $identifier): int;
/**
* Delete an application API key from the panel for a specific user.
*
* @param \Pterodactyl\Models\User $user
* @param string $identifier
* @return int
*/
public function deleteApplicationKey(User $user, string $identifier): int;
}

View file

@ -0,0 +1,117 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pterodactyl\Models\ApiKey;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Api\KeyCreationService;
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Api\StoreApplicationApiKeyRequest;
class ApplicationApiController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Pterodactyl\Services\Api\KeyCreationService
*/
private $keyCreationService;
/**
* @var \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface
*/
private $repository;
/**
* ApplicationApiController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface $repository
* @param \Pterodactyl\Services\Api\KeyCreationService $keyCreationService
*/
public function __construct(
AlertsMessageBag $alert,
ApiKeyRepositoryInterface $repository,
KeyCreationService $keyCreationService
) {
$this->alert = $alert;
$this->keyCreationService = $keyCreationService;
$this->repository = $repository;
}
/**
* Render view showing all of a user's application API keys.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request): View
{
return view('admin.api.index', [
'keys' => $this->repository->getApplicationKeys($request->user()),
]);
}
/**
* Render view allowing an admin to create a new application API key.
*
* @return \Illuminate\View\View
*/
public function create(): View
{
$resources = AdminAcl::getResourceList();
sort($resources);
return view('admin.api.new', [
'resources' => $resources,
'permissions' => [
'r' => AdminAcl::READ,
'rw' => AdminAcl::READ | AdminAcl::WRITE,
'n' => AdminAcl::NONE,
],
]);
}
/**
* Store the new key and redirect the user back to the application key listing.
*
* @param \Pterodactyl\Http\Requests\Admin\Api\StoreApplicationApiKeyRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(StoreApplicationApiKeyRequest $request): RedirectResponse
{
$this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([
'memo' => $request->input('memo'),
'user_id' => $request->user()->id,
], $request->getKeyPermissions());
$this->alert->success('A new application API key has been generated for your account.')->flash();
return redirect()->route('admin.api.index');
}
/**
* Delete an application API key from the database.
*
* @param \Illuminate\Http\Request $request
* @param string $identifier
* @return \Illuminate\Http\Response
*/
public function delete(Request $request, string $identifier): Response
{
$this->repository->deleteApplicationKey($request->user(), $identifier);
return response('', 204);
}
}

View file

@ -21,6 +21,7 @@ use Pterodactyl\Http\Middleware\RedirectIfAuthenticated;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Pterodactyl\Http\Middleware\Api\Admin\AuthenticateKey;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Pterodactyl\Http\Middleware\Api\Admin\AuthenticateUser;
use Pterodactyl\Http\Middleware\Api\Admin\SetSessionDriver;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
@ -66,10 +67,11 @@ class Kernel extends HttpKernel
RequireTwoFactorAuthentication::class,
],
'api' => [
'throttle:60,1',
'throttle:120,1',
SubstituteBindings::class,
SetSessionDriver::class,
AuthenticateKey::class,
AuthenticateUser::class,
AuthenticateIPAccess::class,
],
'daemon' => [

View file

@ -0,0 +1,27 @@
<?php
namespace Pterodactyl\Http\Middleware\Api\Admin;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class AuthenticateUser
{
/**
* Authenticate that the currently authenticated user is an administrator
* and should be allowed to proceede through the application API.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (is_null($request->user()) || ! $request->user()->root_admin) {
throw new AccessDeniedHttpException;
}
return $next($request);
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Api;
use Pterodactyl\Models\ApiKey;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class StoreApplicationApiKeyRequest extends AdminFormRequest
{
/**
* @return array
*/
public function rules()
{
$modelRules = ApiKey::getCreateRules();
return collect(AdminAcl::getResourceList())->mapWithKeys(function ($resource) use ($modelRules) {
return [AdminAcl::COLUMN_IDENTIFER . $resource => $modelRules['r_' . $resource]];
})->merge(['memo' => $modelRules['memo']])->toArray();
}
/**
* @return array
*/
public function attributes()
{
return [
'memo' => 'Description',
];
}
public function getKeyPermissions(): array
{
return collect($this->validated())->filter(function ($value, $key) {
return substr($key, 0, strlen(AdminAcl::COLUMN_IDENTIFER)) === AdminAcl::COLUMN_IDENTIFER;
})->toArray();
}
}

View file

@ -32,6 +32,19 @@ class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInt
->get($this->getColumns());
}
/**
* Get all of the application API keys that exist for a specific user.
*
* @param \Pterodactyl\Models\User $user
* @return \Illuminate\Support\Collection
*/
public function getApplicationKeys(User $user): Collection
{
return $this->getBuilder()->where('user_id', $user->id)
->where('key_type', ApiKey::TYPE_APPLICATION)
->get($this->getColumns());
}
/**
* Delete an account API key from the panel for a specific user.
*
@ -46,4 +59,19 @@ class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInt
->where('identifier', $identifier)
->delete();
}
/**
* Delete an application API key from the panel for a specific user.
*
* @param \Pterodactyl\Models\User $user
* @param string $identifier
* @return int
*/
public function deleteApplicationKey(User $user, string $identifier): int
{
return $this->getBuilder()->where('user_id', $user->id)
->where('key_type', ApiKey::TYPE_APPLICATION)
->where('identifier', $identifier)
->delete();
}
}

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Services\Acl\Api;
use ReflectionClass;
use Pterodactyl\Models\ApiKey;
class AdminAcl
@ -22,7 +23,7 @@ class AdminAcl
/**
* Resources that are available on the API and can contain a permissions
* set for each key. These are stored in the database as permission_{resource}.
* set for each key. These are stored in the database as r_{resource}.
*/
const RESOURCE_SERVERS = 'servers';
const RESOURCE_NODES = 'nodes';
@ -63,4 +64,18 @@ class AdminAcl
{
return self::can(data_get($key, self::COLUMN_IDENTIFER . $resource, self::NONE), $action);
}
/**
* Return a list of all resource constants defined in this ACL.
*
* @return array
*/
public static function getResourceList(): array
{
$reflect = new ReflectionClass(__CLASS__);
return collect($reflect->getConstants())->filter(function ($value, $key) {
return substr($key, 0, 9) === 'RESOURCE_';
})->values()->toArray();
}
}