Merge branch 'develop' into feature/service-changes

This commit is contained in:
Dane Everitt 2017-01-12 13:15:37 -05:00
commit 6bd9663f59
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
136 changed files with 2470 additions and 1737 deletions

View file

@ -75,7 +75,7 @@ class UserController extends BaseController
*/
public function view(Request $request, $id)
{
$query = Models\User::where('id', $id);
$query = Models\User::where((is_numeric($id) ? 'id' : 'email'), $id);
if (! is_null($request->input('fields'))) {
foreach (explode(',', $request->input('fields')) as $field) {

View file

@ -27,6 +27,7 @@ namespace Pterodactyl\Http\Controllers\Admin;
use DB;
use Log;
use Alert;
use Carbon;
use Validator;
use Pterodactyl\Models;
use Illuminate\Http\Request;
@ -82,6 +83,7 @@ class NodesController extends Controller
'_token',
]));
Alert::success('Successfully created new node. <strong>Before you can add any servers you need to first assign some IP addresses and ports.</strong>')->flash();
Alert::info('<strong>To simplify the node setup you can generate a token on the configuration tab.</strong>')->flash();
return redirect()->route('admin.nodes.view', [
'id' => $new,
@ -276,4 +278,24 @@ class NodesController extends Controller
'tab' => 'tab_delete',
]);
}
public function getConfigurationToken(Request $request, $id)
{
// Check if Node exists. Will lead to 404 if not.
Models\Node::findOrFail($id);
// Create a token
$token = new Models\NodeConfigurationToken();
$token->node = $id;
$token->token = str_random(32);
$token->expires_at = Carbon::now()->addMinutes(5); // Expire in 5 Minutes
$token->save();
$token_response = [
'token' => $token->token,
'expires_at' => $token->expires_at->toDateTimeString(),
];
return response()->json($token_response, 200);
}
}

View file

@ -232,7 +232,7 @@ class ServiceController extends Controller
]));
Alert::success('Successfully added new variable to this option.')->flash();
return redirect()->route('admin.services.option', [$service, $option])->withInput();
return redirect()->route('admin.services.option', [$service, $option]);
} catch (DisplayValidationException $ex) {
return redirect()->route('admin.services.option.variable.new', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput();
} catch (DisplayException $ex) {

View file

@ -33,16 +33,14 @@ use Pterodactyl\Http\Controllers\Controller;
class LanguageController extends Controller
{
protected $languages = [
'de' => 'Danish',
'de' => 'German',
'en' => 'English',
'es' => 'Spanish',
'fr' => 'French',
'it' => 'Italian',
'pl' => 'Polish',
'et' => 'Estonian',
'nb' => 'Norwegian',
'nl' => 'Dutch',
'pt' => 'Portuguese',
'ro' => 'Romanian',
'ru' => 'Russian',
'se' => 'Swedish',
'zh' => 'Chinese',
];
/**

View file

@ -24,6 +24,7 @@
namespace Pterodactyl\Http\Controllers\Remote;
use Carbon\Carbon;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use Pterodactyl\Http\Controllers\Controller;
@ -107,4 +108,29 @@ class RemoteController extends Controller
return response('', 201);
}
public function getConfiguration(Request $request, $tokenString)
{
// Try to query the token and the node from the database
try {
$token = Models\NodeConfigurationToken::where('token', $tokenString)->firstOrFail();
$node = Models\Node::findOrFail($token->node);
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
return response()->json(['error' => 'token_invalid'], 403);
}
// Check if token is expired
if ($token->expires_at->lt(Carbon::now())) {
$token->delete();
return response()->json(['error' => 'token_expired'], 403);
}
// Delete the token, it's one-time use
$token->delete();
// Manually as getConfigurationAsJson() returns it in correct format already
return response($node->getConfigurationAsJson(), 200)
->header('Content-Type', 'application/json');
}
}

View file

@ -28,9 +28,9 @@ use DB;
use Log;
use Uuid;
use Alert;
use Javascript;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use InvalidArgumentException;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\ServerRepository;
@ -49,24 +49,6 @@ class ServerController extends Controller
//
}
public function getJavascript(Request $request, $uuid, $folder, $file)
{
$server = Models\Server::getByUUID($uuid);
$info = pathinfo($file);
$routeFile = str_replace('/', '.', $info['dirname']) . '.' . $info['filename'];
try {
return response()->view('server.js.' . $folder . '.' . $routeFile, [
'server' => $server,
'node' => Models\Node::find($server->node),
])->header('Content-Type', 'application/javascript');
} catch (InvalidArgumentException $ex) {
return abort(404);
} catch (\Exception $ex) {
throw $ex;
}
}
/**
* Renders server index page for specified server.
*
@ -77,6 +59,13 @@ class ServerController extends Controller
{
$server = Models\Server::getByUUID($request->route()->server);
Javascript::put([
'meta' => [
'saveFile' => route('server.files.save', $server->uuidShort),
'csrfToken' => csrf_token(),
],
]);
return view('server.index', [
'server' => $server,
'allocations' => Models\Allocation::where('assigned_to', $server->id)->orderBy('ip', 'asc')->orderBy('port', 'asc')->get(),
@ -90,14 +79,34 @@ class ServerController extends Controller
* @param Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getFiles(Request $request)
public function getFiles(Request $request, $uuid)
{
$server = Models\Server::getByUUID($request->route()->server);
$server = Models\Server::getByUUID($uuid);
$this->authorize('list-files', $server);
$node = Models\Node::find($server->node);
Javascript::put([
'server' => collect($server->makeVisible('daemonSecret'))->only('uuid', 'uuidShort', 'daemonSecret'),
'node' => collect($node)->only('fqdn', 'scheme', 'daemonListen'),
'meta' => [
'directoryList' => route('server.files.directory-list', $server->uuidShort),
'csrftoken' => csrf_token(),
],
'permissions' => [
'moveFiles' => $request->user()->can('move-files', $server),
'copyFiles' => $request->user()->can('copy-files', $server),
'compressFiles' => $request->user()->can('compress-files', $server),
'decompressFiles' => $request->user()->can('decompress-files', $server),
'createFiles' => $request->user()->can('create-files', $server),
'downloadFiles' => $request->user()->can('download-files', $server),
'deleteFiles' => $request->user()->can('delete-files', $server),
],
]);
return view('server.files.index', [
'server' => $server,
'node' => Models\Node::find($server->node),
'node' => $node,
]);
}
@ -107,9 +116,9 @@ class ServerController extends Controller
* @param Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getAddFile(Request $request)
public function getAddFile(Request $request, $uuid)
{
$server = Models\Server::getByUUID($request->route()->server);
$server = Models\Server::getByUUID($uuid);
$this->authorize('add-files', $server);
return view('server.files.add', [

View file

@ -286,6 +286,11 @@ class AdminRoutes
'as' => 'admin.nodes.delete',
'uses' => 'Admin\NodesController@deleteNode',
]);
$router->get('/{id}/configurationtoken', [
'as' => 'admin.nodes.configuration-token',
'uses' => 'Admin\NodesController@getConfigurationToken',
]);
});
// Location Routes

View file

@ -46,6 +46,11 @@ class RemoteRoutes
'as' => 'remote.event',
'uses' => 'Remote\RemoteController@event',
]);
$router->get('configuration/{token}', [
'as' => 'remote.configuration',
'uses' => 'Remote\RemoteController@getConfiguration',
]);
});
}
}

View file

@ -166,15 +166,6 @@ class ServerRoutes
'uses' => 'Server\AjaxController@postResetDatabasePassword',
]);
});
// Assorted AJAX Routes
$router->group(['prefix' => 'js'], function ($server) use ($router) {
// Returns Server Status
$router->get('{folder}/{file}', [
'as' => 'server.js',
'uses' => 'Server\ServerController@getJavascript',
])->where('file', '.*');
});
});
}
}

View file

@ -117,4 +117,61 @@ class Node extends Model
return self::$guzzle[$node];
}
/**
* Returns the configuration in JSON format.
*
* @param bool $pretty Wether to pretty print the JSON or not
* @return string The configration in JSON format
*/
public function getConfigurationAsJson($pretty = false)
{
$config = [
'web' => [
'host' => '0.0.0.0',
'listen' => $this->daemonListen,
'ssl' => [
'enabled' => $this->scheme === 'https',
'certificate' => '/etc/letsencrypt/live/localhost/fullchain.pem',
'key' => '/etc/letsencrypt/live/localhost/privkey.pem',
],
],
'docker' => [
'socket' => '/var/run/docker.sock',
'autoupdate_images' => true,
],
'sftp' => [
'path' => $this->daemonBase,
'port' => $this->daemonSFTP,
'container' => 'ptdl-sftp',
],
'query' => [
'kill_on_fail' => true,
'fail_limit' => 5,
],
'logger' => [
'path' => 'logs/',
'src' => false,
'level' => 'info',
'period' => '1d',
'count' => 3,
],
'remote' => [
'base' => config('app.url'),
'download' => route('remote.download'),
'installed' => route('remote.install'),
],
'uploads' => [
'size_limit' => $this->upload_size,
],
'keys' => [$this->daemonSecret],
];
$json_options = JSON_UNESCAPED_SLASHES;
if ($pretty) {
$json_options |= JSON_PRETTY_PRINT;
}
return json_encode($config, $json_options);
}
}

View file

@ -0,0 +1,51 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Models;
use Illuminate\Database\Eloquent\Model;
class NodeConfigurationToken extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'node_configuration_tokens';
/**
* Fields that are not mass assignable.
*
* @var array
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['created_at', 'updated_at', 'expires_at'];
}

View file

@ -213,7 +213,13 @@ class NodeRepository
throw new DisplayException('The mapping for ' . $port . ' is invalid and cannot be processed.');
}
if (preg_match('/^(\d{1,5})-(\d{1,5})$/', $port, $matches)) {
foreach (range($matches[1], $matches[2]) as $assignPort) {
$portBlock = range($matches[1], $matches[2]);
if (count($portBlock) > 2000) {
throw new DisplayException('Adding more than 2000 ports at once is not currently supported. Please consider using a smaller port range.');
}
foreach ($portBlock as $assignPort) {
$alloc = Models\Allocation::firstOrNew([
'node' => $node->id,
'ip' => $ip,
@ -252,7 +258,6 @@ class NodeRepository
}
DB::commit();
// return true;
} catch (\Exception $ex) {
DB::rollBack();
throw $ex;
@ -277,6 +282,9 @@ class NodeRepository
// Delete Allocations
Models\Allocation::where('node', $node->id)->delete();
// Delete configure tokens
Models\NodeConfigurationToken::where('node', $node->id)->delete();
// Delete Node
$node->delete();

View file

@ -73,7 +73,7 @@ class ServerRepository
// Validate Fields
$validator = Validator::make($data, [
'owner' => 'bail|required',
'name' => 'required|regex:/^([\w -]{4,35})$/',
'name' => 'required|regex:/^([\w .-]{1,200})$/',
'memory' => 'required|numeric|min:0',
'swap' => 'required|numeric|min:-1',
'io' => 'required|numeric|min:10|max:1000',
@ -179,7 +179,7 @@ class ServerRepository
foreach ($variables as $variable) {
// Is the variable required?
if (! $data['env_' . $variable->env_variable]) {
if (! isset($data['env_' . $variable->env_variable])) {
if ($variable->required === 1) {
throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.');
}
@ -360,7 +360,7 @@ class ServerRepository
// Validate Fields
$validator = Validator::make($data, [
'owner' => 'email|exists:users,email',
'name' => 'regex:([\w -]{4,35})',
'name' => 'regex:([\w .-]{1,200})',
]);
// Run validator, throw catchable and displayable exception if it fails.

View file

@ -117,6 +117,7 @@ class SubuserRepository
public function create($sid, array $data)
{
$server = Models\Server::findOrFail($sid);
$validator = Validator::make($data, [
'permissions' => 'required|array',
'email' => 'required|email',
@ -140,6 +141,10 @@ class SubuserRepository
} catch (\Exception $ex) {
throw $ex;
}
} elseif ($server->owner === $user->id) {
throw new DisplayException('You cannot add the owner of a server as a subuser.');
} elseif (Models\Subuser::select('id')->where('user_id', $user->id)->where('server_id', $server->id)->first()) {
throw new DisplayException('A subuser with that email already exists for this server.');
}
$uuid = new UuidService;
@ -159,6 +164,7 @@ class SubuserRepository
if (! is_null($this->permissions[$permission])) {
array_push($daemonPermissions, $this->permissions[$permission]);
}
$model = new Models\Permission;
$model->fill([
'user_id' => $user->id,