First round of changes to API to support simpler permissions.
This commit is contained in:
parent
0e24c669c4
commit
a31e5875dc
21 changed files with 403 additions and 169 deletions
|
@ -18,6 +18,7 @@ interface ApiKeyRepositoryInterface extends RepositoryInterface
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Models\APIKey $model
|
* @param \Pterodactyl\Models\APIKey $model
|
||||||
* @param bool $refresh
|
* @param bool $refresh
|
||||||
|
* @deprecated
|
||||||
* @return \Pterodactyl\Models\APIKey
|
* @return \Pterodactyl\Models\APIKey
|
||||||
*/
|
*/
|
||||||
public function loadPermissions(APIKey $model, bool $refresh = false): APIKey;
|
public function loadPermissions(APIKey $model, bool $refresh = false): APIKey;
|
||||||
|
|
|
@ -15,6 +15,8 @@ use Pterodactyl\Http\Requests\Admin\UserFormRequest;
|
||||||
use Pterodactyl\Transformers\Api\Admin\UserTransformer;
|
use Pterodactyl\Transformers\Api\Admin\UserTransformer;
|
||||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
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
|
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
|
* of a collection of users including any defined relations passed in
|
||||||
* the request.
|
* the request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Pterodactyl\Http\Requests\API\Admin\Users\GetUsersRequest $request
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): array
|
public function index(GetUsersRequest $request): array
|
||||||
{
|
{
|
||||||
$users = $this->repository->paginated(100);
|
$users = $this->repository->paginated(100);
|
||||||
|
|
||||||
return $this->fractal->collection($users)
|
return $this->fractal->collection($users)
|
||||||
->transformWith(new UserTransformer($request))
|
->transformWith((new UserTransformer)->setKey($request->key()))
|
||||||
->withResourceName('user')
|
->withResourceName('user')
|
||||||
->paginateWith(new IlluminatePaginatorAdapter($users))
|
->paginateWith(new IlluminatePaginatorAdapter($users))
|
||||||
->toArray();
|
->toArray();
|
||||||
|
@ -89,14 +91,14 @@ class UserController extends Controller
|
||||||
* Handle a request to view a single user. Includes any relations that
|
* Handle a request to view a single user. Includes any relations that
|
||||||
* were defined in the request.
|
* were defined in the request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Pterodactyl\Http\Requests\API\Admin\Users\GetUserRequest $request
|
||||||
* @param \Pterodactyl\Models\User $user
|
* @param \Pterodactyl\Models\User $user
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function view(Request $request, User $user): array
|
public function view(GetUserRequest $request, User $user): array
|
||||||
{
|
{
|
||||||
return $this->fractal->item($user)
|
return $this->fractal->item($user)
|
||||||
->transformWith(new UserTransformer($request))
|
->transformWith((new UserTransformer)->setKey($request->key()))
|
||||||
->withResourceName('user')
|
->withResourceName('user')
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ use Pterodactyl\Http\Middleware\API\AuthenticateIPAccess;
|
||||||
use Pterodactyl\Http\Middleware\Daemon\DaemonAuthenticate;
|
use Pterodactyl\Http\Middleware\Daemon\DaemonAuthenticate;
|
||||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||||
use Pterodactyl\Http\Middleware\API\HasPermissionToResource;
|
|
||||||
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
|
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
|
||||||
use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer;
|
use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer;
|
||||||
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
|
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
|
||||||
|
@ -98,9 +97,6 @@ class Kernel extends HttpKernel
|
||||||
'bindings' => SubstituteBindings::class,
|
'bindings' => SubstituteBindings::class,
|
||||||
'recaptcha' => VerifyReCaptcha::class,
|
'recaptcha' => VerifyReCaptcha::class,
|
||||||
|
|
||||||
// API specific middleware.
|
|
||||||
'api..user_level' => HasPermissionToResource::class,
|
|
||||||
|
|
||||||
// Server specific middleware (used for authenticating access to resources)
|
// Server specific middleware (used for authenticating access to resources)
|
||||||
//
|
//
|
||||||
// These are only used for individual server authentication, and not gloabl
|
// These are only used for individual server authentication, and not gloabl
|
||||||
|
|
|
@ -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.');
|
|
||||||
}
|
|
||||||
}
|
|
98
app/Http/Requests/API/Admin/ApiAdminRequest.php
Normal file
98
app/Http/Requests/API/Admin/ApiAdminRequest.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
32
app/Http/Requests/API/Admin/Users/GetUserRequest.php
Normal file
32
app/Http/Requests/API/Admin/Users/GetUserRequest.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
19
app/Http/Requests/API/Admin/Users/GetUsersRequest.php
Normal file
19
app/Http/Requests/API/Admin/Users/GetUsersRequest.php
Normal 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;
|
||||||
|
}
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Requests\API\Remote;
|
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.
|
* Authenticate the request.
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
abstract class Request extends FormRequest
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,17 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
|
||||||
* Pterodactyl - Panel
|
|
||||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
|
||||||
*
|
|
||||||
* This software is licensed under the terms of the MIT license.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Pterodactyl\Models;
|
namespace Pterodactyl\Models;
|
||||||
|
|
||||||
use Sofa\Eloquence\Eloquence;
|
use Sofa\Eloquence\Eloquence;
|
||||||
use Sofa\Eloquence\Validable;
|
use Sofa\Eloquence\Validable;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Pterodactyl\Services\Acl\Api\AdminAcl;
|
||||||
use Sofa\Eloquence\Contracts\CleansAttributes;
|
use Sofa\Eloquence\Contracts\CleansAttributes;
|
||||||
use Sofa\Eloquence\Contracts\Validable as ValidableContract;
|
use Sofa\Eloquence\Contracts\Validable as ValidableContract;
|
||||||
|
|
||||||
|
@ -35,14 +29,29 @@ class APIKey extends Model implements CleansAttributes, ValidableContract
|
||||||
*/
|
*/
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'allowed_ips' => 'json',
|
'allowed_ips' => 'json',
|
||||||
|
'user_id' => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_USERS => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_DATABASES => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_EGGS => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_LOCATIONS => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_NESTS => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_NODES => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_PACKS => 'int',
|
||||||
|
'r_' . AdminAcl::RESOURCE_SERVERS => 'int',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fields that are not mass assignable.
|
* Fields that are mass assignable.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $guarded = ['id', 'created_at', 'updated_at'];
|
protected $fillable = [
|
||||||
|
'token',
|
||||||
|
'allowed_ips',
|
||||||
|
'memo',
|
||||||
|
'expires_at',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rules defining what fields must be passed when making a model.
|
* Rules defining what fields must be passed when making a model.
|
||||||
|
@ -66,6 +75,24 @@ class APIKey extends Model implements CleansAttributes, ValidableContract
|
||||||
'memo' => 'nullable|string|max:500',
|
'memo' => 'nullable|string|max:500',
|
||||||
'allowed_ips' => 'nullable|json',
|
'allowed_ips' => 'nullable|json',
|
||||||
'expires_at' => 'nullable|datetime',
|
'expires_at' => 'nullable|datetime',
|
||||||
|
'r_' . AdminAcl::RESOURCE_USERS => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_ALLOCATIONS => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_DATABASES => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_EGGS => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_LOCATIONS => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_NESTS => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_NODES => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_PACKS => 'integer|min:0|max:3',
|
||||||
|
'r_' . AdminAcl::RESOURCE_SERVERS => 'integer|min:0|max:3',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = [
|
||||||
|
self::CREATED_AT,
|
||||||
|
self::UPDATED_AT,
|
||||||
|
'expires_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,10 +29,6 @@ class RouteServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function map()
|
public function map()
|
||||||
{
|
{
|
||||||
// Route::middleware(['api'])->prefix('/api/user')
|
|
||||||
// ->namespace($this->namespace . '\API\User')
|
|
||||||
// ->group(base_path('routes/api.php'));
|
|
||||||
|
|
||||||
Route::middleware(['web', 'auth', 'csrf'])
|
Route::middleware(['web', 'auth', 'csrf'])
|
||||||
->namespace($this->namespace . '\Base')
|
->namespace($this->namespace . '\Base')
|
||||||
->group(base_path('routes/base.php'));
|
->group(base_path('routes/base.php'));
|
||||||
|
@ -49,7 +45,7 @@ class RouteServiceProvider extends ServiceProvider
|
||||||
->namespace($this->namespace . '\Server')
|
->namespace($this->namespace . '\Server')
|
||||||
->group(base_path('routes/server.php'));
|
->group(base_path('routes/server.php'));
|
||||||
|
|
||||||
Route::middleware(['api', 'api..user_level:admin'])->prefix('/api/admin')
|
Route::middleware(['api'])->prefix('/api/admin')
|
||||||
->namespace($this->namespace . '\API\Admin')
|
->namespace($this->namespace . '\API\Admin')
|
||||||
->group(base_path('routes/api-admin.php'));
|
->group(base_path('routes/api-admin.php'));
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInt
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Models\APIKey $model
|
* @param \Pterodactyl\Models\APIKey $model
|
||||||
* @param bool $refresh
|
* @param bool $refresh
|
||||||
|
* @deprecated
|
||||||
* @return \Pterodactyl\Models\APIKey
|
* @return \Pterodactyl\Models\APIKey
|
||||||
*/
|
*/
|
||||||
public function loadPermissions(APIKey $model, bool $refresh = false): APIKey
|
public function loadPermissions(APIKey $model, bool $refresh = false): APIKey
|
||||||
|
|
66
app/Services/Acl/Api/AdminAcl.php
Normal file
66
app/Services/Acl/Api/AdminAcl.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Services\Acl\Api;
|
||||||
|
|
||||||
|
use Pterodactyl\Models\APIKey;
|
||||||
|
|
||||||
|
class AdminAcl
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Resource permission columns in the api_keys table begin
|
||||||
|
* with this identifer.
|
||||||
|
*/
|
||||||
|
const COLUMN_IDENTIFER = 'r_';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different types of permissions available for API keys. This
|
||||||
|
* implements a read/write/none permissions scheme for all endpoints.
|
||||||
|
*/
|
||||||
|
const NONE = 0;
|
||||||
|
const READ = 1;
|
||||||
|
const WRITE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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}.
|
||||||
|
*/
|
||||||
|
const RESOURCE_SERVERS = 'servers';
|
||||||
|
const RESOURCE_NODES = 'nodes';
|
||||||
|
const RESOURCE_ALLOCATIONS = 'allocations';
|
||||||
|
const RESOURCE_USERS = 'users';
|
||||||
|
const RESOURCE_LOCATIONS = 'locations';
|
||||||
|
const RESOURCE_NESTS = 'nests';
|
||||||
|
const RESOURCE_EGGS = 'eggs';
|
||||||
|
const RESOURCE_DATABASES = 'databases';
|
||||||
|
const RESOURCE_PACKS = 'packs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an API key has permission to perform a specific read/write operation.
|
||||||
|
*
|
||||||
|
* @param int $permission
|
||||||
|
* @param int $action
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function can(int $permission, int $action = self::READ)
|
||||||
|
{
|
||||||
|
if ($permission & $action) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an API Key model has permission to access a given resource
|
||||||
|
* at a specific action level.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Models\APIKey $key
|
||||||
|
* @param string $resource
|
||||||
|
* @param int $action
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function check(APIKey $key, string $resource, int $action = self::READ)
|
||||||
|
{
|
||||||
|
return self::can(data_get($key, self::COLUMN_IDENTIFER . $resource, self::NONE), $action);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,9 @@
|
||||||
namespace Pterodactyl\Transformers\Api\Admin;
|
namespace Pterodactyl\Transformers\Api\Admin;
|
||||||
|
|
||||||
use Pterodactyl\Models\Allocation;
|
use Pterodactyl\Models\Allocation;
|
||||||
use Pterodactyl\Transformers\Api\ApiTransformer;
|
use Pterodactyl\Transformers\Api\BaseTransformer;
|
||||||
|
|
||||||
class AllocationTransformer extends ApiTransformer
|
class AllocationTransformer extends BaseTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Relationships that can be loaded onto allocation transformations.
|
* Relationships that can be loaded onto allocation transformations.
|
||||||
|
|
69
app/Transformers/Api/Admin/BaseTransformer.php
Normal file
69
app/Transformers/Api/Admin/BaseTransformer.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Transformers\Api\Admin;
|
||||||
|
|
||||||
|
use Pterodactyl\Models\APIKey;
|
||||||
|
use Illuminate\Container\Container;
|
||||||
|
use League\Fractal\TransformerAbstract;
|
||||||
|
use Pterodactyl\Services\Acl\Api\AdminAcl;
|
||||||
|
|
||||||
|
abstract class BaseTransformer extends TransformerAbstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Models\APIKey
|
||||||
|
*/
|
||||||
|
private $key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HTTP request class being used for this request.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Models\APIKey $key
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setKey(APIKey $key)
|
||||||
|
{
|
||||||
|
$this->key = $key;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the request instance being used for this transformer.
|
||||||
|
*
|
||||||
|
* @return \Pterodactyl\Models\APIKey
|
||||||
|
*/
|
||||||
|
public function getKey(): APIKey
|
||||||
|
{
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the API key loaded onto the transformer has permission
|
||||||
|
* to access a different resource. This is used when including other
|
||||||
|
* models on a transformation request.
|
||||||
|
*
|
||||||
|
* @param string $resource
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function authorize(string $resource): bool
|
||||||
|
{
|
||||||
|
return AdminAcl::check($this->getKey(), $resource, AdminAcl::READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of the transformer and pass along the currently
|
||||||
|
* set API key.
|
||||||
|
*
|
||||||
|
* @param string $abstract
|
||||||
|
* @param array $parameters
|
||||||
|
* @return \Pterodactyl\Transformers\Api\Admin\BaseTransformer
|
||||||
|
*/
|
||||||
|
protected function makeTransformer(string $abstract, array $parameters = []): self
|
||||||
|
{
|
||||||
|
/** @var \Pterodactyl\Transformers\Api\Admin\BaseTransformer $transformer */
|
||||||
|
$transformer = Container::getInstance()->makeWith($abstract, $parameters);
|
||||||
|
$transformer->setKey($this->getKey());
|
||||||
|
|
||||||
|
return $transformer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,9 @@
|
||||||
namespace Pterodactyl\Transformers\Api\Admin;
|
namespace Pterodactyl\Transformers\Api\Admin;
|
||||||
|
|
||||||
use Pterodactyl\Models\Location;
|
use Pterodactyl\Models\Location;
|
||||||
use Pterodactyl\Transformers\Api\ApiTransformer;
|
use Pterodactyl\Transformers\Api\BaseTransformer;
|
||||||
|
|
||||||
class LocationTransformer extends ApiTransformer
|
class LocationTransformer extends BaseTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* List of resources that can be included.
|
* List of resources that can be included.
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
namespace Pterodactyl\Transformers\Api\Admin;
|
namespace Pterodactyl\Transformers\Api\Admin;
|
||||||
|
|
||||||
use Pterodactyl\Models\Node;
|
use Pterodactyl\Models\Node;
|
||||||
use Pterodactyl\Transformers\Api\ApiTransformer;
|
use Pterodactyl\Transformers\Api\BaseTransformer;
|
||||||
|
|
||||||
class NodeTransformer extends ApiTransformer
|
class NodeTransformer extends BaseTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* List of resources that can be included.
|
* List of resources that can be included.
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
namespace Pterodactyl\Transformers\Api\Admin;
|
namespace Pterodactyl\Transformers\Api\Admin;
|
||||||
|
|
||||||
use Pterodactyl\Models\User;
|
use Pterodactyl\Models\User;
|
||||||
use Pterodactyl\Transformers\Api\ApiTransformer;
|
use Pterodactyl\Services\Acl\Api\AdminAcl;
|
||||||
|
|
||||||
class UserTransformer extends ApiTransformer
|
class UserTransformer extends BaseTransformer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* List of resources that can be included.
|
* List of resources that can be included.
|
||||||
|
@ -29,18 +29,16 @@ class UserTransformer extends ApiTransformer
|
||||||
* Return the servers associated with this user.
|
* Return the servers associated with this user.
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Models\User $user
|
* @param \Pterodactyl\Models\User $user
|
||||||
* @return bool|\League\Fractal\Resource\Collection
|
* @return \League\Fractal\Resource\Collection|\League\Fractal\Resource\NullResource
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\PterodactylException
|
|
||||||
*/
|
*/
|
||||||
public function includeServers(User $user)
|
public function includeServers(User $user)
|
||||||
{
|
{
|
||||||
if (! $this->authorize('server-list')) {
|
if (! $this->authorize(AdminAcl::RESOURCE_SERVERS)) {
|
||||||
return false;
|
return $this->null();
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->loadMissing('servers');
|
$user->loadMissing('servers');
|
||||||
|
|
||||||
return $this->collection($user->getRelation('servers'), new ServerTransformer($this->getRequest()), 'server');
|
return $this->collection($user->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Transformers\Api;
|
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use League\Fractal\TransformerAbstract;
|
|
||||||
use Pterodactyl\Exceptions\PterodactylException;
|
|
||||||
|
|
||||||
abstract class ApiTransformer extends TransformerAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Illuminate\Http\Request
|
|
||||||
*/
|
|
||||||
private $request;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup request object for transformer.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
*/
|
|
||||||
public function __construct(Request $request)
|
|
||||||
{
|
|
||||||
$this->request = $request;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the request instance being used for this transformer.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Request
|
|
||||||
*/
|
|
||||||
public function getRequest(): Request
|
|
||||||
{
|
|
||||||
return $this->request;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an API key from the request has permission to access
|
|
||||||
* a resource. This is used when loading includes on the transformed
|
|
||||||
* models.
|
|
||||||
*
|
|
||||||
* @param string $permission
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
* @throws \Pterodactyl\Exceptions\PterodactylException
|
|
||||||
*/
|
|
||||||
protected function authorize(string $permission): bool
|
|
||||||
{
|
|
||||||
/** @var \Pterodactyl\Models\APIKey $model */
|
|
||||||
$model = $this->request->attributes->get('api_key');
|
|
||||||
if (! $model->relationLoaded('permissions')) {
|
|
||||||
throw new PterodactylException('Permissions must be loaded onto a model before passing to transformer authorize function.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$count = $model->getRelation('permissions')->filter(function ($model) use ($permission) {
|
|
||||||
return $model->permission === $permission;
|
|
||||||
})->count();
|
|
||||||
|
|
||||||
return $count > 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class AddApiKeyPermissionColumns extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('api_keys', function (Blueprint $table) {
|
||||||
|
$table->unsignedTinyInteger('r_servers')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_nodes')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_allocations')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_users')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_locations')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_nests')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_eggs')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_databases')->default(0);
|
||||||
|
$table->unsignedTinyInteger('r_packs')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('api_keys', function (Blueprint $table) {
|
||||||
|
$table->dropColumn([
|
||||||
|
'r_servers',
|
||||||
|
'r_nodes',
|
||||||
|
'r_allocations',
|
||||||
|
'r_users',
|
||||||
|
'r_locations',
|
||||||
|
'r_nests',
|
||||||
|
'r_eggs',
|
||||||
|
'r_databases',
|
||||||
|
'r_packs',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Pterodactyl\Models\User;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| User Controller Routes
|
| User Controller Routes
|
||||||
|
@ -9,6 +11,10 @@
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
Route::group(['prefix' => '/users'], function () {
|
Route::group(['prefix' => '/users'], function () {
|
||||||
|
Route::bind('user', function ($value) {
|
||||||
|
return User::find($value) ?? new User;
|
||||||
|
});
|
||||||
|
|
||||||
Route::get('/', 'Users\UserController@index')->name('api.admin.user.list');
|
Route::get('/', 'Users\UserController@index')->name('api.admin.user.list');
|
||||||
Route::get('/{user}', 'Users\UserController@view')->name('api.admin.user.view');
|
Route::get('/{user}', 'Users\UserController@view')->name('api.admin.user.view');
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue