diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index e2b617b63..e7f53fa7f 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -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. Before you can add any servers you need to first assign some IP addresses and ports.')->flash(); + Alert::info('To simplify the node setup you can generate a token on the configuration tab.')->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); + } } diff --git a/app/Http/Controllers/Remote/RemoteController.php b/app/Http/Controllers/Remote/RemoteController.php index e6dab6984..600e3d0d0 100644 --- a/app/Http/Controllers/Remote/RemoteController.php +++ b/app/Http/Controllers/Remote/RemoteController.php @@ -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'); + } } diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index 916517171..3b94b2aeb 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -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 diff --git a/app/Http/Routes/RemoteRoutes.php b/app/Http/Routes/RemoteRoutes.php index 2e2201c45..a42a611e9 100644 --- a/app/Http/Routes/RemoteRoutes.php +++ b/app/Http/Routes/RemoteRoutes.php @@ -46,6 +46,11 @@ class RemoteRoutes 'as' => 'remote.event', 'uses' => 'Remote\RemoteController@event', ]); + + $router->get('configuration/{token}', [ + 'as' => 'remote.configuration', + 'uses' => 'Remote\RemoteController@getConfiguration', + ]); }); } } diff --git a/app/Models/Node.php b/app/Models/Node.php index 396f2849d..7c113b2f0 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -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); + } } diff --git a/app/Models/NodeConfigurationToken.php b/app/Models/NodeConfigurationToken.php new file mode 100644 index 000000000..d7a309adc --- /dev/null +++ b/app/Models/NodeConfigurationToken.php @@ -0,0 +1,51 @@ +. + * + * 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']; +} diff --git a/app/Repositories/NodeRepository.php b/app/Repositories/NodeRepository.php index 73a4588e5..021626e97 100644 --- a/app/Repositories/NodeRepository.php +++ b/app/Repositories/NodeRepository.php @@ -282,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(); diff --git a/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php b/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php new file mode 100644 index 000000000..905d28a46 --- /dev/null +++ b/database/migrations/2017_01_07_154228_create_node_configuration_tokens_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->char('token', 32); + $table->timestamp('expires_at'); + $table->integer('node')->unsigned(); + $table->foreign('node')->references('id')->on('nodes'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('node_configuration_tokens'); + } +} diff --git a/resources/views/admin/nodes/view.blade.php b/resources/views/admin/nodes/view.blade.php index 3d5d9c7d7..c6befed5b 100644 --- a/resources/views/admin/nodes/view.blade.php +++ b/resources/views/admin/nodes/view.blade.php @@ -287,48 +287,13 @@ Below is the configuration file for your daemon on this node. We recommend not simply copy and pasting the code below unless you know what you are doing. You should run the auto-installer or auto-updater to setup the daemon.
-
{
-    "web": {
-        "host": "0.0.0.0",
-        "listen": {{ $node->daemonListen }},
-        "ssl": {
-            "enabled": {{ $node->scheme === 'https' ? 'true' : 'false' }},
-            "certificate": "/etc/letsencrypt/live/{{ $node->fqdn }}/fullchain.pem",
-            "key": "/etc/letsencrypt/live/{{ $node->fqdn }}/privkey.pem"
-        }
-    },
-    "docker": {
-        "socket": "/var/run/docker.sock",
-        "autoupdate_images": true
-    },
-    "sftp": {
-        "path": "{{ $node->daemonBase }}",
-        "port": {{ $node->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": {{ $node->upload_size }}
-    },
-    "keys": [
-        "{{ $node->daemonSecret }}"
-    ]
-}
+

To simplify the configuration of nodes it is possible to fetch the config from the panel. A token is required for this process. The button below will generate a token and provide you with the commands necessary for automatic configuration of the node. Be aware that these tokens are only valid for 5 minutes.

+

+ +

+
+
+
{{ $node->getConfigurationAsJson(true) }}
@@ -536,6 +501,27 @@ $(document).ready(function () { }); }); + $('#configTokenBtn').on('click', function (event) { + $.getJSON('{{ route('admin.nodes.configuration-token', $node->id) }}') + .done(function (data) { + swal({ + type: 'success', + title: 'Token created.', + text: 'Here is your token: '+data.token+'
' + + 'It will expire at ' + data.expires_at + '

' + + '

To auto-configure your node run
npm run configure -- --panel-url '+window.location.protocol+'//{{ config('app.url') }} --token '+data.token+'

', + html: true + }) + }) + .fail(function () { + swal({ + title: 'Error', + text: 'Something went wrong creating your token.', + type: 'error' + }); + }) + }) + $('.cloneElement').on('click', function (event) { event.preventDefault(); var rnd = randomKey(10);