Merge branch 'develop' into feature/api-key-changes

This commit is contained in:
Dane Everitt 2017-11-26 13:20:25 -06:00
commit 80ddd5b659
21 changed files with 124 additions and 136 deletions

View file

@ -2,10 +2,10 @@ APP_ENV=production
APP_DEBUG=false APP_DEBUG=false
APP_KEY=SomeRandomString3232RandomString APP_KEY=SomeRandomString3232RandomString
APP_THEME=pterodactyl APP_THEME=pterodactyl
APP_TIMEZONE=UTC APP_TIMEZONE=America/New_York
APP_CLEAR_TASKLOG=720 APP_CLEAR_TASKLOG=720
APP_DELETE_MINUTES=10 APP_DELETE_MINUTES=10
APP_URL=http://yoursite.com/ APP_URL=
DB_HOST=127.0.0.1 DB_HOST=127.0.0.1
DB_PORT=3306 DB_PORT=3306
@ -13,8 +13,8 @@ DB_DATABASE=panel
DB_USERNAME=pterodactyl DB_USERNAME=pterodactyl
DB_PASSWORD= DB_PASSWORD=
CACHE_DRIVER=redis CACHE_DRIVER=
SESSION_DRIVER=database SESSION_DRIVER=
HASHIDS_SALT= HASHIDS_SALT=
HASHIDS_LENGTH=8 HASHIDS_LENGTH=8
@ -25,9 +25,9 @@ MAIL_PORT=2525
MAIL_USERNAME= MAIL_USERNAME=
MAIL_PASSWORD= MAIL_PASSWORD=
MAIL_ENCRYPTION=tls MAIL_ENCRYPTION=tls
MAIL_FROM=you@example.com MAIL_FROM=no-reply@example.com
QUEUE_DRIVER=database QUEUE_DRIVER=
QUEUE_HIGH=high QUEUE_HIGH=high
QUEUE_STANDARD=standard QUEUE_STANDARD=standard
QUEUE_LOW=low QUEUE_LOW=low

View file

@ -13,9 +13,10 @@ services:
before_install: before_install:
- mysql -e 'CREATE DATABASE IF NOT EXISTS travis;' - mysql -e 'CREATE DATABASE IF NOT EXISTS travis;'
before_script: before_script:
- echo 'opcache.enable_cli=1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- cp .env.travis .env - cp .env.travis .env
- composer install --no-interaction --prefer-dist --no-suggest --verbose - composer install --no-interaction --prefer-dist --no-suggest
- php artisan migrate --seed -v - php artisan migrate --seed
script: script:
- vendor/bin/phpunit --coverage-clover coverage.xml - vendor/bin/phpunit --coverage-clover coverage.xml
notifications: notifications:

View file

@ -4,6 +4,12 @@ This file is a running track of new features and fixes to each version of the pa
This project follows [Semantic Versioning](http://semver.org) guidelines. This project follows [Semantic Versioning](http://semver.org) guidelines.
## v0.7.0-beta.3 (Derelict Dermodactylus) ## v0.7.0-beta.3 (Derelict Dermodactylus)
### Fixed
* `[beta.2]` — Fixes a bug that would cause an endless exception message stream in the console when attemping to setup environment settings in certain instances.
* `[beta.2]` — Fixes a bug causing the dropdown menu for a server's egg to display the wrong selected value.
* `[beta.2]` — Fixes a bug that would throw a red page of death when submitting an invalid egg variable value for a server in the Admin CP.
* `[beta.2]` — Someone found a `@todo` that I never `@todid` and thus database hosts could not be created without being linked to a node. This is fixed...
* `[beta.2]` — Fixes bug that caused incorrect rendering of CPU usage on server graphs due to missing variable.
### Changed ### Changed
* API keys have been changed to only use a single public key passed in a bearer token. All existing keys can continue being used, however only the first 32 characters should be sent. * API keys have been changed to only use a single public key passed in a bearer token. All existing keys can continue being used, however only the first 32 characters should be sent.

View file

@ -9,6 +9,7 @@
namespace Pterodactyl\Console\Commands\Environment; namespace Pterodactyl\Console\Commands\Environment;
use DateTimeZone;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel; use Illuminate\Contracts\Console\Kernel;
use Pterodactyl\Traits\Commands\EnvironmentWriterTrait; use Pterodactyl\Traits\Commands\EnvironmentWriterTrait;
@ -18,6 +19,25 @@ class AppSettingsCommand extends Command
{ {
use EnvironmentWriterTrait; use EnvironmentWriterTrait;
const ALLOWED_CACHE_DRIVERS = [
'redis' => 'Redis (recommended)',
'memcached' => 'Memcached',
];
const ALLOWED_SESSION_DRIVERS = [
'redis' => 'Redis (recommended)',
'memcached' => 'Memcached',
'database' => 'MySQL Database',
'file' => 'Filesystem',
'cookie' => 'Cookie',
];
const ALLOWED_QUEUE_DRIVERS = [
'redis' => 'Redis (recommended)',
'database' => 'MySQL Database',
'sync' => 'Sync',
];
/** /**
* @var \Illuminate\Contracts\Console\Kernel * @var \Illuminate\Contracts\Console\Kernel
*/ */
@ -37,11 +57,13 @@ class AppSettingsCommand extends Command
* @var string * @var string
*/ */
protected $signature = 'p:environment:setup protected $signature = 'p:environment:setup
{--new-salt : Wether or not to generate a new salt for Hashids.}
{--author= : The email that services created on this instance should be linked to.} {--author= : The email that services created on this instance should be linked to.}
{--url= : The URL that this Panel is running on.} {--url= : The URL that this Panel is running on.}
{--timezone= : The timezone to use for Panel times.} {--timezone= : The timezone to use for Panel times.}
{--cache= : The cache driver backend to use.} {--cache= : The cache driver backend to use.}
{--session= : The session driver backend to use.} {--session= : The session driver backend to use.}
{--queue= : The queue driver backend to use.}
{--redis-host= : Redis host to use for connections.} {--redis-host= : Redis host to use for connections.}
{--redis-pass= : Password used to connect to redis.} {--redis-pass= : Password used to connect to redis.}
{--redis-port= : Port to connect to redis over.}'; {--redis-port= : Port to connect to redis over.}';
@ -72,7 +94,7 @@ class AppSettingsCommand extends Command
*/ */
public function handle() public function handle()
{ {
if (empty($this->config->get('hashids.salt')) || $this->option('--new-salt')) { if (empty($this->config->get('hashids.salt')) || $this->option('new-salt')) {
$this->variables['HASHIDS_SALT'] = str_random(20); $this->variables['HASHIDS_SALT'] = str_random(20);
} }
@ -87,33 +109,31 @@ class AppSettingsCommand extends Command
); );
$this->output->comment(trans('command/messages.environment.app.timezone_help')); $this->output->comment(trans('command/messages.environment.app.timezone_help'));
$this->variables['APP_TIMEZONE'] = $this->option('timezone') ?? $this->ask( $this->variables['APP_TIMEZONE'] = $this->option('timezone') ?? $this->anticipate(
trans('command/messages.environment.app.timezone'), $this->config->get('app.timezone') trans('command/messages.environment.app.timezone'),
DateTimeZone::listIdentifiers(DateTimeZone::ALL),
$this->config->get('app.timezone')
); );
$selected = $this->config->get('cache.default', 'redis');
$this->variables['CACHE_DRIVER'] = $this->option('cache') ?? $this->choice( $this->variables['CACHE_DRIVER'] = $this->option('cache') ?? $this->choice(
trans('command/messages.environment.app.cache_driver'), [ trans('command/messages.environment.app.cache_driver'),
'redis' => 'Redis (recommended)', self::ALLOWED_CACHE_DRIVERS,
'memcached' => 'Memcached', array_key_exists($selected, self::ALLOWED_CACHE_DRIVERS) ? $selected : null
], $this->config->get('cache.default', 'redis')
); );
$selected = $this->config->get('session.driver', 'redis');
$this->variables['SESSION_DRIVER'] = $this->option('session') ?? $this->choice( $this->variables['SESSION_DRIVER'] = $this->option('session') ?? $this->choice(
trans('command/messages.environment.app.session_driver'), [ trans('command/messages.environment.app.session_driver'),
'redis' => 'Redis (recommended)', self::ALLOWED_SESSION_DRIVERS,
'memcached' => 'Memcached', array_key_exists($selected, self::ALLOWED_SESSION_DRIVERS) ? $selected : null
'database' => 'MySQL Database',
'file' => 'Filesystem',
'cookie' => 'Cookie',
], $this->config->get('session.driver', 'redis')
); );
$this->variables['QUEUE_DRIVER'] = $this->option('session') ?? $this->choice( $selected = $this->config->get('queue.default', 'redis');
trans('command/messages.environment.app.session_driver'), [ $this->variables['QUEUE_DRIVER'] = $this->option('queue') ?? $this->choice(
'redis' => 'Redis (recommended)', trans('command/messages.environment.app.queue_driver'),
'database' => 'MySQL Database', self::ALLOWED_QUEUE_DRIVERS,
'sync' => 'Sync', array_key_exists($selected, self::ALLOWED_QUEUE_DRIVERS) ? $selected : null
], $this->config->get('queue.driver', 'redis')
); );
$this->checkForRedis(); $this->checkForRedis();

View file

@ -9,6 +9,6 @@
namespace Pterodactyl\Exceptions; namespace Pterodactyl\Exceptions;
class DisplayValidationException extends PterodactylException class DisplayValidationException extends DisplayException
{ {
} }

View file

@ -319,14 +319,14 @@ class ServersController extends Controller
/** /**
* Display startup configuration page for a server. * Display startup configuration page for a server.
* *
* @param int $server * @param \Pterodactyl\Models\Server $server
* @return \Illuminate\View\View * @return \Illuminate\View\View
* *
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function viewStartup($server) public function viewStartup(Server $server)
{ {
$parameters = $this->repository->getVariablesWithValues($server, true); $parameters = $this->repository->getVariablesWithValues($server->id, true);
if (! $parameters->server->installed) { if (! $parameters->server->installed) {
abort(404); abort(404);
} }
@ -334,6 +334,7 @@ class ServersController extends Controller
$nests = $this->nestRepository->getWithEggs(); $nests = $this->nestRepository->getWithEggs();
Javascript::put([ Javascript::put([
'server' => $server,
'nests' => $nests->map(function ($item) { 'nests' => $nests->map(function ($item) {
return array_merge($item->toArray(), [ return array_merge($item->toArray(), [
'eggs' => $item->eggs->keyBy('id')->toArray(), 'eggs' => $item->eggs->keyBy('id')->toArray(),

View file

@ -38,6 +38,9 @@ class ConsoleController extends Controller
$server = $request->attributes->get('server'); $server = $request->attributes->get('server');
$this->setRequest($request)->injectJavascript([ $this->setRequest($request)->injectJavascript([
'server' => [
'cpu' => $server->cpu,
],
'meta' => [ 'meta' => [
'saveFile' => route('server.files.save', $server->uuidShort), 'saveFile' => route('server.files.save', $server->uuidShort),
'csrfToken' => csrf_token(), 'csrfToken' => csrf_token(),

View file

@ -18,6 +18,10 @@ class DatabaseHostFormRequest extends AdminFormRequest
*/ */
public function rules() public function rules()
{ {
if (! $this->has('node_id')) {
$this->merge(['node_id' => null]);
}
if ($this->method() !== 'POST') { if ($this->method() !== 'POST') {
return DatabaseHost::getUpdateRulesForId($this->route()->parameter('host')->id); return DatabaseHost::getUpdateRulesForId($this->route()->parameter('host')->id);
} }

View file

@ -63,14 +63,13 @@ class DatabaseHost extends Model implements CleansAttributes, ValidableContract
'host' => 'required', 'host' => 'required',
'port' => 'required', 'port' => 'required',
'username' => 'required', 'username' => 'required',
'node_id' => 'sometimes|required', 'node_id' => 'sometimes',
]; ];
/** /**
* Validation rules to assign to this model. * Validation rules to assign to this model.
* *
* @var array * @var array
* @todo the node_id field doesn't validate correctly if no node is provided in request
*/ */
protected static $dataIntegrityRules = [ protected static $dataIntegrityRules = [
'name' => 'string|max:255', 'name' => 'string|max:255',
@ -78,7 +77,7 @@ class DatabaseHost extends Model implements CleansAttributes, ValidableContract
'port' => 'numeric|between:1,65535', 'port' => 'numeric|between:1,65535',
'username' => 'string|max:32', 'username' => 'string|max:32',
'password' => 'nullable|string', 'password' => 'nullable|string',
'node_id' => 'nullable|exists:nodes,id', 'node_id' => 'nullable|integer|exists:nodes,id',
]; ];
/** /**

View file

@ -78,9 +78,10 @@ class StartupModificationService
* @param \Pterodactyl\Models\Server $server * @param \Pterodactyl\Models\Server $server
* @param array $data * @param array $data
* *
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/ */
public function handle(Server $server, array $data) public function handle(Server $server, array $data)
{ {

View file

@ -11,8 +11,8 @@ namespace Pterodactyl\Services\Servers;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;
use Pterodactyl\Traits\Services\HasUserLevels; use Pterodactyl\Traits\Services\HasUserLevels;
use Pterodactyl\Exceptions\DisplayValidationException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
@ -25,22 +25,22 @@ class VariableValidatorService
/** /**
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
*/ */
protected $optionVariableRepository; private $optionVariableRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/ */
protected $serverRepository; private $serverRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface
*/ */
protected $serverVariableRepository; private $serverVariableRepository;
/** /**
* @var \Illuminate\Contracts\Validation\Factory * @var \Illuminate\Contracts\Validation\Factory
*/ */
protected $validator; private $validator;
/** /**
* VariableValidatorService constructor. * VariableValidatorService constructor.
@ -68,32 +68,32 @@ class VariableValidatorService
* @param int $egg * @param int $egg
* @param array $fields * @param array $fields
* @return \Illuminate\Support\Collection * @return \Illuminate\Support\Collection
* @throws \Illuminate\Validation\ValidationException
*/ */
public function handle(int $egg, array $fields = []): Collection public function handle(int $egg, array $fields = []): Collection
{ {
$variables = $this->optionVariableRepository->findWhere([['egg_id', '=', $egg]]); $variables = $this->optionVariableRepository->findWhere([['egg_id', '=', $egg]]);
$messages = $this->validator->make([], []);
return $variables->map(function ($item) use ($fields) { $response = $variables->map(function ($item) use ($fields, $messages) {
// Skip doing anything if user is not an admin and // Skip doing anything if user is not an admin and
// variable is not user viewable or editable. // variable is not user viewable or editable.
if (! $this->isUserLevel(User::USER_LEVEL_ADMIN) && (! $item->user_editable || ! $item->user_viewable)) { if (! $this->isUserLevel(User::USER_LEVEL_ADMIN) && (! $item->user_editable || ! $item->user_viewable)) {
return false; return false;
} }
$validator = $this->validator->make([ $v = $this->validator->make([
'variable_value' => array_get($fields, $item->env_variable), 'variable_value' => array_get($fields, $item->env_variable),
], [ ], [
'variable_value' => $item->rules, 'variable_value' => $item->rules,
], [], [
'variable_value' => trans('validation.internal.variable_value', ['env' => $item->name]),
]); ]);
if ($validator->fails()) { if ($v->fails()) {
throw new DisplayValidationException(json_encode( foreach ($v->getMessageBag()->all() as $message) {
collect([ $messages->getMessageBag()->add($item->env_variable, $message);
'notice' => [ }
trans('admin/server.exceptions.bad_variable', ['name' => $item->name]),
],
])->merge($validator->errors()->toArray())
));
} }
return (object) [ return (object) [
@ -104,5 +104,11 @@ class VariableValidatorService
})->filter(function ($item) { })->filter(function ($item) {
return is_object($item); return is_object($item);
}); });
if (! empty($messages->getMessageBag()->all())) {
throw new ValidationException($messages);
}
return $response;
} }
} }

View file

@ -45,7 +45,7 @@ trait JavascriptInjection
$server = $request->attributes->get('server'); $server = $request->attributes->get('server');
$token = $request->attributes->get('server_token'); $token = $request->attributes->get('server_token');
$response = array_merge([ $response = array_merge_recursive([
'server' => [ 'server' => [
'uuid' => $server->uuid, 'uuid' => $server->uuid,
'uuidShort' => $server->uuidShort, 'uuidShort' => $server->uuidShort,

View file

@ -36,7 +36,7 @@
"name": "OxideMod", "name": "OxideMod",
"description": "Set whether you want the server to use and auto update OxideMod or not. Valid options are \"1\" for true and \"0\" for false.", "description": "Set whether you want the server to use and auto update OxideMod or not. Valid options are \"1\" for true and \"0\" for false.",
"env_variable": "OXIDE", "env_variable": "OXIDE",
"default_value": "false", "default_value": "0",
"user_viewable": 1, "user_viewable": 1,
"user_editable": 1, "user_editable": 1,
"rules": "required|boolean" "rules": "required|boolean"

View file

@ -16,7 +16,7 @@ return [
'default_allocation_not_found' => 'The requested default allocation was not found in this server\'s allocations.', 'default_allocation_not_found' => 'The requested default allocation was not found in this server\'s allocations.',
], ],
'alerts' => [ 'alerts' => [
'startup_changed' => 'The startup configuration for this server has been updated. If this server\'s service or option was changed a reinstall will be occuring now.', 'startup_changed' => 'The startup configuration for this server has been updated. If this server\'s nest or egg was changed a reinstall will be occuring now.',
'server_deleted' => 'Server has successfully been deleted from the system.', 'server_deleted' => 'Server has successfully been deleted from the system.',
'server_created' => 'Server was successfully created on the panel. Please allow the daemon a few minutes to completely install this server.', 'server_created' => 'Server was successfully created on the panel. Please allow the daemon a few minutes to completely install this server.',
'build_updated' => 'The build details for this server have been updated. Some changes may require a restart to take effect.', 'build_updated' => 'The build details for this server have been updated. Some changes may require a restart to take effect.',

View file

@ -82,6 +82,7 @@ return [
'timezone' => 'Application Timezone', 'timezone' => 'Application Timezone',
'cache_driver' => 'Cache Driver', 'cache_driver' => 'Cache Driver',
'session_driver' => 'Session Driver', 'session_driver' => 'Session Driver',
'queue_driver' => 'Queue Driver',
'using_redis' => 'You\'ve selected the Redis driver for one or more options, please provide valid connection information below. In most cases you can use the defaults provided unless you have modified your setup.', 'using_redis' => 'You\'ve selected the Redis driver for one or more options, please provide valid connection information below. In most cases you can use the defaults provided unless you have modified your setup.',
'redis_host' => 'Redis Host', 'redis_host' => 'Redis Host',
'redis_password' => 'Redis Password', 'redis_password' => 'Redis Password',

View file

@ -85,23 +85,6 @@ return [
'uploaded' => 'The :attribute failed to upload.', 'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.', 'url' => 'The :attribute format is invalid.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Attributes | Custom Validation Attributes
@ -114,4 +97,9 @@ return [
*/ */
'attributes' => [], 'attributes' => [],
// Internal validation logic for Pterodactyl
'internal' => [
'variable_value' => ':env variable',
],
]; ];

View file

@ -103,7 +103,7 @@
<div class="form-group"> <div class="form-group">
<label for="pNodeId" class="form-label">Linked Node</label> <label for="pNodeId" class="form-label">Linked Node</label>
<select name="node_id" id="pNodeId" class="form-control"> <select name="node_id" id="pNodeId" class="form-control">
<option value="null">None</option> <option value="">None</option>
@foreach($locations as $location) @foreach($locations as $location)
<optgroup label="{{ $location->short }}"> <optgroup label="{{ $location->short }}">
@foreach($location->nodes as $node) @foreach($location->nodes as $node)

View file

@ -148,7 +148,7 @@
text: item.name, text: item.name,
}; };
}), }),
}).change(); }).val(Pterodactyl.server.egg_id).change();
}); });
$('#pEggId').on('change', function (event) { $('#pEggId').on('change', function (event) {
@ -157,8 +157,8 @@
$('#setDefaultImage').html(_.get(objectChain, 'docker_image', 'undefined')); $('#setDefaultImage').html(_.get(objectChain, 'docker_image', 'undefined'));
$('#pDockerImage').val(_.get(objectChain, 'docker_image', 'undefined')); $('#pDockerImage').val(_.get(objectChain, 'docker_image', 'undefined'));
if (objectChain.id === parseInt('{{ $server->egg_id }}')) { if (objectChain.id === parseInt(Pterodactyl.server.egg_id)) {
$('#pDockerImage').val('{{ $server->image }}'); $('#pDockerImage').val(Pterodactyl.server.image);
} }
if (!_.get(objectChain, 'startup', false)) { if (!_.get(objectChain, 'startup', false)) {
@ -178,9 +178,9 @@
), ),
}); });
@if(! is_null($server->pack_id)) if (Pterodactyl.server.pack_id !== null) {
$('#pPackId').val({{ $server->pack_id }}); $('#pPackId').val(Pterodactyl.server.pack_id);
@endif }
$('#appendVariablesTo').html(''); $('#appendVariablesTo').html('');
$.each(_.get(objectChain, 'variables', []), function (i, item) { $.each(_.get(objectChain, 'variables', []), function (i, item) {

View file

@ -133,7 +133,7 @@
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
@if (count($errors) > 0) @if (count($errors) > 0)
<div class="callout callout-danger"> <div class="alert alert-danger">
@lang('base.validation_error')<br><br> @lang('base.validation_error')<br><br>
<ul> <ul>
@foreach ($errors->all() as $error) @foreach ($errors->all() as $error)
@ -144,7 +144,7 @@
@endif @endif
@foreach (Alert::getMessages() as $type => $messages) @foreach (Alert::getMessages() as $type => $messages)
@foreach ($messages as $message) @foreach ($messages as $message)
<div class="callout callout-{{ $type }} alert-dismissable" role="alert"> <div class="alert alert-{{ $type }} alert-dismissable" role="alert">
{!! $message !!} {!! $message !!}
</div> </div>
@endforeach @endforeach

View file

@ -215,7 +215,7 @@
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
@if (count($errors) > 0) @if (count($errors) > 0)
<div class="callout callout-danger"> <div class="alert alert-danger">
@lang('base.validation_error')<br><br> @lang('base.validation_error')<br><br>
<ul> <ul>
@foreach ($errors->all() as $error) @foreach ($errors->all() as $error)
@ -226,7 +226,7 @@
@endif @endif
@foreach (Alert::getMessages() as $type => $messages) @foreach (Alert::getMessages() as $type => $messages)
@foreach ($messages as $message) @foreach ($messages as $message)
<div class="callout callout-{{ $type }} alert-dismissable" role="alert"> <div class="alert alert-{{ $type }} alert-dismissable" role="alert">
{!! $message !!} {!! $message !!}
</div> </div>
@endforeach @endforeach

View file

@ -1,11 +1,4 @@
<?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 Tests\Unit\Services\Servers; namespace Tests\Unit\Services\Servers;
@ -15,8 +8,7 @@ use Pterodactyl\Models\User;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Pterodactyl\Models\EggVariable; use Pterodactyl\Models\EggVariable;
use Illuminate\Contracts\Validation\Factory; use Illuminate\Contracts\Validation\Factory;
use Pterodactyl\Exceptions\PterodactylException; use Illuminate\Validation\ValidationException;
use Pterodactyl\Exceptions\DisplayValidationException;
use Pterodactyl\Services\Servers\VariableValidatorService; use Pterodactyl\Services\Servers\VariableValidatorService;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
@ -27,22 +19,17 @@ class VariableValidatorServiceTest extends TestCase
/** /**
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface|\Mockery\Mock
*/ */
protected $optionVariableRepository; private $optionVariableRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface|\Mockery\Mock
*/ */
protected $serverRepository; private $serverRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface|\Mockery\Mock
*/ */
protected $serverVariableRepository; private $serverVariableRepository;
/**
* @var \Illuminate\Contracts\Validation\Factory|\Mockery\Mock
*/
protected $validator;
/** /**
* Setup tests. * Setup tests.
@ -54,7 +41,6 @@ class VariableValidatorServiceTest extends TestCase
$this->optionVariableRepository = m::mock(EggVariableRepositoryInterface::class); $this->optionVariableRepository = m::mock(EggVariableRepositoryInterface::class);
$this->serverRepository = m::mock(ServerRepositoryInterface::class); $this->serverRepository = m::mock(ServerRepositoryInterface::class);
$this->serverVariableRepository = m::mock(ServerVariableRepositoryInterface::class); $this->serverVariableRepository = m::mock(ServerVariableRepositoryInterface::class);
$this->validator = m::mock(Factory::class);
} }
/** /**
@ -77,13 +63,6 @@ class VariableValidatorServiceTest extends TestCase
$variables = $this->getVariableCollection(); $variables = $this->getVariableCollection();
$this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables); $this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables);
$this->validator->shouldReceive('make')->with([
'variable_value' => 'Test_SomeValue_0',
], [
'variable_value' => $variables[0]->rules,
])->once()->andReturnSelf();
$this->validator->shouldReceive('fails')->withNoArgs()->once()->andReturn(false);
$response = $this->getService()->handle(1, [ $response = $this->getService()->handle(1, [
$variables[0]->env_variable => 'Test_SomeValue_0', $variables[0]->env_variable => 'Test_SomeValue_0',
$variables[1]->env_variable => 'Test_SomeValue_1', $variables[1]->env_variable => 'Test_SomeValue_1',
@ -112,15 +91,6 @@ class VariableValidatorServiceTest extends TestCase
$variables = $this->getVariableCollection(); $variables = $this->getVariableCollection();
$this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables); $this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables);
foreach ($variables as $key => $variable) {
$this->validator->shouldReceive('make')->with([
'variable_value' => 'Test_SomeValue_' . $key,
], [
'variable_value' => $variables[$key]->rules,
])->once()->andReturnSelf();
$this->validator->shouldReceive('fails')->withNoArgs()->once()->andReturn(false);
}
$service = $this->getService(); $service = $this->getService();
$service->setUserLevel(User::USER_LEVEL_ADMIN); $service->setUserLevel(User::USER_LEVEL_ADMIN);
$response = $service->handle(1, [ $response = $service->handle(1, [
@ -152,28 +122,16 @@ class VariableValidatorServiceTest extends TestCase
$variables = $this->getVariableCollection(); $variables = $this->getVariableCollection();
$this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables); $this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables);
$this->validator->shouldReceive('make')->with([
'variable_value' => null,
], [
'variable_value' => $variables[0]->rules,
])->once()->andReturnSelf();
$this->validator->shouldReceive('fails')->withNoArgs()->once()->andReturn(true);
$this->validator->shouldReceive('errors')->withNoArgs()->once()->andReturnSelf();
$this->validator->shouldReceive('toArray')->withNoArgs()->once()->andReturn([]);
try { try {
$this->getService()->handle(1, [$variables[0]->env_variable => null]); $this->getService()->handle(1, [$variables[0]->env_variable => null]);
} catch (PterodactylException $exception) { } catch (ValidationException $exception) {
$this->assertInstanceOf(DisplayValidationException::class, $exception); $messages = $exception->validator->getMessageBag()->all();
$decoded = json_decode($exception->getMessage()); $this->assertNotEmpty($messages);
$this->assertEquals(0, json_last_error(), 'Assert that response is decodable JSON.'); $this->assertSame(1, count($messages));
$this->assertObjectHasAttribute('notice', $decoded); $this->assertSame(trans('validation.required', [
$this->assertEquals( 'attribute' => trans('validation.internal.variable_value', ['env' => $variables[0]->name]),
trans('admin/server.exceptions.bad_variable', ['name' => $variables[0]->name]), ]), $messages[0]);
$decoded->notice[0]
);
} }
} }
@ -205,7 +163,7 @@ class VariableValidatorServiceTest extends TestCase
$this->optionVariableRepository, $this->optionVariableRepository,
$this->serverRepository, $this->serverRepository,
$this->serverVariableRepository, $this->serverVariableRepository,
$this->validator $this->app->make(Factory::class)
); );
} }
} }