diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php index 51dd83ee6..41bef1f31 100644 --- a/app/Http/Controllers/Admin/DatabaseController.php +++ b/app/Http/Controllers/Admin/DatabaseController.php @@ -56,7 +56,7 @@ class DatabaseController extends Controller 'servers.id as a_serverId', 'servers.name as a_serverName' )->join('database_servers', 'database_servers.id', '=', 'databases.db_server') - ->join('servers', 'databases.server', '=', 'servers.id') + ->join('servers', 'databases.server_id', '=', 'servers.id') ->paginate(20), 'dbh' => Models\DatabaseServer::select( 'database_servers.*', diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 2ba1253ed..2d10c02a4 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -99,7 +99,7 @@ class ServersController extends Controller ->where('server_variables.server_id', $server->id) ->get(), 'databases' => Models\Database::select('databases.*', 'database_servers.host as a_host', 'database_servers.port as a_port') - ->where('server', $server->id) + ->where('server_id', $server->id) ->join('database_servers', 'database_servers.id', '=', 'databases.db_server') ->get(), 'db_servers' => Models\DatabaseServer::all() diff --git a/app/Http/Controllers/Server/AjaxController.php b/app/Http/Controllers/Server/AjaxController.php index cc64f9f85..d1bf443a6 100644 --- a/app/Http/Controllers/Server/AjaxController.php +++ b/app/Http/Controllers/Server/AjaxController.php @@ -25,6 +25,7 @@ namespace Pterodactyl\Http\Controllers\Server; use Log; use Debugbar; +use Pterodactyl\Models; use Pterodactyl\Models\Server; use Pterodactyl\Models\Node; @@ -223,4 +224,28 @@ class AjaxController extends Controller } } + public function postResetDatabasePassword(Request $request, $uuid) + { + $server = Models\Server::getByUUID($uuid); + $database = Models\Database::where('id', $request->input('database'))->where('server_id', $server->id)->firstOrFail(); + + $this->authorize('reset-db-password', $server); + try { + + $repo = new Repositories\DatabaseRepository; + $password = str_random(16); + $repo->modifyPassword($request->input('database'), $password); + return response($password); + } catch (\Pterodactyl\Exceptions\DisplayException $ex) { + return response()->json([ + 'error' => $ex->getMessage(), + ], 503); + } catch(\Exception $ex) { + Log::error($ex); + return response()->json([ + 'error' => 'An unhandled error occured while attempting to modify this database\'s password.' + ], 503); + } + } + } diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index 125f7daa1..c9f70c350 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -229,7 +229,7 @@ class ServerController extends Controller return view('server.settings', [ 'server' => $server, 'databases' => Models\Database::select('databases.*', 'database_servers.host as a_host', 'database_servers.port as a_port') - ->where('server', $server->id) + ->where('server_id', $server->id) ->join('database_servers', 'database_servers.id', '=', 'databases.db_server') ->get(), 'node' => Models\Node::find($server->node), diff --git a/app/Http/Routes/ServerRoutes.php b/app/Http/Routes/ServerRoutes.php index 9a0cd8ea3..d6093d37d 100644 --- a/app/Http/Routes/ServerRoutes.php +++ b/app/Http/Routes/ServerRoutes.php @@ -152,6 +152,11 @@ class ServerRoutes { $router->post('set-connection', [ 'uses' => 'Server\AjaxController@postSetConnection' ]); + + $router->post('settings/reset-database-password', [ + 'as' => 'server.ajax.reset-database-password', + 'uses' => 'Server\AjaxController@postResetDatabasePassword' + ]); }); // Assorted AJAX Routes diff --git a/app/Models/DatabaseServer.php b/app/Models/DatabaseServer.php index 2a58e3168..78b45d953 100644 --- a/app/Models/DatabaseServer.php +++ b/app/Models/DatabaseServer.php @@ -56,6 +56,8 @@ class DatabaseServer extends Model */ protected $casts = [ 'id' => 'integer', + 'server_id' => 'integer', + 'db_server' => 'integer' ]; } diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index 05e66bc02..9df744ea2 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -546,4 +546,13 @@ class ServerPolicy return $user->permissions()->server($server)->permission('create-task')->exists(); } + public function resetDbPassword(User $user, Server $server) + { + if ($this->isOwner($user, $server)) { + return true; + } + + return $user->permissions()->server($server)->permission('create-task')->exists(); + } + } diff --git a/app/Repositories/DatabaseRepository.php b/app/Repositories/DatabaseRepository.php index 085b56253..777b6f2b6 100644 --- a/app/Repositories/DatabaseRepository.php +++ b/app/Repositories/DatabaseRepository.php @@ -60,7 +60,7 @@ class DatabaseRepository { try { $db = new Models\Database; $db->fill([ - 'server' => $server->id, + 'server_id' => $server->id, 'db_server' => $options['db_server'], 'database' => $server->uuidShort . '_' . $options['database'], 'username' => $server->uuidShort . '_' . str_random(7), @@ -103,6 +103,54 @@ class DatabaseRepository { } } + /** + * Updates the password for a given database. + * @param int $database The ID of the database to modify. + * @param string $password The new password to use for the database. + * @return bool + */ + public function modifyPassword($database, $password) + { + $db = Models\Database::findOrFail($database); + $dbr = Models\DatabaseServer::findOrFail($db->db_server); + + DB::beginTransaction(); + try { + + $db->password = Crypt::encrypt($password); + $db->save(); + + $capsule = new Capsule; + $capsule->addConnection([ + 'driver' => 'mysql', + 'host' => $dbr->host, + 'port' => $dbr->port, + 'database' => 'mysql', + 'username' => $dbr->username, + 'password' => Crypt::decrypt($dbr->password), + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + 'options' => [ + \PDO::ATTR_TIMEOUT => 3, + ] + ]); + + $capsule->setAsGlobal(); + Capsule::statement(sprintf( + 'ALTER USER \'%s\'@\'%s\' IDENTIFIED BY \'%s\'', + $db->username, + $db->remote, + $password + )); + + DB::commit(); + } catch(\Exception $ex) { + DB::rollback(); + throw $ex; + } + } + /** * Drops a database from the associated MySQL Server * @param int $database The ID of the database to drop. diff --git a/database/migrations/2016_08_15_234600_fix_column_name_for_databases.php b/database/migrations/2016_08_15_234600_fix_column_name_for_databases.php new file mode 100644 index 000000000..e6cd70392 --- /dev/null +++ b/database/migrations/2016_08_15_234600_fix_column_name_for_databases.php @@ -0,0 +1,31 @@ +renameColumn('server', 'server_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('databases', function (Blueprint $table) { + $table->renameColumn('server_id', 'server'); + }); + } +} diff --git a/resources/views/admin/servers/view.blade.php b/resources/views/admin/servers/view.blade.php index 878714ff2..7498d899c 100644 --- a/resources/views/admin/servers/view.blade.php +++ b/resources/views/admin/servers/view.blade.php @@ -363,7 +363,7 @@ {{ $database->database }} {{ $database->username }} ({{ $database->remote }}) - {{ Crypt::decrypt($database->password) }} + {{ Crypt::decrypt($database->password) }} {{ $database->a_host }}:{{ $database->a_port }} @@ -530,6 +530,36 @@ $(document).ready(function () { }); }); }); + $('[data-action="reset-database-password"]').click(function (e) { + e.preventDefault(); + var block = $(this); + $(this).find('i').addClass('fa-spin'); + $.ajax({ + type: 'POST', + url: '{{ route('server.ajax.reset-database-password', $server->uuidShort) }}', + headers: { + 'X-CSRF-TOKEN': '{{ csrf_token() }}' + }, + data: { + 'database': $(this).data('id') + } + }).done(function (data) { + block.parent().find('code').html(data); + }).fail(function(jqXHR, textStatus, errorThrown) { + console.error(jqXHR); + var error = 'An error occured while trying to process this request.'; + if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { + error = jqXHR.responseJSON.error; + } + swal({ + type: 'error', + title: 'Whoops!', + text: error + }); + }).always(function () { + block.find('i').removeClass('fa-spin'); + }); + }); }); @endsection diff --git a/resources/views/server/settings.blade.php b/resources/views/server/settings.blade.php index 423a45405..620be43d8 100644 --- a/resources/views/server/settings.blade.php +++ b/resources/views/server/settings.blade.php @@ -142,7 +142,7 @@ Database - User (Connections From) + Username Password DB Server @@ -151,8 +151,8 @@ @foreach($databases as $database) {{ $database->database }} - {{ $database->username }} ({{ $database->remote }}) - {{ Crypt::decrypt($database->password) }} + {{ $database->username }} + {{ Crypt::decrypt($database->password) }} {{ $database->a_host }}:{{ $database->a_port }} @endforeach @@ -206,7 +206,36 @@ $(document).ready(function () { }); return false; }); - + $('[data-action="reset-database-password"]').click(function (e) { + e.preventDefault(); + var block = $(this); + $(this).find('i').addClass('fa-spin'); + $.ajax({ + type: 'POST', + url: '{{ route('server.ajax.reset-database-password', $server->uuidShort) }}', + headers: { + 'X-CSRF-TOKEN': '{{ csrf_token() }}' + }, + data: { + 'database': $(this).data('id') + } + }).done(function (data) { + block.parent().find('code').html(data); + }).fail(function(jqXHR, textStatus, errorThrown) { + console.error(jqXHR); + var error = 'An error occured while trying to process this request.'; + if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') { + error = jqXHR.responseJSON.error; + } + swal({ + type: 'error', + title: 'Whoops!', + text: error + }); + }).always(function () { + block.find('i').removeClass('fa-spin'); + }); + }); }); @endsection