Repository interface improvements

This commit is contained in:
Dane Everitt 2017-07-15 11:52:34 -05:00
parent 1f4f6024cc
commit bc3366b10d
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
23 changed files with 829 additions and 179 deletions

View file

@ -24,7 +24,7 @@
namespace Pterodactyl\Contracts\Repository;
interface DatabaseHostInterface extends RepositoryInterface
interface DatabaseHostRepositoryInterface extends RepositoryInterface
{
/**
* Delete a database host from the DB if there are no databases using it.

View file

@ -0,0 +1,98 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Contracts\Repository;
interface DatabaseRepositoryInterface extends RepositoryInterface
{
/**
* Create a new database if it does not already exist on the host with
* the provided details.
*
* @param array $data
* @return mixed
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function createIfNotExists(array $data);
/**
* Create a new database on a given connection.
*
* @param string $database
* @param null|string $connection
* @return bool
*/
public function createDatabase($database, $connection = null);
/**
* Create a new database user on a given connection.
*
* @param string $username
* @param string $remote
* @param string $password
* @param null|string $connection
* @return bool
*/
public function createUser($username, $remote, $password, $connection = null);
/**
* Give a specific user access to a given database.
*
* @param string $database
* @param string $username
* @param string $remote
* @param null|string $connection
* @return bool
*/
public function assignUserToDatabase($database, $username, $remote, $connection = null);
/**
* Flush the privileges for a given connection.
*
* @param null|string $connection
* @return mixed
*/
public function flush($connection = null);
/**
* Drop a given database on a specific connection.
*
* @param string $database
* @param null|string $connection
* @return bool
*/
public function dropDatabase($database, $connection = null);
/**
* Drop a given user on a specific connection.
*
* @param string $username
* @param string $remote
* @param null|string $connection
* @return mixed
*/
public function dropUser($username, $remote, $connection = null);
}

View file

@ -138,4 +138,11 @@ interface RepositoryInterface
* @return mixed
*/
public function massUpdate(array $where, array $fields);
/**
* Return all records from the model.
*
* @return mixed
*/
public function all();
}

View file

@ -0,0 +1,38 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Contracts\Repository;
use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface;
interface ServerRepositoryInterface extends RepositoryInterface, SearchableInterface
{
/**
* Returns a listing of all servers that exist including relationships.
*
* @param int $paginate
* @return mixed
*/
public function getAllServers($paginate);
}

View file

@ -0,0 +1,36 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Contracts\Repository;
interface ServiceRepositoryInterface extends RepositoryInterface
{
/**
* Return a service or all services with their associated options, variables, and packs.
*
* @param int $id
* @return \Illuminate\Support\Collection
*/
public function getWithOptions($id = null);
}

View file

@ -24,7 +24,7 @@
namespace Pterodactyl\Extensions;
use Pterodactyl\Contracts\Repository\DatabaseHostInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
use Pterodactyl\Models\DatabaseHost;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Config\Repository as ConfigRepository;
@ -46,20 +46,20 @@ class DynamicDatabaseConnection
protected $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostInterface
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
*/
protected $repository;
/**
* DynamicDatabaseConnection constructor.
*
* @param \Illuminate\Config\Repository $config
* @param \Pterodactyl\Contracts\Repository\DatabaseHostInterface $repository
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @param \Illuminate\Config\Repository $config
* @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
*/
public function __construct(
ConfigRepository $config,
DatabaseHostInterface $repository,
DatabaseHostRepositoryInterface $repository,
Encrypter $encrypter
) {
$this->config = $config;

View file

@ -28,7 +28,7 @@ use Pterodactyl\Models\Location;
use Pterodactyl\Models\DatabaseHost;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Administrative\DatabaseHostService;
use Pterodactyl\Services\Database\DatabaseHostService;
use Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest;
class DatabaseController extends Controller
@ -49,7 +49,7 @@ class DatabaseController extends Controller
protected $locationModel;
/**
* @var \Pterodactyl\Services\Administrative\DatabaseHostService
* @var \Pterodactyl\Services\Database\DatabaseHostService
*/
protected $service;
@ -59,7 +59,7 @@ class DatabaseController extends Controller
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Models\DatabaseHost $hostModel
* @param \Pterodactyl\Models\Location $locationModel
* @param \Pterodactyl\Services\Administrative\DatabaseHostService $service
* @param \Pterodactyl\Services\Database\DatabaseHostService $service
*/
public function __construct(
AlertsMessageBag $alert,

View file

@ -24,9 +24,14 @@
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Log;
use Alert;
use Javascript;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use GuzzleHttp\Exception\TransferException;
@ -39,34 +44,70 @@ use Pterodactyl\Exceptions\DisplayValidationException;
class ServersController extends Controller
{
/**
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
protected $databaseRepository;
/**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
*/
protected $locationRepository;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Contracts\Repository\ServiceRepositoryInterface
*/
protected $serviceRepository;
public function __construct(
ConfigRepository $config,
DatabaseRepositoryInterface $databaseRepository,
LocationRepositoryInterface $locationRepository,
ServerRepositoryInterface $repository,
ServiceRepositoryInterface $serviceRepository
) {
$this->config = $config;
$this->databaseRepository = $databaseRepository;
$this->locationRepository = $locationRepository;
$this->repository = $repository;
$this->serviceRepository = $serviceRepository;
}
/**
* Display the index page with all servers currently on the system.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
public function index()
{
$servers = Models\Server::with('node', 'user', 'allocation');
if (! is_null($request->input('query'))) {
$servers->search($request->input('query'));
}
return view('admin.servers.index', [
'servers' => $servers->paginate(25),
'servers' => $this->repository->getAllServers(
$this->config->get('pterodactyl.paginate.admin.servers')
),
]);
}
/**
* Display create new server page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Exception
*/
public function create(Request $request)
public function create()
{
$services = Models\Service::with('options.packs', 'options.variables')->get();
$services = $this->serviceRepository->getWithOptions();
Javascript::put([
'services' => $services->map(function ($item) {
return array_merge($item->toArray(), [
@ -76,7 +117,7 @@ class ServersController extends Controller
]);
return view('admin.servers.new', [
'locations' => Models\Location::all(),
'locations' => $this->locationRepository->all(),
'services' => $services,
]);
}
@ -115,7 +156,7 @@ class ServersController extends Controller
* Returns a tree of all avaliable nodes in a given location.
*
* @param \Illuminate\Http\Request $request
* @return array
* @return \Illuminate\Support\Collection
*/
public function nodes(Request $request)
{

View file

@ -25,9 +25,13 @@
namespace Pterodactyl\Models;
use Illuminate\Database\Eloquent\Model;
use Sofa\Eloquence\Eloquence;
use Sofa\Eloquence\Validable;
class Database extends Model
{
use Eloquence, Validable;
/**
* The table associated with the model.
*
@ -61,6 +65,22 @@ class Database extends Model
'database_host_id' => 'integer',
];
protected static $applicationRules = [
'server_id' => 'required',
'database_host_id' => 'required',
'database' => 'required',
'remote' => 'required',
];
protected static $dataIntegrityRules = [
'server_id' => 'numeric|exists:servers,id',
'database_host_id' => 'exists:database_hosts,id',
'database' => 'string|alpha_dash|between:3,100',
'username' => 'string|alpha_dash|between:3,100',
'remote' => 'string|regex:/^[0-9%.]{1,15}$/',
'password' => 'string',
];
/**
* Gets the host database server associated with a database.
*

View file

@ -24,12 +24,13 @@
namespace Pterodactyl\Models;
use Watson\Validating\ValidatingTrait;
use Sofa\Eloquence\Eloquence;
use Sofa\Eloquence\Validable;
use Illuminate\Database\Eloquent\Model;
class DatabaseHost extends Model
{
use ValidatingTrait;
use Eloquence, Validable;
/**
* The table associated with the model.
@ -65,18 +66,31 @@ class DatabaseHost extends Model
'node_id' => 'integer',
];
/**
* Application validation rules.
*
* @var array
*/
protected static $applicationRules = [
'name' => 'required',
'host' => 'required',
'port' => 'required',
'username' => 'required',
'node_id' => 'sometimes|required',
];
/**
* Validation rules to assign to this model.
*
* @var array
*/
protected $rules = [
'name' => 'required|string|max:255',
'host' => 'required|ip|unique:database_hosts,host',
'port' => 'required|numeric|between:1,65535',
'username' => 'required|string|max:32',
'password' => 'sometimes|nullable|string',
'node_id' => 'sometimes|required|nullable|exists:nodes,id',
protected static $dataIntegrityRules = [
'name' => 'string|max:255',
'host' => 'ip|unique:database_hosts,host',
'port' => 'numeric|between:1,65535',
'username' => 'string|max:32',
'password' => 'nullable|string',
'node_id' => 'nullable|exists:nodes,id',
];
/**

View file

@ -27,12 +27,18 @@ namespace Pterodactyl\Providers;
use Illuminate\Support\ServiceProvider;
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
use Pterodactyl\Contracts\Repository\ApiPermissionRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
use Pterodactyl\Repositories\Eloquent\ApiPermissionRepository;
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Repositories\Eloquent\ServiceRepository;
use Pterodactyl\Repositories\Eloquent\UserRepository;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
@ -45,8 +51,11 @@ class RepositoryServiceProvider extends ServiceProvider
{
$this->app->bind(ApiKeyRepositoryInterface::class, ApiKeyRepository::class);
$this->app->bind(ApiPermissionRepositoryInterface::class, ApiPermissionRepository::class);
$this->app->bind(DatabaseHostInterface::class, DatabaseHostRepository::class);
$this->app->bind(DatabaseRepositoryInterface::class, DatabaseRepository::class);
$this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class);
$this->app->bind(LocationRepositoryInterface::class, LocationRepository::class);
$this->app->bind(ServerRepositoryInterface::class, ServerRepository::class);
$this->app->bind(ServiceRepositoryInterface::class, ServiceRepository::class);
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}
}

View file

@ -0,0 +1,51 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Repositories\Eloquent\Attributes;
use Pterodactyl\Repositories\Eloquent\EloquentRepository;
use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface;
abstract class SearchableRepository extends EloquentRepository implements SearchableInterface
{
/**
* @var bool|string
*/
protected $searchTerm = false;
/**
* {@inheritdoc}
*/
public function search($term)
{
if (empty($term)) {
return $this;
}
$clone = clone $this;
$clone->searchTerm = $term;
return $clone;
}
}

View file

@ -24,12 +24,12 @@
namespace Pterodactyl\Repositories\Eloquent;
use Pterodactyl\Contracts\Repository\DatabaseHostInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Models\DatabaseHost;
class DatabaseHostRepository extends EloquentRepository implements DatabaseHostInterface
class DatabaseHostRepository extends EloquentRepository implements DatabaseHostRepositoryInterface
{
/**
* {@inheritdoc}

View file

@ -0,0 +1,155 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Repositories\Eloquent;
use Pterodactyl\Models\Database;
use Illuminate\Foundation\Application;
use Illuminate\Database\ConnectionResolver;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
class DatabaseRepository extends EloquentRepository implements DatabaseRepositoryInterface
{
/**
* @var \Illuminate\Database\ConnectionResolverInterface
*/
protected $connection;
/**
* DatabaseRepository constructor.
*
* @param \Illuminate\Foundation\Application $application
* @param \Illuminate\Database\ConnectionResolver $connection
*/
public function __construct(
Application $application,
ConnectionResolver $connection
) {
parent::__construct($application);
$this->connection = $connection;
}
/**
* {@inheritdoc}
*/
public function model()
{
return Database::class;
}
/**
* {@inheritdoc}
* @return bool|\Illuminate\Database\Eloquent\Model
*/
public function createIfNotExists(array $data)
{
$instance = $this->getBuilder()->where([
['server_id', $data['server_id']],
['database_host_id', $data['database_host_id']],
['database', $data['database']],
])->count();
if ($instance > 0) {
throw new DisplayException('A database with those details already exists for the specified server.');
}
return $this->create($data);
}
/**
* {@inheritdoc}
*/
public function createDatabase($database, $connection = null)
{
return $this->runStatement(
sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database), $connection
);
}
/**
* {@inheritdoc}
*/
public function createUser($username, $remote, $password, $connection = null)
{
return $this->runStatement(
sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', $username, $remote, $password), $connection
);
}
/**
* {@inheritdoc}
*/
public function assignUserToDatabase($database, $username, $remote, $connection = null)
{
return $this->runStatement(
sprintf(
'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON `%s`.* TO `%s`@`%s`',
$database, $username, $remote
),
$connection
);
}
/**
* {@inheritdoc}
*/
public function flush($connection = null)
{
return $this->runStatement('FLUSH PRIVILEGES', $connection);
}
/**
* {@inheritdoc}
*/
public function dropDatabase($database, $connection = null)
{
return $this->runStatement(
sprintf('DROP DATABASE IF EXISTS `%s`', $database), $connection
);
}
/**
* {@inheritdoc}
*/
public function dropUser($username, $remote, $connection = null)
{
return $this->runStatement(
sprintf('DROP USER IF EXISTS `%s`@`%s`', $username, $remote), $connection
);
}
/**
* Run the provided statement against the database on a given connection.
*
* @param string $statement
* @param null|string $connection
* @return bool
*/
protected function runStatement($statement, $connection = null)
{
return $this->connection->connection($connection)->statement($statement);
}
}

View file

@ -152,4 +152,12 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf
{
// TODO: Implement massUpdate() method.
}
/**
* {@inheritdoc}
*/
public function all()
{
return $this->getBuilder()->get($this->getColumns());
}
}

View file

@ -0,0 +1,54 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Repositories\Eloquent;
use Pterodactyl\Models\Server;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Repositories\Eloquent\Attributes\SearchableRepository;
class ServerRepository extends SearchableRepository implements ServerRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function model()
{
return Server::class;
}
/**
* {@inheritdoc}
*/
public function getAllServers($paginate = 25)
{
$instance = $this->getBuilder()->with('node', 'user', 'allocation');
if ($this->searchTerm) {
$instance->search($this->searchTerm);
}
return $instance->paginate($paginate);
}
}

View file

@ -0,0 +1,60 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Repositories\Eloquent;
use Pterodactyl\Contracts\Repository\ServiceRepositoryInterface;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Models\Service;
class ServiceRepository extends EloquentRepository implements ServiceRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function model()
{
return Service::class;
}
/**
* {@inheritdoc}
*/
public function getWithOptions($id = null)
{
$instance = $this->getBuilder()->with('options.packs', 'options.variables');
if (! is_null($id)) {
$instance = $instance->find($id, $this->getColumns());
if (! $instance) {
throw new RecordNotFoundException();
}
return $instance;
}
return $instance->get($this->getColumns());
}
}

View file

@ -30,19 +30,15 @@ use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Pterodactyl\Models\User;
use Pterodactyl\Repositories\Eloquent\Attributes\SearchableRepository;
class UserRepository extends EloquentRepository implements UserRepositoryInterface
class UserRepository extends SearchableRepository implements UserRepositoryInterface
{
/**
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* @var bool|array
*/
protected $searchTerm = false;
/**
* UserRepository constructor.
*
@ -64,21 +60,6 @@ class UserRepository extends EloquentRepository implements UserRepositoryInterfa
return User::class;
}
/**
* {@inheritdoc}
*/
public function search($term)
{
if (empty($term)) {
return $this;
}
$clone = clone $this;
$clone->searchTerm = $term;
return $clone;
}
/**
* {@inheritdoc}
*/

View file

@ -170,115 +170,4 @@ class DatabaseRepository
$database->delete();
});
}
/**
* Deletes a database host from the system if it has no associated databases.
*
* @param int $id
* @return void
*
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function delete($id)
{
$host = DatabaseHost::withCount('databases')->findOrFail($id);
if ($host->databases_count > 0) {
throw new DisplayException('You cannot delete a database host that has active databases attached to it.');
}
$host->delete();
}
/**
* Adds a new Database Host to the system.
*
* @param array $data
* @return \Pterodactyl\Models\DatabaseHost
*
* @throws \Pterodactyl\Exceptions\DisplayValidationException
*/
public function add(array $data)
{
if (isset($data['host'])) {
$data['host'] = gethostbyname($data['host']);
}
$validator = Validator::make($data, [
'name' => 'required|string|max:255',
'host' => 'required|ip|unique:database_hosts,host',
'port' => 'required|numeric|between:1,65535',
'username' => 'required|string|max:32',
'password' => 'required|string',
'node_id' => 'sometimes|required|exists:nodes,id',
]);
if ($validator->fails()) {
throw new DisplayValidationException(json_encode($validator->errors()));
}
return DB::transaction(function () use ($data) {
$host = new DatabaseHost;
$host->password = Crypt::encrypt($data['password']);
$host->fill([
'name' => $data['name'],
'host' => $data['host'],
'port' => $data['port'],
'username' => $data['username'],
'max_databases' => null,
'node_id' => (isset($data['node_id'])) ? $data['node_id'] : null,
])->save();
// Allows us to check that we can connect to things.
$host->setDynamicConnection();
DB::connection('dynamic')->select('SELECT 1 FROM dual');
return $host;
});
}
/**
* Updates a Database Host on the system.
*
* @param int $id
* @param array $data
* @return \Pterodactyl\Models\DatabaseHost
*
* @throws \Pterodactyl\Exceptions\DisplayValidationException
*/
public function update($id, array $data)
{
$host = DatabaseHost::findOrFail($id);
if (isset($data['host'])) {
$data['host'] = gethostbyname($data['host']);
}
$validator = Validator::make($data, [
'name' => 'sometimes|required|string|max:255',
'host' => 'sometimes|required|ip|unique:database_hosts,host,' . $host->id,
'port' => 'sometimes|required|numeric|between:1,65535',
'username' => 'sometimes|required|string|max:32',
'password' => 'sometimes|required|string',
'node_id' => 'sometimes|required|exists:nodes,id',
]);
if ($validator->fails()) {
throw new DisplayValidationException(json_encode($validator->errors()));
}
return DB::transaction(function () use ($data, $host) {
if (isset($data['password'])) {
$host->password = Crypt::encrypt($data['password']);
}
$host->fill($data)->save();
// Check that we can still connect with these details.
$host->setDynamicConnection();
DB::connection('dynamic')->select('SELECT 1 FROM dual');
return $host;
});
}
}

View file

@ -0,0 +1,188 @@
<?php
/*
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 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\Services\Database;
use Illuminate\Database\ConnectionResolver;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
class CreationService
{
/**
* @var \Illuminate\Database\ConnectionResolver
*/
protected $connection;
/**
* @var \Illuminate\Database\ConnectionInterface
*/
protected $database;
/**
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection
*/
protected $dynamic;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
protected $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
protected $repository;
/**
* CreationService constructor.
*
* @param \Illuminate\Database\ConnectionInterface $database
* @param \Illuminate\Database\ConnectionResolver $connection
* @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
*/
public function __construct(
ConnectionInterface $database,
ConnectionResolver $connection,
DynamicDatabaseConnection $dynamic,
DatabaseRepositoryInterface $repository,
Encrypter $encrypter
) {
$this->connection = $connection;
$this->database = $database;
$this->dynamic = $dynamic;
$this->encrypter = $encrypter;
$this->repository = $repository;
}
/**
* Create a new database that is linked to a specific host.
*
* @param array $data
* @return \Illuminate\Database\Eloquent\Model
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function create(array $data)
{
$data['database'] = sprintf('d%d_%s', $data['server_id'], $data['database']);
$data['username'] = sprintf('u%d_%s', $data['server_id'], str_random(10));
$data['password'] = $this->encrypter->encrypt(str_random(16));
$this->database->beginTransaction();
try {
$database = $this->repository->createIfNotExists($data);
$this->dynamic->set('dynamic', $data['database_host_id']);
$this->repository->createDatabase($database->database, 'dynamic');
$this->repository->createUser(
$database->username, $database->remote, $this->encrypter->decrypt($database->password), 'dynamic'
);
$this->repository->assignUserToDatabase(
$database->database, $database->username, $database->remote, 'dynamic'
);
$this->repository->flush('dynamic');
$this->database->commit();
} catch (\Exception $ex) {
try {
if (isset($database)) {
$this->repository->dropDatabase($database->database, 'dynamic');
$this->repository->dropUser($database->username, $database->remote, 'dynamic');
$this->repository->flush('dynamic');
}
} catch (\Exception $ex) {
// ignore an exception
}
$this->database->rollBack();
throw $ex;
}
return $database;
}
/**
* Change the password for a specific user and database combination.
*
* @param int $id
* @param string $password
* @return bool
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function changePassword($id, $password)
{
$database = $this->repository->find($id);
$this->dynamic->set('dynamic', $database->database_host_id);
$this->database->beginTransaction();
try {
$updated = $this->repository->withoutFresh()->update($id, [
'password' => $this->encrypter->encrypt($password),
]);
$this->repository->dropUser($database->username, $database->remote, 'dynamic');
$this->repository->createUser($database->username, $database->remote, $password);
$this->repository->assignUserToDatabase(
$database->database, $database->username, $database->remote, 'dynamic'
);
$this->repository->flush();
$this->database->commit();
} catch (\Exception $ex) {
$this->database->rollBack();
throw $ex;
}
return $updated;
}
/**
* Delete a database from the given host server.
*
* @param int $id
* @return bool|null
*/
public function delete($id)
{
$database = $this->repository->find($id);
$this->dynamic->set('dynamic', $database->database_host_id);
$this->repository->dropDatabase($database->database, 'dynamic');
$this->repository->dropUser($database->username, $database->remote, 'dynamic');
$this->repository->flush('dynamic');
return $this->repository->delete($id);
}
}

View file

@ -27,7 +27,7 @@ namespace Pterodactyl\Services\Database;
use Illuminate\Database\DatabaseManager;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Contracts\Repository\DatabaseHostInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
class DatabaseHostService
{
@ -47,20 +47,20 @@ class DatabaseHostService
protected $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostInterface
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
*/
protected $repository;
/**
* DatabaseHostService constructor.
*
* @param \Pterodactyl\Contracts\Repository\DatabaseHostInterface $repository
* @param \Illuminate\Database\DatabaseManager $database
* @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository
* @param \Illuminate\Database\DatabaseManager $database
* @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
*/
public function __construct(
DatabaseHostInterface $repository,
DatabaseHostRepositoryInterface $repository,
DatabaseManager $database,
DynamicDatabaseConnection $dynamic,
Encrypter $encrypter

View file

@ -40,6 +40,7 @@ return [
'servers' => env('APP_PAGINATE_FRONT_SERVERS', 15),
],
'admin' => [
'servers' => env('APP_PAGINATE_ADMIN_SERVERS', 25),
'users' => env('APP_PAGINATE_ADMIN_USERS', 25),
],
'api' => [

View file

@ -29,7 +29,7 @@ use Tests\TestCase;
use Illuminate\Database\DatabaseManager;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Contracts\Repository\DatabaseHostInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
use Pterodactyl\Services\Database\DatabaseHostService;
class DatabaseHostServiceTest extends TestCase
@ -50,7 +50,7 @@ class DatabaseHostServiceTest extends TestCase
protected $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostInterface
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
*/
protected $repository;
@ -69,7 +69,7 @@ class DatabaseHostServiceTest extends TestCase
$this->database = m::mock(DatabaseManager::class);
$this->dynamic = m::mock(DynamicDatabaseConnection::class);
$this->encrypter = m::mock(Encrypter::class);
$this->repository = m::mock(DatabaseHostInterface::class);
$this->repository = m::mock(DatabaseHostRepositoryInterface::class);
$this->service = new DatabaseHostService(
$this->repository,