2016-02-08 23:03:02 +00:00
< ? php
/**
* Pterodactyl - Panel
2016-12-07 22:46:38 +00:00
* Copyright ( c ) 2015 - 2016 Dane Everitt < dane @ daneeveritt . com >.
2016-02-08 23:03:02 +00:00
*
* 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 .
*/
2016-12-07 22:46:38 +00:00
2016-02-08 23:03:02 +00:00
namespace Pterodactyl\Repositories ;
use DB ;
2016-12-07 22:46:38 +00:00
use Crypt ;
2016-02-08 23:03:02 +00:00
use Validator ;
use Pterodactyl\Models ;
use Pterodactyl\Exceptions\DisplayException ;
use Illuminate\Database\Capsule\Manager as Capsule ;
2016-12-07 22:46:38 +00:00
use Pterodactyl\Exceptions\DisplayValidationException ;
2016-02-08 23:03:02 +00:00
2016-12-07 22:46:38 +00:00
class DatabaseRepository
{
2016-02-08 23:03:02 +00:00
/**
* Adds a new database to a given database server .
* @ param int $server Id of the server to add a database for .
* @ param array $options Array of options for creating that database .
* @ return void
*/
public function create ( $server , $options )
{
$server = Models\Server :: findOrFail ( $server );
$validator = Validator :: make ( $options , [
'db_server' => 'required|exists:database_servers,id' ,
'database' => 'required|regex:/^\w{1,100}$/' ,
'remote' => 'required|regex:/^[0-9%.]{1,15}$/' ,
]);
if ( $validator -> fails ()) {
throw new DisplayValidationException ( $validator -> errors ());
}
DB :: beginTransaction ();
try {
2016-02-15 02:43:20 +00:00
$db = new Models\Database ;
$db -> fill ([
2016-08-16 04:07:10 +00:00
'server_id' => $server -> id ,
2016-02-15 02:43:20 +00:00
'db_server' => $options [ 'db_server' ],
2016-11-26 22:34:14 +00:00
'database' => " s { $server -> id } _ { $options [ 'database' ] } " ,
2016-02-15 02:43:20 +00:00
'username' => $server -> uuidShort . '_' . str_random ( 7 ),
'remote' => $options [ 'remote' ],
2016-12-07 22:46:38 +00:00
'password' => Crypt :: encrypt ( str_random ( 20 )),
2016-02-15 02:43:20 +00:00
]);
$db -> save ();
// Contact Remote
$dbr = Models\DatabaseServer :: findOrFail ( $options [ 'db_server' ]);
2016-02-08 23:03:02 +00:00
$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' ,
2016-02-15 02:43:20 +00:00
'prefix' => '' ,
'options' => [
\PDO :: ATTR_TIMEOUT => 3 ,
2016-12-07 22:46:38 +00:00
],
2016-02-08 23:03:02 +00:00
]);
$capsule -> setAsGlobal ();
2016-11-26 22:34:14 +00:00
} catch ( \Exception $ex ) {
DB :: rollBack ();
throw new DisplayException ( 'There was an error while connecting to the Database Host Server. Please check the error logs.' , $ex );
}
2016-02-08 23:03:02 +00:00
2016-11-26 22:34:14 +00:00
try {
Capsule :: statement ( 'CREATE DATABASE `' . $db -> database . '`' );
Capsule :: statement ( 'CREATE USER `' . $db -> username . '`@`' . $db -> remote . '` IDENTIFIED BY \'' . Crypt :: decrypt ( $db -> password ) . '\'' );
Capsule :: statement ( 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON `' . $db -> database . '`.* TO `' . $db -> username . '`@`' . $db -> remote . '`' );
Capsule :: statement ( 'FLUSH PRIVILEGES' );
2016-02-08 23:03:02 +00:00
DB :: commit ();
} catch ( \Exception $ex ) {
2016-11-26 22:34:14 +00:00
try {
Capsule :: statement ( 'DROP DATABASE `' . $db -> database . '`' );
Capsule :: statement ( 'DROP USER `' . $db -> username . '`@`' . $db -> remote . '`' );
} catch ( \Exception $exi ) {
// ignore it, if it fails its probably
// because we failed to ever make the DB
// or the user on the system.
} finally {
DB :: rollBack ();
throw $ex ;
}
2016-02-08 23:03:02 +00:00
}
}
2016-08-16 04:07:10 +00:00
/**
* 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 ,
2016-12-07 22:46:38 +00:00
],
2016-08-16 04:07:10 +00:00
]);
$capsule -> setAsGlobal ();
Capsule :: statement ( sprintf (
2016-11-26 22:34:14 +00:00
'SET PASSWORD FOR `%s`@`%s` = PASSWORD(\'%s\')' ,
2016-08-16 04:07:10 +00:00
$db -> username ,
$db -> remote ,
$password
));
DB :: commit ();
2016-12-07 22:46:38 +00:00
} catch ( \Exception $ex ) {
2016-08-16 04:07:10 +00:00
DB :: rollback ();
throw $ex ;
}
}
2016-02-08 23:03:02 +00:00
/**
2016-12-07 22:46:38 +00:00
* Drops a database from the associated MySQL Server .
2016-02-08 23:03:02 +00:00
* @ param int $database The ID of the database to drop .
2016-12-07 22:46:38 +00:00
* @ return bool
2016-02-08 23:03:02 +00:00
*/
public function drop ( $database )
{
$db = Models\Database :: findOrFail ( $database );
$dbr = Models\DatabaseServer :: findOrFail ( $db -> db_server );
2016-02-15 02:43:20 +00:00
DB :: beginTransaction ();
2016-02-08 23:03:02 +00:00
2016-02-15 02:43:20 +00:00
try {
2016-02-08 23:03:02 +00:00
$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' ,
2016-02-15 02:43:20 +00:00
'prefix' => '' ,
'options' => [
\PDO :: ATTR_TIMEOUT => 3 ,
2016-12-07 22:46:38 +00:00
],
2016-02-08 23:03:02 +00:00
]);
$capsule -> setAsGlobal ();
2016-11-26 22:34:14 +00:00
Capsule :: statement ( 'DROP USER `' . $db -> username . '`@`' . $db -> remote . '`' );
Capsule :: statement ( 'DROP DATABASE `' . $db -> database . '`' );
2016-02-08 23:03:02 +00:00
$db -> delete ();
DB :: commit ();
2016-12-07 22:46:38 +00:00
2016-02-08 23:03:02 +00:00
return true ;
} catch ( \Exception $ex ) {
DB :: rollback ();
throw $ex ;
}
}
2016-02-15 02:43:20 +00:00
/**
* Deletes a database server from the system if it is empty .
* @ param int $server The ID of the Database Server .
* @ return
*/
public function delete ( $server )
{
$dbh = Models\DatabaseServer :: findOrFail ( $server );
$databases = Models\Database :: where ( 'db_server' , $dbh -> id ) -> count ();
if ( $databases > 0 ) {
throw new DisplayException ( 'You cannot delete a database server that has active databases attached to it.' );
}
return $dbh -> delete ();
}
/**
* Adds a new Database Server to the system .
* @ param array $data
*/
public function add ( array $data )
{
2016-11-27 01:27:36 +00:00
if ( isset ( $data [ 'host' ])) {
$data [ 'host' ] = gethostbyname ( $data [ 'host' ]);
}
2016-02-15 02:43:20 +00:00
$validator = Validator :: make ( $data , [
'name' => 'required|string|max:255' ,
'host' => 'required|ip|unique:database_servers,host' ,
'port' => 'required|numeric|between:1,65535' ,
'username' => 'required|string|max:32' ,
'password' => 'required|string' ,
'linked_node' => 'sometimes' ,
]);
if ( $validator -> fails ()) {
throw new DisplayValidationException ( $validator -> errors ());
}
DB :: beginTransaction ();
try {
$capsule = new Capsule ;
$capsule -> addConnection ([
'driver' => 'mysql' ,
'host' => $data [ 'host' ],
'port' => $data [ 'port' ],
'database' => 'mysql' ,
'username' => $data [ 'username' ],
'password' => $data [ 'password' ],
'charset' => 'utf8' ,
'collation' => 'utf8_unicode_ci' ,
'prefix' => '' ,
'options' => [
\PDO :: ATTR_TIMEOUT => 3 ,
2016-12-07 22:46:38 +00:00
],
2016-02-15 02:43:20 +00:00
]);
$capsule -> setAsGlobal ();
// Allows us to check that we can connect to things.
Capsule :: select ( 'SELECT 1 FROM dual' );
$dbh = new Models\DatabaseServer ;
$dbh -> fill ([
'name' => $data [ 'name' ],
'host' => $data [ 'host' ],
'port' => $data [ 'port' ],
'username' => $data [ 'username' ],
'password' => Crypt :: encrypt ( $data [ 'password' ]),
2016-12-07 22:46:38 +00:00
'max_databases' => null ,
'linked_node' => ( ! empty ( $data [ 'linked_node' ]) && $data [ 'linked_node' ] > 0 ) ? $data [ 'linked_node' ] : null ,
2016-02-15 02:43:20 +00:00
]);
$dbh -> save ();
DB :: commit ();
} catch ( \Exception $ex ) {
DB :: rollBack ();
throw $ex ;
}
}
2016-02-08 23:03:02 +00:00
}