Skip to content

Commit de07b3c

Browse files
committed
Add server database management support to API.
1 parent 2bd691e commit de07b3c

File tree

10 files changed

+257
-54
lines changed

10 files changed

+257
-54
lines changed

app/Exceptions/DisplayException.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ public function getErrorLevel()
4646
return $this->level;
4747
}
4848

49+
/**
50+
* @return int
51+
*/
52+
public function getStatusCode()
53+
{
54+
return Response::HTTP_BAD_REQUEST;
55+
}
56+
4957
/**
5058
* Render the exception to the user by adding a flashed message to the session
5159
* and then redirecting them back to the page that they came from. If the

app/Extensions/Spatie/Fractalistic/Fractal.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
namespace Pterodactyl\Extensions\Spatie\Fractalistic;
44

55
use League\Fractal\TransformerAbstract;
6+
use Spatie\Fractal\Fractal as SpatieFractal;
67
use League\Fractal\Serializer\JsonApiSerializer;
7-
use Spatie\Fractalistic\Fractal as SpatieFractal;
88
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
99
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
1010

app/Http/Controllers/Api/Application/Servers/DatabaseController.php

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,32 @@
22

33
namespace Pterodactyl\Http\Controllers\Api\Application\Servers;
44

5+
use Illuminate\Http\Response;
56
use Pterodactyl\Models\Server;
7+
use Pterodactyl\Models\Database;
8+
use Illuminate\Http\JsonResponse;
9+
use Pterodactyl\Services\Databases\DatabasePasswordService;
10+
use Pterodactyl\Services\Databases\DatabaseManagementService;
611
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
712
use Pterodactyl\Transformers\Api\Application\ServerDatabaseTransformer;
813
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
14+
use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabaseRequest;
15+
use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabasesRequest;
16+
use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\ServerDatabaseWriteRequest;
17+
use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\StoreServerDatabaseRequest;
918

1019
class DatabaseController extends ApplicationApiController
1120
{
21+
/**
22+
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
23+
*/
24+
private $databaseManagementService;
25+
26+
/**
27+
* @var \Pterodactyl\Services\Databases\DatabasePasswordService
28+
*/
29+
private $databasePasswordService;
30+
1231
/**
1332
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
1433
*/
@@ -17,28 +36,105 @@ class DatabaseController extends ApplicationApiController
1736
/**
1837
* DatabaseController constructor.
1938
*
39+
* @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService
40+
* @param \Pterodactyl\Services\Databases\DatabasePasswordService $databasePasswordService
2041
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
2142
*/
22-
public function __construct(DatabaseRepositoryInterface $repository)
23-
{
43+
public function __construct(
44+
DatabaseManagementService $databaseManagementService,
45+
DatabasePasswordService $databasePasswordService,
46+
DatabaseRepositoryInterface $repository
47+
) {
2448
parent::__construct();
2549

50+
$this->databaseManagementService = $databaseManagementService;
51+
$this->databasePasswordService = $databasePasswordService;
2652
$this->repository = $repository;
2753
}
2854

2955
/**
3056
* Return a listing of all databases currently available to a single
3157
* server.
3258
*
33-
* @param \Pterodactyl\Models\Server $server
59+
* @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabasesRequest $request
60+
* @param \Pterodactyl\Models\Server $server
3461
* @return array
3562
*/
36-
public function index(Server $server): array
63+
public function index(GetServerDatabasesRequest $request, Server $server): array
3764
{
3865
$databases = $this->repository->getDatabasesForServer($server->id);
3966

4067
return $this->fractal->collection($databases)
4168
->transformWith($this->getTransformer(ServerDatabaseTransformer::class))
4269
->toArray();
4370
}
71+
72+
/**
73+
* Return a single server database.
74+
*
75+
* @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabaseRequest $request
76+
* @param \Pterodactyl\Models\Server $server
77+
* @param \Pterodactyl\Models\Database $database
78+
* @return array
79+
*/
80+
public function view(GetServerDatabaseRequest $request, Server $server, Database $database): array
81+
{
82+
return $this->fractal->item($database)
83+
->transformWith($this->getTransformer(ServerDatabaseTransformer::class))
84+
->toArray();
85+
}
86+
87+
/**
88+
* Reset the password for a specific server database.
89+
*
90+
* @param \Pterodactyl\Models\Server $server
91+
* @param \Pterodactyl\Models\Database $database
92+
* @return \Illuminate\Http\Response
93+
*
94+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
95+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
96+
*/
97+
public function resetPassword(ServerDatabaseWriteRequest $request, Server $server, Database $database): Response
98+
{
99+
$this->databasePasswordService->handle($database, str_random(24));
100+
101+
return response('', 204);
102+
}
103+
104+
/**
105+
* Create a new database on the Panel for a given server.
106+
*
107+
* @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\StoreServerDatabaseRequest $request
108+
* @param \Pterodactyl\Models\Server $server
109+
* @return \Illuminate\Http\JsonResponse
110+
*
111+
* @throws \Exception
112+
* @throws \Pterodactyl\Exceptions\DisplayException
113+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
114+
*/
115+
public function store(StoreServerDatabaseRequest $request, Server $server): JsonResponse
116+
{
117+
$database = $this->databaseManagementService->create($server->id, $request->validated());
118+
119+
return $this->fractal->item($database)
120+
->transformWith($this->getTransformer(ServerDatabaseTransformer::class))
121+
->respond(201);
122+
}
123+
124+
/**
125+
* Delete a specific database from the Panel.
126+
*
127+
* @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\ServerDatabaseWriteRequest $request
128+
* @param \Pterodactyl\Models\Server $server
129+
* @param \Pterodactyl\Models\Database $database
130+
* @return \Illuminate\Http\Response
131+
*
132+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
133+
*/
134+
public function delete(ServerDatabaseWriteRequest $request, Server $server, Database $database): Response
135+
{
136+
$this->databaseManagementService->delete($database->id);
137+
138+
return response('', 204);
139+
}
44140
}

app/Http/Middleware/Api/ApiSubstituteBindings.php

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33
namespace Pterodactyl\Http\Middleware\Api;
44

55
use Closure;
6-
use ReflectionMethod;
7-
use Illuminate\Container\Container;
8-
use Illuminate\Routing\ImplicitRouteBinding;
9-
use Illuminate\Contracts\Routing\UrlRoutable;
106
use Illuminate\Routing\Middleware\SubstituteBindings;
7+
use Illuminate\Database\Eloquent\ModelNotFoundException;
118

129
class ApiSubstituteBindings extends SubstituteBindings
1310
{
@@ -24,47 +21,18 @@ public function handle($request, Closure $next)
2421
$route = $request->route();
2522

2623
$this->router->substituteBindings($route);
27-
$this->resolveForRoute($route);
2824

29-
return $next($request);
30-
}
31-
32-
/**
33-
* Resolve the implicit route bindings for the given route. This function
34-
* overrides Laravel's default inn \Illuminate\Routing\ImplictRouteBinding
35-
* to not throw a 404 error when a model is not found.
36-
*
37-
* If a model is not found using the provided information, the binding is
38-
* replaced with null which is then checked in the form requests on API
39-
* routes. This avoids a potential imformation leak on API routes for
40-
* unauthenticated users.
41-
*
42-
* @param \Illuminate\Routing\Route $route
43-
*/
44-
protected function resolveForRoute($route)
45-
{
46-
$parameters = $route->parameters();
47-
48-
// Avoid having to copy and paste the entirety of that class into this middleware
49-
// by using reflection to access a protected method.
50-
$reflection = new ReflectionMethod(ImplicitRouteBinding::class, 'getParameterName');
51-
$reflection->setAccessible(true);
52-
53-
foreach ($route->signatureParameters(UrlRoutable::class) as $parameter) {
54-
if (! $parameterName = $reflection->invokeArgs(null, [$parameter->name, $parameters])) {
55-
continue;
56-
}
57-
58-
$parameterValue = $parameters[$parameterName];
59-
60-
if ($parameterValue instanceof UrlRoutable) {
61-
continue;
62-
}
63-
64-
// Try to find an existing model, if one is not found simply bind the
65-
// parameter as null.
66-
$instance = Container::getInstance()->make($parameter->getClass()->name);
67-
$route->setParameter($parameterName, $instance->resolveRouteBinding($parameterValue));
25+
// Attempt to resolve bindings for this route. If one of the models
26+
// cannot be resolved do not immediately return a 404 error. Set a request
27+
// attribute that can be checked in the base API request class to only
28+
// trigger a 404 after validating that the API key making the request is valid
29+
// and even has permission to access the requested resource.
30+
try {
31+
$this->router->substituteImplicitBindings($route);
32+
} catch (ModelNotFoundException $exception) {
33+
$request->attributes->set('is_missing_model', true);
6834
}
35+
36+
return $next($request);
6937
}
7038
}

app/Http/Requests/Api/Application/ApplicationApiRequest.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,32 @@ public function key(): ApiKey
7373
return $this->attributes->get('api_key');
7474
}
7575

76-
/**
76+
/*
7777
* Determine if the request passes the authorization check as well
7878
* as the exists check.
7979
*
8080
* @return bool
8181
*
8282
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
8383
*/
84+
85+
/**
86+
* @return bool
87+
*/
8488
protected function passesAuthorization()
8589
{
86-
$passes = parent::passesAuthorization();
90+
if (! parent::passesAuthorization()) {
91+
return false;
92+
}
8793

8894
// Only let the user know that a resource does not exist if they are
8995
// authenticated to access the endpoint. This avoids exposing that
9096
// an item exists (or does not exist) to the user until they can prove
9197
// that they have permission to know about it.
92-
if ($passes && ! $this->resourceExists()) {
98+
if ($this->attributes->get('is_missing_model', false) || ! $this->resourceExists()) {
9399
throw new NotFoundHttpException('The requested resource does not exist on this server.');
94100
}
95101

96-
return $passes;
102+
return true;
97103
}
98104
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
4+
5+
use Pterodactyl\Services\Acl\Api\AdminAcl;
6+
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
7+
8+
class GetServerDatabaseRequest extends ApplicationApiRequest
9+
{
10+
/**
11+
* @var string
12+
*/
13+
protected $resource = AdminAcl::RESOURCE_SERVER_DATABASES;
14+
15+
/**
16+
* @var int
17+
*/
18+
protected $permission = AdminAcl::READ;
19+
20+
/**
21+
* Determine if the requested server database exists.
22+
*
23+
* @return bool
24+
*/
25+
public function resourceExists(): bool
26+
{
27+
$server = $this->route()->parameter('server');
28+
$database = $this->route()->parameter('database');
29+
30+
return $database->server_id === $server->id;
31+
}
32+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
4+
5+
use Pterodactyl\Services\Acl\Api\AdminAcl;
6+
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
7+
8+
class GetServerDatabasesRequest extends ApplicationApiRequest
9+
{
10+
/**
11+
* @var string
12+
*/
13+
protected $resource = AdminAcl::RESOURCE_SERVER_DATABASES;
14+
15+
/**
16+
* @var int
17+
*/
18+
protected $permission = AdminAcl::READ;
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
4+
5+
use Pterodactyl\Services\Acl\Api\AdminAcl;
6+
7+
class ServerDatabaseWriteRequest extends GetServerDatabasesRequest
8+
{
9+
/**
10+
* @var int
11+
*/
12+
protected $permission = AdminAcl::WRITE;
13+
}

0 commit comments

Comments
 (0)