mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(api): hide DB URLs in create responses without sensitive access (#12297)
This commit is contained in:
@@ -210,6 +210,25 @@ class DatabasesController extends Controller
|
||||
return $backup->unsetRelation('s3Storages')->unsetRelation('s3');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the create response; the connection URLs contain credentials, so they need sensitive read access.
|
||||
*
|
||||
* @return array{uuid: string, internal_db_url?: string, external_db_url?: string}
|
||||
*/
|
||||
private function createdDatabasePayload(Request $request, $database): array
|
||||
{
|
||||
$payload = ['uuid' => $database->uuid];
|
||||
if ($request->attributes->get('can_read_sensitive', false) !== true) {
|
||||
return $payload;
|
||||
}
|
||||
$payload['internal_db_url'] = $database->internal_db_url;
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
private function removeSensitiveData($database, bool $loadNestedServerSecrets = false)
|
||||
{
|
||||
$database->makeHidden([
|
||||
@@ -2330,13 +2349,7 @@ class DatabasesController extends Controller
|
||||
$this->attachTagsToResource($database, $tagNames, $teamId);
|
||||
}
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
'uuid' => $database->uuid,
|
||||
'internal_db_url' => $database->internal_db_url,
|
||||
];
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
$payload = $this->createdDatabasePayload($request, $database);
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
@@ -2402,13 +2415,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
'uuid' => $database->uuid,
|
||||
'internal_db_url' => $database->internal_db_url,
|
||||
];
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
$payload = $this->createdDatabasePayload($request, $database);
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
@@ -2474,13 +2481,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
'uuid' => $database->uuid,
|
||||
'internal_db_url' => $database->internal_db_url,
|
||||
];
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
$payload = $this->createdDatabasePayload($request, $database);
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
@@ -2543,13 +2544,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
'uuid' => $database->uuid,
|
||||
'internal_db_url' => $database->internal_db_url,
|
||||
];
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
$payload = $this->createdDatabasePayload($request, $database);
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
@@ -2645,13 +2640,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
'uuid' => $database->uuid,
|
||||
'internal_db_url' => $database->internal_db_url,
|
||||
];
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
$payload = $this->createdDatabasePayload($request, $database);
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
@@ -2694,13 +2683,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
'uuid' => $database->uuid,
|
||||
'internal_db_url' => $database->internal_db_url,
|
||||
];
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
$payload = $this->createdDatabasePayload($request, $database);
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
@@ -2765,13 +2748,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
'uuid' => $database->uuid,
|
||||
'internal_db_url' => $database->internal_db_url,
|
||||
];
|
||||
if ($database->is_public && $database->public_port) {
|
||||
$payload['external_db_url'] = $database->external_db_url;
|
||||
}
|
||||
$payload = $this->createdDatabasePayload($request, $database);
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
config(['app.maintenance.driver' => 'file']);
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::firstOrCreate(['id' => 0], ['is_api_enabled' => true]));
|
||||
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user = User::factory()->create();
|
||||
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
||||
|
||||
Queue::fake();
|
||||
});
|
||||
|
||||
function databaseCreateSensitiveHeaders(User $user, int $teamId, array $abilities): array
|
||||
{
|
||||
$plainTextToken = Str::random(40);
|
||||
$token = $user->tokens()->create([
|
||||
'name' => 'database-create-sensitive-test',
|
||||
'token' => hash('sha256', $plainTextToken),
|
||||
'abilities' => $abilities,
|
||||
'team_id' => $teamId,
|
||||
]);
|
||||
|
||||
return ['Authorization' => 'Bearer '.$token->getKey().'|'.$plainTextToken];
|
||||
}
|
||||
|
||||
function createDatabaseViaApi(object $test, string $engine, array $abilities)
|
||||
{
|
||||
return $test->withHeaders(databaseCreateSensitiveHeaders($test->user, $test->team->id, $abilities))
|
||||
->postJson("/api/v1/databases/{$engine}", [
|
||||
'project_uuid' => $test->project->uuid,
|
||||
'environment_uuid' => $test->environment->uuid,
|
||||
'server_uuid' => $test->server->uuid,
|
||||
'is_public' => true,
|
||||
'public_port' => 15432,
|
||||
]);
|
||||
}
|
||||
|
||||
$engines = ['postgresql', 'mariadb', 'mysql', 'redis', 'keydb', 'clickhouse', 'mongodb'];
|
||||
|
||||
test('create response hides database urls from a token without sensitive read access', function (string $engine) {
|
||||
$response = createDatabaseViaApi($this, $engine, ['write'])->assertCreated();
|
||||
|
||||
expect($response->json())->toHaveKey('uuid')
|
||||
->not->toHaveKey('internal_db_url')
|
||||
->not->toHaveKey('external_db_url');
|
||||
})->with($engines);
|
||||
|
||||
test('create response includes database urls for a token with sensitive read access', function (string $engine, array $abilities) {
|
||||
$response = createDatabaseViaApi($this, $engine, $abilities)->assertCreated();
|
||||
|
||||
expect($response->json())->toHaveKeys(['uuid', 'internal_db_url', 'external_db_url']);
|
||||
})->with($engines)->with([
|
||||
'read:sensitive' => [['write', 'read:sensitive']],
|
||||
'root' => [['root']],
|
||||
]);
|
||||
Reference in New Issue
Block a user