Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions app/Commands/Connection/AddCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class AddCommand extends Command
{--username= : Database username}
{--password= : Database password}
{--production : Mark this as a production connection}
{--trust-server-certificate : Trust the server certificate (SQL Server with self-signed certs)}';
{--trust-server-certificate : Trust the server certificate (SQL Server with self-signed certs)}
{--attr_ssl_ca : The file path to an SSL certificate authority (CA), to enable secure encrypted connections to a MySQL database}';

/**
* @var string
Expand Down Expand Up @@ -227,7 +228,20 @@ public function handle(ConfigService $config): int
}
}

// --- Step 10: Production flag ---
// --- Step 10: SSL certificate ---
$attrSslCa = null;

if ($type->requiresNetworkConfig() && ($type === DatabaseConnectionType::Mysql || $type === DatabaseConnectionType::MariaDB)) {
$attrSslCaOption = $this->option('attr_ssl_ca');
$attrSslCa = is_string($attrSslCaOption) && $attrSslCaOption !== '' ? $attrSslCaOption : null;

if ($attrSslCa === null) {
$asked = $this->ask('SSL Certificate Authority');
$attrSslCa = is_string($asked) ? $asked : null;
}
}

// --- Step 11: Production flag ---
$isProduction = (bool) $this->option('production');

if (! $isProduction) {
Expand All @@ -239,6 +253,10 @@ public function handle(ConfigService $config): int
$this->warn('This connection is marked as production. Destructive operations will require confirmation.');
}





// --- Summary table ---
$summaryRows = [
['Name', $name],
Expand Down Expand Up @@ -282,6 +300,7 @@ public function handle(ConfigService $config): int
}

$summaryRows[] = ['Production', $isProduction ? 'Yes' : 'No'];
$summaryRows[] = ['SSL Certificate Authority', $attrSslCa ? 'Yes' : 'No'];

$this->table(['Field', 'Value'], $summaryRows);

Expand All @@ -305,6 +324,7 @@ public function handle(ConfigService $config): int
isProduction: $isProduction,
trustServerCertificate: $trustServerCertificate,
dialect: $dialect,
attrSslCa: $attrSslCa,
);

try {
Expand Down
3 changes: 2 additions & 1 deletion app/Commands/Connection/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ public function handle(ConfigService $config): int
$host,
$connection->database ?? '—',
$connection->isProduction ? 'Yes' : 'No',
$connection->attrSslCa ? 'Yes' : 'No',
];
}

$this->table(['Name', 'Driver', 'Host', 'Database', 'Production'], $rows);
$this->table(['Name', 'Driver', 'Host', 'Database', 'Production', 'SSL Certificate Authority'], $rows);

return ExitCode::Success->value;
}
Expand Down
6 changes: 6 additions & 0 deletions app/Commands/Connection/UpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ private function promptForFields(ConnectionData $current): ConnectionData
$newType = DatabaseConnectionType::from($newTypeValue);
$typeChanged = $newType !== $current->type;

$attrSslCa = null;
if ($newType->requiresNetworkConfig()) {
if ($typeChanged) {
$host = $this->askString('Host', 'localhost');
Expand Down Expand Up @@ -156,6 +157,10 @@ private function promptForFields(ConnectionData $current): ConnectionData
$password = Crypt::encryptString($passwordInput);
}

if($newType->requiresNetworkConfig() && ($newType === DatabaseConnectionType::Mysql || $newType === DatabaseConnectionType::MariaDB)) {
$attrSslCa = $this->ask('SSL Certificate Authority', $current->attrSslCa ?? null);
}

$isProduction = $this->confirm('Is this a production connection?', $current->isProduction);

$trustServerCertificate = false;
Expand All @@ -177,6 +182,7 @@ private function promptForFields(ConnectionData $current): ConnectionData
password: $password,
isProduction: $isProduction,
trustServerCertificate: $trustServerCertificate,
attrSslCa: $attrSslCa,
);
}

Expand Down
7 changes: 7 additions & 0 deletions app/Data/ConnectionData.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public function __construct(
public bool $isProduction,
public bool $trustServerCertificate = false,
public ?DatabaseConnectionType $dialect = null,
public ?string $attrSslCa = null,
) {}

/** @param array<string, mixed> $data */
Expand All @@ -33,6 +34,7 @@ public static function fromArray(string $name, array $data): self
$username = $data['username'] ?? null;
$password = $data['password'] ?? null;
$dialect = $data['dialect'] ?? null;
$attrSslCa = $data['attr_ssl_ca'] ?? null;

return new self(
name: $name,
Expand All @@ -46,6 +48,7 @@ public static function fromArray(string $name, array $data): self
isProduction: (bool) ($data['is_production'] ?? false),
trustServerCertificate: (bool) ($data['trust_server_certificate'] ?? false),
dialect: is_string($dialect) ? DatabaseConnectionType::tryFrom($dialect) : null,
attrSslCa: is_string($attrSslCa) ? $attrSslCa : null,
);
}

Expand Down Expand Up @@ -85,6 +88,10 @@ public function toArray(): array
$data['trust_server_certificate'] = true;
}

if ($this->attrSslCa) {
$data['attr_ssl_ca'] = $this->attrSslCa;
}

return $data;
}
}
8 changes: 8 additions & 0 deletions app/Services/Database/DatabaseConnectionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use App\Enums\DatabaseConnectionType;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use PDO;
use Pdo\Mysql;
use RuntimeException;
use Throwable;

Expand Down Expand Up @@ -92,6 +94,12 @@ public function buildConfig(ConnectionData $connection, string $password): array
$config['search_path'] = $connection->schema;
}

if($connection->attrSslCa) {
$config['options'] = [
Mysql::ATTR_SSL_CA => base_path($connection->attrSslCa),
];
}

return $config;
}

Expand Down
6 changes: 6 additions & 0 deletions tests/Feature/Commands/Connection/AddCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ function fakeConfigReadOnly(): ConfigService
'--database' => 'mydb',
'--username' => 'root',
'--password' => 'secret',
'--attr_ssl_ca' => '',
])
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save this connection?', 'yes')
->assertExitCode(0);
Expand Down Expand Up @@ -162,6 +164,7 @@ function fakeConfigReadOnly(): ConfigService
'--username' => 'root',
'--password' => 'secret',
])
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save this connection?', 'no')
->expectsOutputToContain('Cancelled.')
Expand All @@ -179,6 +182,7 @@ function fakeConfigReadOnly(): ConfigService
->expectsQuestion('Database name', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password', 'secret')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save this connection?', 'yes')
->assertExitCode(0);
Expand Down Expand Up @@ -273,6 +277,7 @@ function fakeConfigReadOnly(): ConfigService
'--password' => 'secret',
'--production' => true,
])
->expectsQuestion('SSL Certificate Authority', null)
->expectsOutputToContain('This connection is marked as production.')
->expectsConfirmation('Save this connection?', 'yes')
->assertExitCode(0);
Expand Down Expand Up @@ -326,6 +331,7 @@ function fakeConfigReadOnly(): ConfigService
'--username' => 'root',
'--password' => 'secret',
])
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save this connection?', 'yes')
->expectsOutputToContain('Failed to save connection: disk full')
Expand Down
8 changes: 8 additions & 0 deletions tests/Feature/Commands/Connection/UpdateCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save changes?', 'yes')
->assertExitCode(0);
Expand All @@ -73,6 +74,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save changes?', 'no')
->assertExitCode(0);
Expand Down Expand Up @@ -114,6 +116,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save changes?', 'yes')
->assertExitCode(0);
Expand All @@ -139,6 +142,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save changes?', 'yes')
->assertExitCode(0);
Expand Down Expand Up @@ -179,6 +183,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save changes?', 'yes')
->expectsOutputToContain("Connection 'renamed' updated successfully.")
Expand All @@ -204,6 +209,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save changes?', 'yes')
->expectsOutputToContain('disk full')
Expand Down Expand Up @@ -234,6 +240,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsConfirmation('Save changes?', 'yes')
->assertExitCode(0);
Expand Down Expand Up @@ -396,6 +403,7 @@ function makeUpdateConnection(string $name = 'staging'): ConnectionData
->expectsQuestion('Database', 'mydb')
->expectsQuestion('Username', 'root')
->expectsQuestion('Password (press Enter to keep current)', '')
->expectsQuestion('SSL Certificate Authority', null)
->expectsConfirmation('Is this a production connection?', 'no')
->expectsOutputToContain("A connection named 'production' already exists.")
->assertExitCode(4);
Expand Down