Merge branch 'develop' into matthewpi/security-keys-backport

This commit is contained in:
Matthew Penner 2023-01-17 15:33:53 -07:00
commit f631ac1946
No known key found for this signature in database
1153 changed files with 25099 additions and 37002 deletions

View file

@ -1,35 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
abstract class AdminFormRequest extends FormRequest
{
/**
* The rules to apply to the incoming form request.
*/
abstract public function rules(): array;
/**
* Determine if the user is an admin and has permission to access this
* form controller in the first place.
*/
public function authorize(): bool
{
if (is_null($this->user())) {
return false;
}
return (bool) $this->user()->root_admin;
}
/**
* Return only the fields that we are interested in from the request.
* This will include empty fields as a null value.
*/
public function normalize(array $only = null): array
{
return $this->only($only ?? array_keys($this->rules()));
}
}

View file

@ -1,37 +0,0 @@
<?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
{
/**
* @throws \ReflectionException
* @throws \ReflectionException
*/
public function rules(): array
{
$modelRules = ApiKey::getRules();
return collect(AdminAcl::getResourceList())->mapWithKeys(function ($resource) use ($modelRules) {
return [AdminAcl::COLUMN_IDENTIFIER . $resource => $modelRules['r_' . $resource]];
})->merge(['memo' => $modelRules['memo']])->toArray();
}
public function attributes(): array
{
return [
'memo' => 'Description',
];
}
public function getKeyPermissions(): array
{
return collect($this->validated())->filter(function ($value, $key) {
return substr($key, 0, strlen(AdminAcl::COLUMN_IDENTIFIER)) === AdminAcl::COLUMN_IDENTIFIER;
})->toArray();
}
}

View file

@ -1,13 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
class BaseFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'company' => 'required|between:1,256',
];
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
use Pterodactyl\Models\DatabaseHost;
use Illuminate\Contracts\Validation\Validator;
class DatabaseHostFormRequest extends AdminFormRequest
{
public function rules(): array
{
if ($this->method() !== 'POST') {
return DatabaseHost::getRulesForUpdate($this->route()->parameter('host'));
}
return DatabaseHost::getRules();
}
/**
* Modify submitted data before it is passed off to the validator.
*/
protected function getValidatorInstance(): Validator
{
if (!$this->filled('node_id')) {
$this->merge(['node_id' => null]);
}
return parent::getValidatorInstance();
}
}

View file

@ -1,47 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Egg;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class EggFormRequest extends AdminFormRequest
{
public function rules(): array
{
$rules = [
'name' => 'required|string|max:191',
'description' => 'nullable|string',
'docker_images' => 'required|string',
'force_outgoing_ip' => 'sometimes|boolean',
'file_denylist' => 'array',
'startup' => 'required|string',
'config_from' => 'sometimes|bail|nullable|numeric',
'config_stop' => 'required_without:config_from|nullable|string|max:191',
'config_startup' => 'required_without:config_from|nullable|json',
'config_logs' => 'required_without:config_from|nullable|json',
'config_files' => 'required_without:config_from|nullable|json',
];
if ($this->method() === 'POST') {
$rules['nest_id'] = 'required|numeric|exists:nests,id';
}
return $rules;
}
public function withValidator($validator)
{
$validator->sometimes('config_from', 'exists:eggs,id', function () {
return (int) $this->input('config_from') !== 0;
});
}
public function validated($key = null, $default = null): array
{
$data = parent::validated();
return array_merge($data, [
'force_outgoing_ip' => array_get($data, 'force_outgoing_ip', false),
]);
}
}

View file

@ -1,21 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Egg;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class EggImportFormRequest extends AdminFormRequest
{
public function rules(): array
{
$rules = [
'import_file' => 'bail|required|file|max:1000|mimetypes:application/json,text/plain',
];
if ($this->method() !== 'PUT') {
$rules['import_to_nest'] = 'bail|required|integer|exists:nests,id';
}
return $rules;
}
}

View file

@ -1,22 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Egg;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class EggScriptFormRequest extends AdminFormRequest
{
/**
* Return the rules to be used when validating the data sent in the request.
*/
public function rules(): array
{
return [
'script_install' => 'sometimes|nullable|string',
'script_is_privileged' => 'sometimes|required|boolean',
'script_entry' => 'sometimes|required|string',
'script_container' => 'sometimes|required|string',
'copy_script_from' => 'sometimes|nullable|numeric',
];
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Egg;
use Pterodactyl\Models\EggVariable;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class EggVariableFormRequest extends AdminFormRequest
{
/**
* Define rules for validation of this request.
*/
public function rules(): array
{
return [
'name' => 'required|string|min:1|max:191',
'description' => 'sometimes|nullable|string',
'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES,
'options' => 'sometimes|required|array',
'rules' => 'bail|required|string',
'default_value' => 'present',
];
}
}

View file

@ -1,20 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
use Pterodactyl\Models\Location;
class LocationFormRequest extends AdminFormRequest
{
/**
* Set up the validation rules to use for these requests.
*/
public function rules(): array
{
if ($this->method() === 'PATCH') {
return Location::getRulesForUpdate($this->route()->parameter('location')->id);
}
return Location::getRules();
}
}

View file

@ -1,20 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
use Pterodactyl\Models\Mount;
class MountFormRequest extends AdminFormRequest
{
/**
* Set up the validation rules to use for these requests.
*/
public function rules(): array
{
if ($this->method() === 'PATCH') {
return Mount::getRulesForUpdate($this->route()->parameter('mount')->id);
}
return Mount::getRules();
}
}

View file

@ -1,16 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Nest;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class StoreNestFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|min:1|max:191',
'description' => 'string|nullable',
];
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
use Pterodactyl\Models\User;
use Illuminate\Support\Collection;
class NewUserFormRequest extends AdminFormRequest
{
/**
* Rules to apply to requests for updating or creating a user
* in the Admin CP.
*/
public function rules(): array
{
return Collection::make(
User::getRules()
)->only([
'email',
'username',
'name_first',
'name_last',
'password',
'language',
'root_admin',
])->toArray();
}
}

View file

@ -1,16 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Node;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class AllocationAliasFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'alias' => 'present|nullable|string',
'allocation_id' => 'required|numeric|exists:allocations,id',
];
}
}

View file

@ -1,17 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Node;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class AllocationFormRequest extends AdminFormRequest
{
public function rules(): array
{
return [
'allocation_ip' => 'required|string',
'allocation_alias' => 'sometimes|nullable|string|max:191',
'allocation_ports' => 'required|array',
];
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Node;
use Pterodactyl\Rules\Fqdn;
use Pterodactyl\Models\Node;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class NodeFormRequest extends AdminFormRequest
{
/**
* Get rules to apply to data in this request.
*/
public function rules(): array
{
if ($this->method() === 'PATCH') {
return Node::getRulesForUpdate($this->route()->parameter('node'));
}
$data = Node::getRules();
$data['fqdn'][] = Fqdn::make('scheme');
return $data;
}
}

View file

@ -1,58 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
use Pterodactyl\Models\Server;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class ServerFormRequest extends AdminFormRequest
{
/**
* Rules to be applied to this request.
*/
public function rules(): array
{
$rules = Server::getRules();
$rules['description'][] = 'nullable';
$rules['custom_image'] = 'sometimes|nullable|string';
return $rules;
}
/**
* Run validation after the rules above have been applied.
*/
public function withValidator(Validator $validator): void
{
$validator->after(function ($validator) {
$validator->sometimes('node_id', 'required|numeric|bail|exists:nodes,id', function ($input) {
return !$input->auto_deploy;
});
$validator->sometimes('allocation_id', [
'required',
'numeric',
'bail',
Rule::exists('allocations', 'id')->where(function ($query) {
$query->where('node_id', $this->input('node_id'));
$query->whereNull('server_id');
}),
], function ($input) {
return !$input->auto_deploy;
});
$validator->sometimes('allocation_additional.*', [
'sometimes',
'required',
'numeric',
Rule::exists('allocations', 'id')->where(function ($query) {
$query->where('node_id', $this->input('node_id'));
$query->whereNull('server_id');
}),
], function ($input) {
return !$input->auto_deploy;
});
});
}
}

View file

@ -1,31 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Servers\Databases;
use Illuminate\Validation\Rule;
use Illuminate\Database\Query\Builder;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class StoreServerDatabaseRequest extends AdminFormRequest
{
/**
* Validation rules for database creation.
*/
public function rules(): array
{
return [
'database' => [
'required',
'string',
'min:1',
'max:24',
Rule::unique('databases')->where(function (Builder $query) {
$query->where('database_host_id', $this->input('database_host_id') ?? 0);
}),
],
'max_connections' => 'nullable',
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
'database_host_id' => 'required|integer|exists:database_hosts,id',
];
}
}

View file

@ -1,50 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Settings;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class AdvancedSettingsFormRequest extends AdminFormRequest
{
/**
* Return all the rules to apply to this request's data.
*/
public function rules(): array
{
return [
'recaptcha:enabled' => 'required|in:true,false',
'recaptcha:secret_key' => 'required|string|max:191',
'recaptcha:website_key' => 'required|string|max:191',
'pterodactyl:guzzle:timeout' => 'required|integer|between:1,60',
'pterodactyl:guzzle:connect_timeout' => 'required|integer|between:1,60',
'pterodactyl:client_features:allocations:enabled' => 'required|in:true,false',
'pterodactyl:client_features:allocations:range_start' => [
'nullable',
'required_if:pterodactyl:client_features:allocations:enabled,true',
'integer',
'between:1024,65535',
],
'pterodactyl:client_features:allocations:range_end' => [
'nullable',
'required_if:pterodactyl:client_features:allocations:enabled,true',
'integer',
'between:1024,65535',
'gt:pterodactyl:client_features:allocations:range_start',
],
];
}
public function attributes(): array
{
return [
'recaptcha:enabled' => 'reCAPTCHA Enabled',
'recaptcha:secret_key' => 'reCAPTCHA Secret Key',
'recaptcha:website_key' => 'reCAPTCHA Website Key',
'pterodactyl:guzzle:timeout' => 'HTTP Request Timeout',
'pterodactyl:guzzle:connect_timeout' => 'HTTP Connection Timeout',
'pterodactyl:client_features:allocations:enabled' => 'Auto Create Allocations Enabled',
'pterodactyl:client_features:allocations:range_start' => 'Starting Port',
'pterodactyl:client_features:allocations:range_end' => 'Ending Port',
];
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Settings;
use Illuminate\Validation\Rule;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class BaseSettingsFormRequest extends AdminFormRequest
{
use AvailableLanguages;
public function rules(): array
{
return [
'app:name' => 'required|string|max:191',
'pterodactyl:auth:2fa_required' => 'required|integer|in:0,1,2',
'app:locale' => ['required', 'string', Rule::in(array_keys($this->getAvailableLanguages()))],
];
}
public function attributes(): array
{
return [
'app:name' => 'Company Name',
'pterodactyl:auth:2fa_required' => 'Require 2-Factor Authentication',
'app:locale' => 'Default Language',
];
}
}

View file

@ -1,40 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin\Settings;
use Illuminate\Validation\Rule;
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
class MailSettingsFormRequest extends AdminFormRequest
{
/**
* Return rules to validate mail settings POST data against.
*/
public function rules(): array
{
return [
'mail:host' => 'required|string',
'mail:port' => 'required|integer|between:1,65535',
'mail:encryption' => ['present', Rule::in([null, 'tls', 'ssl'])],
'mail:username' => 'nullable|string|max:191',
'mail:password' => 'nullable|string|max:191',
'mail:from:address' => 'required|string|email',
'mail:from:name' => 'nullable|string|max:191',
];
}
/**
* Override the default normalization function for this type of request
* as we need to accept empty values on the keys.
*/
public function normalize(array $only = null): array
{
$keys = array_flip(array_keys($this->rules()));
if (empty($this->input('mail:password'))) {
unset($keys['mail:password']);
}
return $this->only(array_flip($keys));
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Admin;
use Pterodactyl\Models\User;
use Illuminate\Support\Collection;
class UserFormRequest extends AdminFormRequest
{
/**
* Rules to apply to requests for updating or creating a user
* in the Admin CP.
*/
public function rules(): array
{
return Collection::make(
User::getRulesForUpdate($this->route()->parameter('user'))
)->only([
'email',
'username',
'name_first',
'name_last',
'password',
'language',
'root_admin',
])->toArray();
}
}

View file

@ -0,0 +1,83 @@
<?php
namespace Pterodactyl\Http\Requests\Api;
use Illuminate\Foundation\Http\FormRequest;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* @method \Pterodactyl\Models\User user($guard = null)
*/
abstract class ApiRequest extends FormRequest
{
/**
* Tracks if the request has been validated internally or not to avoid
* making duplicate validation calls.
*/
private bool $hasValidated = false;
/**
* Determine if the current user is authorized to perform the requested
* action against the API.
*/
public function authorize(): bool
{
return false;
}
/**
* Default set of rules to apply to API requests.
*/
public function rules(): array
{
return [];
}
/**
* Validate that the resource exists and can be accessed prior to booting
* the validator and attempting to use the data.
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
protected function prepareForValidation()
{
if (!$this->passesAuthorization()) {
$this->failedAuthorization();
}
$this->hasValidated = true;
}
/*
* 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()
{
// If we have already validated we do not need to call this function
// again. This is needed to work around Laravel's normal auth validation
// that occurs after validating the request params since we are doing auth
// validation in the prepareForValidation() function.
if ($this->hasValidated) {
return true;
}
if (!parent::passesAuthorization()) {
return false;
}
// 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 ($this->attributes->get('is_missing_model', false)) {
throw new NotFoundHttpException(trans('exceptions.api.resource_not_found'));
}
return true;
}
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Allocations;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteAllocationRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_ALLOCATIONS;
protected int $permission = AdminAcl::WRITE;
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Allocations;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetAllocationsRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_ALLOCATIONS;
protected int $permission = AdminAcl::READ;
}

View file

@ -2,15 +2,11 @@
namespace Pterodactyl\Http\Requests\Api\Application\Allocations;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Illuminate\Support\Arr;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreAllocationRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_ALLOCATIONS;
protected int $permission = AdminAcl::WRITE;
public function rules(): array
{
return [
@ -21,14 +17,22 @@ class StoreAllocationRequest extends ApplicationApiRequest
];
}
public function validated($key = null, $default = null): array
/**
* @param string|null $key
* @param string|array|null $default
*
* @return mixed
*/
public function validated($key = null, $default = null)
{
$data = parent::validated();
return [
$response = [
'allocation_ip' => $data['ip'],
'allocation_ports' => $data['ports'],
'allocation_alias' => $data['alias'] ?? null,
];
return is_null($key) ? $response : Arr::get($response, $key, $default);
}
}

View file

@ -2,92 +2,16 @@
namespace Pterodactyl\Http\Requests\Api\Application;
use Webmozart\Assert\Assert;
use Pterodactyl\Models\ApiKey;
use Laravel\Sanctum\TransientToken;
use Illuminate\Validation\Validator;
use Illuminate\Database\Eloquent\Model;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Illuminate\Foundation\Http\FormRequest;
use Pterodactyl\Exceptions\PterodactylException;
use Pterodactyl\Http\Requests\Api\ApiRequest;
abstract class ApplicationApiRequest extends FormRequest
abstract class ApplicationApiRequest extends ApiRequest
{
/**
* The resource that should be checked when performing the authorization
* function for this request.
*/
protected ?string $resource;
/**
* The permission level that a given API key should have for accessing
* the defined $resource during the request cycle.
*/
protected int $permission = AdminAcl::NONE;
/**
* Determine if the current user is authorized to perform
* the requested action against the API.
*
* @throws \Pterodactyl\Exceptions\PterodactylException
* This will eventually be replaced with per-request permissions checking
* on the API key and for the user.
*/
public function authorize(): bool
{
if (is_null($this->resource)) {
throw new PterodactylException('An ACL resource must be defined on API requests.');
}
$token = $this->user()->currentAccessToken();
if ($token instanceof TransientToken) {
return true;
}
if ($token->key_type === ApiKey::TYPE_ACCOUNT) {
return true;
}
return AdminAcl::check($token, $this->resource, $this->permission);
}
/**
* Default set of rules to apply to API requests.
*/
public function rules(): array
{
return [];
}
/**
* Helper method allowing a developer to easily hook into this logic without having
* to remember what the method name is called or where to use it. By default this is
* a no-op.
*/
public function withValidator(Validator $validator): void
{
// do nothing
}
/**
* Returns the named route parameter and asserts that it is a real model that
* exists in the database.
*
* @template T of \Illuminate\Database\Eloquent\Model
*
* @param class-string<T> $expect
*
* @return T
*
* @noinspection PhpDocSignatureInspection
*/
public function parameter(string $key, string $expect)
{
$value = $this->route()->parameter($key);
Assert::isInstanceOf($value, $expect);
Assert::isInstanceOf($value, Model::class);
Assert::true($value->exists);
/* @var T $value */
return $value;
return $this->user()->root_admin;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Databases;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteDatabaseRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Databases;
class GetDatabaseRequest extends GetDatabasesRequest
{
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Databases;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetDatabasesRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,14 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Databases;
use Pterodactyl\Models\DatabaseHost;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreDatabaseRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return $rules ?? DatabaseHost::getRules();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Databases;
use Pterodactyl\Models\DatabaseHost;
class UpdateDatabaseRequest extends StoreDatabaseRequest
{
public function rules(array $rules = null): array
{
return $rules ?? DatabaseHost::getRulesForUpdate($this->route()->parameter('databaseHost'));
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
use Pterodactyl\Models\Egg;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteEggRequest extends ApplicationApiRequest
{
public function resourceExists(): bool
{
$egg = $this->route()->parameter('egg');
return $egg instanceof Egg && $egg->exists;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class ExportEggRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
class GetEggRequest extends GetEggsRequest
{
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetEggsRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class ImportEggRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,30 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreEggRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return [
'nest_id' => 'required|bail|numeric|exists:nests,id',
'name' => 'required|string|max:191',
'description' => 'sometimes|string|nullable',
'features' => 'sometimes|array',
'docker_images' => 'required|array|min:1',
'docker_images.*' => 'required|string',
'file_denylist' => 'sometimes|array|nullable',
'file_denylist.*' => 'sometimes|string',
'config_files' => 'required|nullable|json',
'config_startup' => 'required|nullable|json',
'config_stop' => 'required|nullable|string|max:191',
// 'config_from' => 'sometimes|nullable|numeric|exists:eggs,id',
'startup' => 'required|string',
'script_container' => 'sometimes|string',
'script_entry' => 'sometimes|string',
'script_install' => 'sometimes|string',
];
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
class UpdateEggRequest extends StoreEggRequest
{
public function rules(array $rules = null): array
{
return [
'nest_id' => 'sometimes|numeric|exists:nests,id',
'name' => 'sometimes|string|max:191',
'description' => 'sometimes|string|nullable',
'features' => 'sometimes|array',
'docker_images' => 'sometimes|array|min:1',
'docker_images.*' => 'sometimes|string',
'file_denylist' => 'sometimes|array|nullable',
'file_denylist.*' => 'sometimes|string',
'config_files' => 'sometimes|nullable|json',
'config_startup' => 'sometimes|nullable|json',
'config_stop' => 'sometimes|nullable|string|max:191',
// 'config_from' => 'sometimes|nullable|numeric|exists:eggs,id',
'startup' => 'sometimes|string',
'script_container' => 'sometimes|string',
'script_entry' => 'sometimes|string',
'script_install' => 'sometimes|string',
];
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs\Variables;
use Pterodactyl\Models\EggVariable;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreEggVariableRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return [
'name' => 'required|string|min:1|max:191',
'description' => 'sometimes|string|nullable',
'env_variable' => 'required|regex:/^[\w]{1,191}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES,
'default_value' => 'present',
'user_viewable' => 'required|boolean',
'user_editable' => 'required|boolean',
'rules' => 'bail|required|string',
];
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs\Variables;
use Pterodactyl\Models\EggVariable;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class UpdateEggVariablesRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return [
'*' => 'array',
'*.id' => 'required|integer',
'*.name' => 'sometimes|string|min:1|max:191',
'*.description' => 'sometimes|string|nullable',
'*.env_variable' => 'sometimes|regex:/^[\w]{1,191}$/|notIn:' . EggVariable::RESERVED_ENV_NAMES,
'*.default_value' => 'sometimes|present',
'*.user_viewable' => 'sometimes|boolean',
'*.user_editable' => 'sometimes|boolean',
'*.rules' => 'sometimes|string',
];
}
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Locations;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteLocationRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_LOCATIONS;
protected int $permission = AdminAcl::WRITE;
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Locations;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetLocationsRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_LOCATIONS;
protected int $permission = AdminAcl::READ;
}

View file

@ -3,18 +3,10 @@
namespace Pterodactyl\Http\Requests\Api\Application\Locations;
use Pterodactyl\Models\Location;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreLocationRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_LOCATIONS;
protected int $permission = AdminAcl::WRITE;
/**
* Rules to validate the request against.
*/
public function rules(): array
{
return collect(Location::getRules())->only([
@ -23,9 +15,6 @@ class StoreLocationRequest extends ApplicationApiRequest
])->toArray();
}
/**
* Rename fields to be more clear in error messages.
*/
public function attributes(): array
{
return [

View file

@ -6,12 +6,9 @@ use Pterodactyl\Models\Location;
class UpdateLocationRequest extends StoreLocationRequest
{
/**
* Rules to validate this request against.
*/
public function rules(): array
{
$locationId = $this->route()->parameter('location')->id;
$locationId = $this->route()->parameter('location');
return collect(Location::getRulesForUpdate($locationId))->only([
'short',

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Mounts;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteMountRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Mounts;
class GetMountRequest extends GetMountsRequest
{
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Mounts;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetMountsRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,13 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Mounts;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class MountEggsRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return $rules ?? ['eggs' => 'required|exists:eggs,id'];
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Mounts;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class MountNodesRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return $rules ?? ['nodes' => 'required|exists:nodes,id'];
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Mounts;
use Pterodactyl\Models\Mount;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreMountRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return $rules ?? Mount::getRules();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Mounts;
use Pterodactyl\Models\Mount;
class UpdateMountRequest extends StoreMountRequest
{
public function rules(array $rules = null): array
{
return $rules ?? Mount::getRulesForUpdate($this->route()->parameter('mount'));
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Nests;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteNestRequest extends ApplicationApiRequest
{
}

View file

@ -1,13 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Nests\Eggs;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetEggRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_EGGS;
protected int $permission = AdminAcl::READ;
}

View file

@ -1,13 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Nests\Eggs;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetEggsRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_EGGS;
protected int $permission = AdminAcl::READ;
}

View file

@ -0,0 +1,7 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Nests;
class GetNestRequest extends GetNestsRequest
{
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Nests;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetNestsRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_NESTS;
protected int $permission = AdminAcl::READ;
}

View file

@ -0,0 +1,14 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Nests;
use Pterodactyl\Models\Nest;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreNestRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return $rules ?? Nest::getRules();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Nests;
use Pterodactyl\Models\Nest;
class UpdateNestRequest extends StoreNestRequest
{
public function rules(array $rules = null): array
{
return $rules ?? Nest::getRulesForUpdate($this->route()->parameter('nest'));
}
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Nodes;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteNodeRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_NODES;
protected int $permission = AdminAcl::WRITE;
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Nodes;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetNodesRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_NODES;
protected int $permission = AdminAcl::READ;
}

View file

@ -2,39 +2,40 @@
namespace Pterodactyl\Http\Requests\Api\Application\Nodes;
use Illuminate\Support\Arr;
use Pterodactyl\Models\Node;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreNodeRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_NODES;
protected int $permission = AdminAcl::WRITE;
/**
* Validation rules to apply to this request.
*/
public function rules(array $rules = null): array
{
return collect($rules ?? Node::getRules())->only([
'public',
'name',
'description',
'location_id',
'database_host_id',
'fqdn',
'scheme',
'behind_proxy',
'public',
'listen_port_http',
'public_port_http',
'listen_port_sftp',
'public_port_sftp',
'memory',
'memory_overallocate',
'disk',
'disk_overallocate',
'upload_size',
'daemonListen',
'daemonSFTP',
'daemonBase',
])->mapWithKeys(function ($value, $key) {
$key = ($key === 'daemonSFTP') ? 'daemonSftp' : $key;
'upload_size',
'daemon_base',
])->mapWithKeys(function ($value, $key) {
return [snake_case($key) => $value];
})->toArray();
}
@ -59,11 +60,11 @@ class StoreNodeRequest extends ApplicationApiRequest
public function validated($key = null, $default = null): array
{
$response = parent::validated();
$response['daemonListen'] = $response['daemon_listen'];
$response['daemonSFTP'] = $response['daemon_sftp'];
$response['daemonBase'] = $response['daemon_base'] ?? (new Node())->getAttribute('daemonBase');
$response['daemon_base'] = $response['daemon_base'] ?? Node::DEFAULT_DAEMON_BASE;
unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']);
if (!is_null($key)) {
return Arr::get($response, $key, $default);
}
return $response;
}

View file

@ -6,14 +6,8 @@ use Pterodactyl\Models\Node;
class UpdateNodeRequest extends StoreNodeRequest
{
/**
* Apply validation rules to this request. Uses the parent class rules()
* function but passes in the rules for updating rather than creating.
*/
public function rules(array $rules = null): array
{
$node = $this->route()->parameter('node')->id;
return parent::rules(Node::getRulesForUpdate($node));
return parent::rules($rules ?? Node::getRulesForUpdate($this->route()->parameter('node')));
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Roles;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteRoleRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Roles;
class GetRoleRequest extends GetRolesRequest
{
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Roles;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetRolesRequest extends ApplicationApiRequest
{
}

View file

@ -0,0 +1,14 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Roles;
use Pterodactyl\Models\AdminRole;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreRoleRequest extends ApplicationApiRequest
{
public function rules(array $rules = null): array
{
return $rules ?? AdminRole::getRules();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Roles;
use Pterodactyl\Models\AdminRole;
class UpdateRoleRequest extends StoreRoleRequest
{
public function rules(array $rules = null): array
{
return $rules ?? AdminRole::getRulesForUpdate($this->route()->parameter('role'));
}
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetServerDatabaseRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVER_DATABASES;
protected int $permission = AdminAcl::READ;
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetServerDatabasesRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVER_DATABASES;
protected int $permission = AdminAcl::READ;
}

View file

@ -2,9 +2,6 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
use Pterodactyl\Services\Acl\Api\AdminAcl;
class ServerDatabaseWriteRequest extends GetServerDatabasesRequest
{
protected int $permission = AdminAcl::WRITE;
}

View file

@ -2,25 +2,19 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
use Illuminate\Support\Arr;
use Webmozart\Assert\Assert;
use Pterodactyl\Models\Server;
use Illuminate\Validation\Rule;
use Illuminate\Database\Query\Builder;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Services\Databases\DatabaseManagementService;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreServerDatabaseRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVER_DATABASES;
protected int $permission = AdminAcl::WRITE;
/**
* Validation rules for database creation.
*/
public function rules(): array
{
/** @var \Pterodactyl\Models\Server $server */
$server = $this->route()->parameter('server');
return [
@ -39,20 +33,22 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest
}
/**
* Return data formatted in the correct format for the service to consume.
* @param string|null $key
* @param string|array|null $default
*
* @return mixed
*/
public function validated($key = null, $default = null): array
public function validated($key = null, $default = null)
{
return [
$data = [
'database' => $this->input('database'),
'remote' => $this->input('remote'),
'database_host_id' => $this->input('host'),
];
return is_null($key) ? $data : Arr::get($data, $key, $default);
}
/**
* Format error messages in a more understandable format for API output.
*/
public function attributes(): array
{
return [
@ -62,9 +58,6 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest
];
}
/**
* Returns the database name in the expected format.
*/
public function databaseName(): string
{
$server = $this->route()->parameter('server');

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetExternalServerRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVERS;
protected int $permission = AdminAcl::READ;
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetServerRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVERS;
protected int $permission = AdminAcl::READ;
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class ServerWriteRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVERS;
protected int $permission = AdminAcl::WRITE;
}

View file

@ -2,22 +2,12 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Illuminate\Support\Arr;
use Pterodactyl\Models\Server;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Models\Objects\DeploymentObject;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreServerRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVERS;
protected int $permission = AdminAcl::WRITE;
/**
* Rules to be applied to this request.
*/
public function rules(): array
{
$rules = Server::getRules();
@ -26,15 +16,9 @@ class StoreServerRequest extends ApplicationApiRequest
'external_id' => $rules['external_id'],
'name' => $rules['name'],
'description' => array_merge(['nullable'], $rules['description']),
'user' => $rules['owner_id'],
'egg' => $rules['egg_id'],
'docker_image' => $rules['image'],
'startup' => $rules['startup'],
'environment' => 'present|array',
'skip_scripts' => 'sometimes|boolean',
'oom_disabled' => 'sometimes|boolean',
'owner_id' => $rules['owner_id'],
'node_id' => $rules['node_id'],
// Resource limitations
'limits' => 'required|array',
'limits.memory' => $rules['memory'],
'limits.swap' => $rules['swap'],
@ -42,110 +26,64 @@ class StoreServerRequest extends ApplicationApiRequest
'limits.io' => $rules['io'],
'limits.threads' => $rules['threads'],
'limits.cpu' => $rules['cpu'],
'limits.oom_killer' => 'required|boolean',
// Application Resource Limits
'feature_limits' => 'required|array',
'feature_limits.databases' => $rules['database_limit'],
'feature_limits.allocations' => $rules['allocation_limit'],
'feature_limits.backups' => $rules['backup_limit'],
'feature_limits.databases' => $rules['database_limit'],
// Placeholders for rules added in withValidator() function.
'allocation.default' => '',
'allocation.additional.*' => '',
'allocation.default' => 'required|bail|integer|exists:allocations,id',
'allocation.additional.*' => 'integer|exists:allocations,id',
// Automatic deployment rules
'deploy' => 'sometimes|required|array',
'deploy.locations' => 'array',
'deploy.locations.*' => 'integer|min:1',
'deploy.dedicated_ip' => 'required_with:deploy,boolean',
'deploy.port_range' => 'array',
'deploy.port_range.*' => 'string',
'start_on_completion' => 'sometimes|boolean',
'startup' => $rules['startup'],
'environment' => 'present|array',
'egg_id' => $rules['egg_id'],
'image' => $rules['image'],
'skip_scripts' => 'present|boolean',
];
}
/**
* Normalize the data into a format that can be consumed by the service.
* @param string|null $key
* @param string|array|null $default
*
* @return array
*/
public function validated($key = null, $default = null): array
public function validated($key = null, $default = null)
{
$data = parent::validated();
return [
$response = [
'external_id' => array_get($data, 'external_id'),
'name' => array_get($data, 'name'),
'description' => array_get($data, 'description'),
'owner_id' => array_get($data, 'user'),
'egg_id' => array_get($data, 'egg'),
'image' => array_get($data, 'docker_image'),
'startup' => array_get($data, 'startup'),
'environment' => array_get($data, 'environment'),
'owner_id' => array_get($data, 'owner_id'),
'node_id' => array_get($data, 'node_id'),
'memory' => array_get($data, 'limits.memory'),
'swap' => array_get($data, 'limits.swap'),
'disk' => array_get($data, 'limits.disk'),
'io' => array_get($data, 'limits.io'),
'cpu' => array_get($data, 'limits.cpu'),
'threads' => array_get($data, 'limits.threads'),
'skip_scripts' => array_get($data, 'skip_scripts', false),
'allocation_id' => array_get($data, 'allocation.default'),
'allocation_additional' => array_get($data, 'allocation.additional'),
'start_on_completion' => array_get($data, 'start_on_completion', false),
'database_limit' => array_get($data, 'feature_limits.databases'),
'cpu' => array_get($data, 'limits.cpu'),
'oom_killer' => array_get($data, 'limits.oom_killer'),
'allocation_limit' => array_get($data, 'feature_limits.allocations'),
'backup_limit' => array_get($data, 'feature_limits.backups'),
'oom_disabled' => array_get($data, 'oom_disabled'),
'database_limit' => array_get($data, 'feature_limits.databases'),
'allocation_id' => array_get($data, 'allocation.default'),
'allocation_additional' => array_get($data, 'allocation.additional'),
'startup' => array_get($data, 'startup'),
'environment' => array_get($data, 'environment'),
'egg_id' => array_get($data, 'egg_id'),
'image' => array_get($data, 'image'),
'skip_scripts' => array_get($data, 'skip_scripts'),
'start_on_completion' => array_get($data, 'start_on_completion', false),
];
}
/*
* Run validation after the rules above have been applied.
*
* @param \Illuminate\Validation\Validator $validator
*/
public function withValidator(Validator $validator): void
{
$validator->sometimes('allocation.default', [
'required', 'integer', 'bail',
Rule::exists('allocations', 'id')->where(function ($query) {
$query->whereNull('server_id');
}),
], function ($input) {
return !$input->deploy;
});
$validator->sometimes('allocation.additional.*', [
'integer',
Rule::exists('allocations', 'id')->where(function ($query) {
$query->whereNull('server_id');
}),
], function ($input) {
return !$input->deploy;
});
$validator->sometimes('deploy.locations', 'present', function ($input) {
return $input->deploy;
});
$validator->sometimes('deploy.port_range', 'present', function ($input) {
return $input->deploy;
});
}
/**
* Return a deployment object that can be passed to the server creation service.
*/
public function getDeploymentObject(): ?DeploymentObject
{
if (is_null($this->input('deploy'))) {
return null;
}
$object = new DeploymentObject();
$object->setDedicated($this->input('deploy.dedicated_ip', false));
$object->setLocations($this->input('deploy.locations', []));
$object->setPorts($this->input('deploy.port_range', []));
return $object;
return is_null($key) ? $response : Arr::get($response, $key, $default);
}
}

View file

@ -1,113 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Pterodactyl\Models\Server;
use Illuminate\Support\Collection;
class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
{
/**
* Return the rules to validate this request against.
*/
public function rules(): array
{
$rules = Server::getRulesForUpdate($this->parameter('server', Server::class));
return [
'allocation' => $rules['allocation_id'],
'oom_disabled' => $rules['oom_disabled'],
'limits' => 'sometimes|array',
'limits.memory' => $this->requiredToOptional('memory', $rules['memory'], true),
'limits.swap' => $this->requiredToOptional('swap', $rules['swap'], true),
'limits.io' => $this->requiredToOptional('io', $rules['io'], true),
'limits.cpu' => $this->requiredToOptional('cpu', $rules['cpu'], true),
'limits.threads' => $this->requiredToOptional('threads', $rules['threads'], true),
'limits.disk' => $this->requiredToOptional('disk', $rules['disk'], true),
// Legacy rules to maintain backwards compatable API support without requiring
// a major version bump.
//
// @see https://github.com/pterodactyl/panel/issues/1500
'memory' => $this->requiredToOptional('memory', $rules['memory']),
'swap' => $this->requiredToOptional('swap', $rules['swap']),
'io' => $this->requiredToOptional('io', $rules['io']),
'cpu' => $this->requiredToOptional('cpu', $rules['cpu']),
'threads' => $this->requiredToOptional('threads', $rules['threads']),
'disk' => $this->requiredToOptional('disk', $rules['disk']),
'add_allocations' => 'bail|array',
'add_allocations.*' => 'integer',
'remove_allocations' => 'bail|array',
'remove_allocations.*' => 'integer',
'feature_limits' => 'required|array',
'feature_limits.databases' => $rules['database_limit'],
'feature_limits.allocations' => $rules['allocation_limit'],
'feature_limits.backups' => $rules['backup_limit'],
];
}
/**
* Convert the allocation field into the expected format for the service handler.
*/
public function validated($key = null, $default = null): array
{
$data = parent::validated();
$data['allocation_id'] = $data['allocation'];
$data['database_limit'] = $data['feature_limits']['databases'] ?? null;
$data['allocation_limit'] = $data['feature_limits']['allocations'] ?? null;
$data['backup_limit'] = $data['feature_limits']['backups'] ?? null;
unset($data['allocation'], $data['feature_limits']);
// Adjust the limits field to match what is expected by the model.
if (!empty($data['limits'])) {
foreach ($data['limits'] as $key => $value) {
$data[$key] = $value;
}
unset($data['limits']);
}
return $data;
}
/**
* Custom attributes to use in error message responses.
*/
public function attributes(): array
{
return [
'add_allocations' => 'allocations to add',
'remove_allocations' => 'allocations to remove',
'add_allocations.*' => 'allocation to add',
'remove_allocations.*' => 'allocation to remove',
'feature_limits.databases' => 'Database Limit',
'feature_limits.allocations' => 'Allocation Limit',
'feature_limits.backups' => 'Backup Limit',
];
}
/**
* Converts existing rules for certain limits into a format that maintains backwards
* compatability with the old API endpoint while also supporting a more correct API
* call.
*
* @see https://github.com/pterodactyl/panel/issues/1500
*/
protected function requiredToOptional(string $field, array $rules, bool $limits = false): array
{
if (!in_array('required', $rules)) {
return $rules;
}
return (new Collection($rules))
->filter(function ($value) {
return $value !== 'required';
})
->prepend($limits ? 'required_with:limits' : 'required_without:limits')
->toArray();
}
}

View file

@ -1,49 +0,0 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Pterodactyl\Models\Server;
class UpdateServerDetailsRequest extends ServerWriteRequest
{
/**
* Rules to apply to a server details update request.
*/
public function rules(): array
{
$rules = Server::getRulesForUpdate($this->parameter('server', Server::class));
return [
'external_id' => $rules['external_id'],
'name' => $rules['name'],
'user' => $rules['owner_id'],
'description' => array_merge(['nullable'], $rules['description']),
];
}
/**
* Convert the posted data into the correct format that is expected
* by the application.
*/
public function validated($key = null, $default = null): array
{
return [
'external_id' => $this->input('external_id'),
'name' => $this->input('name'),
'owner_id' => $this->input('user'),
'description' => $this->input('description'),
];
}
/**
* Rename some attributes in error messages to clarify the field
* being discussed.
*/
public function attributes(): array
{
return [
'user' => 'User ID',
'name' => 'Server Name',
];
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Illuminate\Support\Arr;
use Pterodactyl\Models\Server;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class UpdateServerRequest extends ApplicationApiRequest
{
public function rules(): array
{
$rules = Server::getRules();
return [
'external_id' => $rules['external_id'],
'name' => $rules['name'],
'description' => array_merge(['nullable'], $rules['description']),
'owner_id' => $rules['owner_id'],
'limits' => 'sometimes|array',
'limits.memory' => $rules['memory'],
'limits.swap' => $rules['swap'],
'limits.disk' => $rules['disk'],
'limits.io' => $rules['io'],
'limits.threads' => $rules['threads'],
'limits.cpu' => $rules['cpu'],
'limits.oom_killer' => 'sometimes|boolean',
'feature_limits' => 'required|array',
'feature_limits.allocations' => $rules['allocation_limit'],
'feature_limits.backups' => $rules['backup_limit'],
'feature_limits.databases' => $rules['database_limit'],
'allocation_id' => 'bail|exists:allocations,id',
'add_allocations' => 'bail|array',
'add_allocations.*' => 'integer',
'remove_allocations' => 'bail|array',
'remove_allocations.*' => 'integer',
];
}
/**
* @param string|null $key
* @param string|array|null $default
*
* @return mixed
*/
public function validated($key = null, $default = null)
{
$data = parent::validated();
$response = [
'external_id' => array_get($data, 'external_id'),
'name' => array_get($data, 'name'),
'description' => array_get($data, 'description'),
'owner_id' => array_get($data, 'owner_id'),
'memory' => array_get($data, 'limits.memory'),
'swap' => array_get($data, 'limits.swap'),
'disk' => array_get($data, 'limits.disk'),
'io' => array_get($data, 'limits.io'),
'threads' => array_get($data, 'limits.threads'),
'cpu' => array_get($data, 'limits.cpu'),
'oom_killer' => array_get($data, 'limits.oom_killer'),
'allocation_limit' => array_get($data, 'feature_limits.allocations'),
'backup_limit' => array_get($data, 'feature_limits.backups'),
'database_limit' => array_get($data, 'feature_limits.databases'),
'allocation_id' => array_get($data, 'allocation_id'),
'add_allocations' => array_get($data, 'add_allocations'),
'remove_allocations' => array_get($data, 'remove_allocations'),
];
return is_null($key) ? $response : Arr::get($response, $key, $default);
}
}

View file

@ -3,41 +3,20 @@
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
use Pterodactyl\Models\Server;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class UpdateServerStartupRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_SERVERS;
protected int $permission = AdminAcl::WRITE;
/**
* Validation rules to run the input against.
*/
public function rules(): array
{
$data = Server::getRulesForUpdate($this->parameter('server', Server::class));
$rules = Server::getRulesForUpdate($this->route()->parameter('server'));
return [
'startup' => $data['startup'],
'startup' => $rules['startup'],
'environment' => 'present|array',
'egg' => $data['egg_id'],
'image' => $data['image'],
'egg_id' => $rules['egg_id'],
'image' => $rules['image'],
'skip_scripts' => 'present|boolean',
];
}
/**
* Return the validated data in a format that is expected by the service.
*/
public function validated($key = null, $default = null): array
{
$data = parent::validated();
return collect($data)->only(['startup', 'environment', 'skip_scripts'])->merge([
'egg_id' => array_get($data, 'egg'),
'docker_image' => array_get($data, 'image'),
])->toArray();
}
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Users;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class DeleteUserRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_USERS;
protected int $permission = AdminAcl::WRITE;
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Users;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetExternalUserRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_USERS;
protected int $permission = AdminAcl::READ;
}

View file

@ -0,0 +1,7 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Users;
class GetUserRequest extends GetUsersRequest
{
}

View file

@ -2,12 +2,8 @@
namespace Pterodactyl\Http\Requests\Api\Application\Users;
use Pterodactyl\Services\Acl\Api\AdminAcl as Acl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class GetUsersRequest extends ApplicationApiRequest
{
protected ?string $resource = Acl::RESOURCE_USERS;
protected int $permission = Acl::READ;
}

View file

@ -3,59 +3,21 @@
namespace Pterodactyl\Http\Requests\Api\Application\Users;
use Pterodactyl\Models\User;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class StoreUserRequest extends ApplicationApiRequest
{
protected ?string $resource = AdminAcl::RESOURCE_USERS;
protected int $permission = AdminAcl::WRITE;
/**
* Return the validation rules for this request.
*/
public function rules(array $rules = null): array
{
$rules = $rules ?? User::getRules();
$response = collect($rules)->only([
return collect($rules)->only([
'external_id',
'email',
'username',
'password',
'language',
'admin_role_id',
'root_admin',
])->toArray();
$response['first_name'] = $rules['name_first'];
$response['last_name'] = $rules['name_last'];
return $response;
}
public function validated($key = null, $default = null): array
{
$data = parent::validated();
$data['name_first'] = $data['first_name'];
$data['name_last'] = $data['last_name'];
unset($data['first_name'], $data['last_name']);
return $data;
}
/**
* Rename some fields to be more user friendly.
*/
public function attributes(): array
{
return [
'external_id' => 'Third Party Identifier',
'name_first' => 'First Name',
'name_last' => 'Last Name',
'root_admin' => 'Root Administrator Status',
];
}
}

View file

@ -6,13 +6,8 @@ use Pterodactyl\Models\User;
class UpdateUserRequest extends StoreUserRequest
{
/**
* Return the validation rules for this request.
*/
public function rules(array $rules = null): array
{
$userId = $this->parameter('user', User::class)->id;
return parent::rules(User::getRulesForUpdate($userId));
return parent::rules($rules ?? User::getRulesForUpdate($this->route()->parameter('user')));
}
}

View file

@ -2,7 +2,6 @@
namespace Pterodactyl\Http\Requests\Api\Client\Account;
use Exception;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\RSA;
use Pterodactyl\Models\UserSSHKey;
@ -71,7 +70,7 @@ class StoreSSHKeyRequest extends ClientApiRequest
public function getKeyFingerprint(): string
{
if (!$this->key) {
throw new Exception('The public key was not properly loaded for this request.');
throw new \Exception('The public key was not properly loaded for this request.');
}
return $this->key->getFingerprint('sha256');

View file

@ -11,6 +11,7 @@ use Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException;
class UpdateEmailRequest extends ClientApiRequest
{
/**
* @throws \Illuminate\Contracts\Container\BindingResolutionException
* @throws \Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException
*/
public function authorize(): bool

View file

@ -21,6 +21,7 @@ class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissions
public function rules(): array
{
/** @var Server $server */
$server = $this->route()->parameter('server');
Assert::isInstanceOf($server, Server::class);

View file

@ -13,6 +13,6 @@ class DownloadFileRequest extends ClientApiRequest
*/
public function authorize(): bool
{
return $this->user()->can('file.read', $this->parameter('server', Server::class));
return $this->user()->can('file.read', $this->route()->parameter('server'));
}
}

View file

@ -63,7 +63,6 @@ abstract class SubuserRequest extends ClientApiRequest
// Otherwise, get the current subuser's permission set, and ensure that the
// permissions they are trying to assign are not _more_ than the ones they
// already have.
/** @var \Pterodactyl\Models\Subuser|null $subuser */
/** @var \Pterodactyl\Services\Servers\GetUserPermissionsService $service */
$service = $this->container->make(GetUserPermissionsService::class);

View file

@ -17,11 +17,11 @@ class ActivityEventRequest extends FormRequest
return [
'data' => ['required', 'array'],
'data.*' => ['array'],
'data.*.user' => ['present', 'uuid'],
'data.*.user' => ['sometimes', 'nullable', 'uuid'],
'data.*.server' => ['required', 'uuid'],
'data.*.event' => ['required', 'string'],
'data.*.metadata' => ['present', 'nullable', 'array'],
'data.*.ip' => ['present', 'ip'],
'data.*.ip' => ['sometimes', 'nullable', 'ip'],
'data.*.timestamp' => ['required', 'string'],
];
}

View file

@ -15,6 +15,7 @@ class InstallationDataRequest extends FormRequest
{
return [
'successful' => 'present|boolean',
'reinstall' => 'sometimes|boolean',
];
}
}