First round of changes to API to support simpler permissions.

This commit is contained in:
Dane Everitt 2018-01-11 22:49:46 -06:00
parent 0e24c669c4
commit a31e5875dc
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
21 changed files with 403 additions and 169 deletions

View file

@ -15,6 +15,8 @@ use Pterodactyl\Http\Requests\Admin\UserFormRequest;
use Pterodactyl\Transformers\Api\Admin\UserTransformer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Http\Requests\API\Admin\Users\GetUserRequest;
use Pterodactyl\Http\Requests\API\Admin\Users\GetUsersRequest;
class UserController extends Controller
{
@ -67,19 +69,19 @@ class UserController extends Controller
}
/**
* Handle request to list all users on the panel. Returns a JSONAPI representation
* Handle request to list all users on the panel. Returns a JSON-API representation
* of a collection of users including any defined relations passed in
* the request.
*
* @param \Illuminate\Http\Request $request
* @param \Pterodactyl\Http\Requests\API\Admin\Users\GetUsersRequest $request
* @return array
*/
public function index(Request $request): array
public function index(GetUsersRequest $request): array
{
$users = $this->repository->paginated(100);
return $this->fractal->collection($users)
->transformWith(new UserTransformer($request))
->transformWith((new UserTransformer)->setKey($request->key()))
->withResourceName('user')
->paginateWith(new IlluminatePaginatorAdapter($users))
->toArray();
@ -89,14 +91,14 @@ class UserController extends Controller
* Handle a request to view a single user. Includes any relations that
* were defined in the request.
*
* @param \Illuminate\Http\Request $request
* @param \Pterodactyl\Models\User $user
* @param \Pterodactyl\Http\Requests\API\Admin\Users\GetUserRequest $request
* @param \Pterodactyl\Models\User $user
* @return array
*/
public function view(Request $request, User $user): array
public function view(GetUserRequest $request, User $user): array
{
return $this->fractal->item($user)
->transformWith(new UserTransformer($request))
->transformWith((new UserTransformer)->setKey($request->key()))
->withResourceName('user')
->toArray();
}

View file

@ -25,7 +25,6 @@ use Pterodactyl\Http\Middleware\API\AuthenticateIPAccess;
use Pterodactyl\Http\Middleware\Daemon\DaemonAuthenticate;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Pterodactyl\Http\Middleware\API\HasPermissionToResource;
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer;
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
@ -98,9 +97,6 @@ class Kernel extends HttpKernel
'bindings' => SubstituteBindings::class,
'recaptcha' => VerifyReCaptcha::class,
// API specific middleware.
'api..user_level' => HasPermissionToResource::class,
// Server specific middleware (used for authenticating access to resources)
//
// These are only used for individual server authentication, and not gloabl

View file

@ -1,58 +0,0 @@
<?php
namespace Pterodactyl\Http\Middleware\API;
use Closure;
use Illuminate\Http\Request;
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class HasPermissionToResource
{
/**
* @var \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface
*/
private $repository;
/**
* HasPermissionToResource constructor.
*
* @param \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface $repository
*/
public function __construct(ApiKeyRepositoryInterface $repository)
{
$this->repository = $repository;
}
/**
* Determine if an API key has permission to access the given route.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $role
* @return mixed
*/
public function handle(Request $request, Closure $next, string $role = 'admin')
{
/** @var \Pterodactyl\Models\APIKey $model */
$model = $request->attributes->get('api_key');
if ($role === 'admin' && ! $request->user()->root_admin) {
throw new NotFoundHttpException;
}
$this->repository->loadPermissions($model);
$routeKey = str_replace(['api.', 'admin.'], '', $request->route()->getName());
$count = $model->getRelation('permissions')->filter(function ($permission) use ($routeKey) {
return $routeKey === str_replace('-', '.', $permission->permission);
})->count();
if ($count === 1) {
return $next($request);
}
throw new AccessDeniedHttpException('Cannot access resource without required `' . $routeKey . '` permission.');
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Pterodactyl\Http\Requests\API\Admin;
use Pterodactyl\Models\APIKey;
use Illuminate\Foundation\Http\FormRequest;
use Pterodactyl\Exceptions\PterodactylException;
use Pterodactyl\Services\Acl\Api\AdminAcl as Acl;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
abstract class ApiAdminRequest extends FormRequest
{
/**
* The resource that should be checked when performing the authorization
* function for this request.
*
* @var string|null
*/
protected $resource;
/**
* The permission level that a given API key should have for accessing
* the defined $resource during the request cycle.
*
* @var int
*/
protected $permission = Acl::NONE;
/**
* Determine if the current user is authorized to perform
* the requested action aganist the API.
*
* @return bool
*
* @throws \Pterodactyl\Exceptions\PterodactylException
*/
public function authorize(): bool
{
if (is_null($this->resource)) {
throw new PterodactylException('An ACL resource must be defined on API requests.');
}
return Acl::check($this->key(), $this->resource, $this->permission);
}
/**
* Determine if the requested resource exists on the server.
*
* @return bool
*/
public function resourceExists(): bool
{
return true;
}
/**
* Default set of rules to apply to API requests.
*
* @return array
*/
public function rules(): array
{
return [];
}
/**
* Return the API key being used for the request.
*
* @return \Pterodactyl\Models\APIKey
*/
public function key(): APIKey
{
return $this->attributes->get('api_key');
}
/**
* Determine if the request passes the authorization check as well
* as the exists check.
*
* @return bool
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function passesAuthorization()
{
$passes = parent::passesAuthorization();
// Only let the user know that a resource does not exist if they are
// authenticated to access the endpoint. This avoids exposing that
// an item exists (or does not exist) to the user until they can prove
// that they have permission to know about it.
if ($passes && ! $this->resourceExists()) {
throw new NotFoundHttpException('The requested resource does not exist on this server.');
}
return $passes;
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace Pterodactyl\Http\Requests\API\Admin\Users;
use Pterodactyl\Models\User;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\API\Admin\ApiAdminRequest;
class GetUserRequest extends ApiAdminRequest
{
/**
* @var string
*/
protected $resource = AdminAcl::RESOURCE_USERS;
/**
* @var int
*/
protected $permission = AdminAcl::READ;
/**
* Determine if the requested user exists on the Panel.
*
* @return bool
*/
public function resourceExists(): bool
{
$user = $this->route()->parameter('user');
return $user instanceof User && $user->exists;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Pterodactyl\Http\Requests\API\Admin\Users;
use Pterodactyl\Services\Acl\Api\AdminAcl as Acl;
use Pterodactyl\Http\Requests\API\Admin\ApiAdminRequest;
class GetUsersRequest extends ApiAdminRequest
{
/**
* @var string
*/
protected $resource = Acl::RESOURCE_USERS;
/**
* @var int
*/
protected $permission = Acl::READ;
}

View file

@ -2,9 +2,9 @@
namespace Pterodactyl\Http\Requests\API\Remote;
use Pterodactyl\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class SftpAuthenticationFormRequest extends Request
class SftpAuthenticationFormRequest extends FormRequest
{
/**
* Authenticate the request.

View file

@ -1,9 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
abstract class Request extends FormRequest
{
}