-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add migration auth0 -> keycloak clients
- Loading branch information
Koen Eelen
committed
Jun 12, 2024
1 parent
7853a18
commit 5249a12
Showing
2 changed files
with
166 additions
and
0 deletions.
There are no files selected for viewing
88 changes: 88 additions & 0 deletions
88
app/Console/Commands/Migrations/MigrateAuth0ToKeycloakClients.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Console\Commands\Migrations; | ||
|
||
use Illuminate\Console\Command; | ||
use Illuminate\Support\Collection; | ||
use Illuminate\Support\Facades\DB; | ||
|
||
final class MigrateAuth0ToKeycloakClients extends Command | ||
{ | ||
protected $signature = 'migrate:keycloak {updated_at?}';//updated_at format = | ||
protected $description = 'Copy all auth0 clients to keycloak clients - does NOT remove the auth0 clients.'; | ||
|
||
public function __construct() | ||
{ | ||
parent::__construct(); | ||
} | ||
|
||
public function handle(): int | ||
{ | ||
$updatedAt = $this->argument('updated_at'); | ||
$auth0Clients = $this->findAuth0Clients($updatedAt); | ||
|
||
if ($updatedAt !== null && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $updatedAt)) { | ||
$this->warn('Invalid format used for updated at, use the format YYYY-MM-DD'); | ||
return self::FAILURE; | ||
} | ||
|
||
$total = count($auth0Clients); | ||
if ($total <= 0) { | ||
$this->warn($updatedAt ? 'No clients found to migrate starting from ' . $updatedAt : 'No clients found to migrate '); | ||
return self::FAILURE; | ||
} | ||
|
||
if (!$this->confirm( | ||
sprintf( | ||
'Are you sure you want to copy %s auth0 clients to Keycloak %s?', | ||
$total, | ||
$updatedAt ? 'starting from ' . $updatedAt : '' | ||
) | ||
)) { | ||
return self::FAILURE; | ||
} | ||
|
||
$bar = $this->output->createProgressBar($total); | ||
$bar->start(); | ||
|
||
foreach ($auth0Clients as $client) { | ||
DB::table('keycloak_clients')->insert([ | ||
'id' => $client->id, | ||
'integration_id' => $client->integration_id, | ||
'client_id' => $client->auth0_client_id, | ||
'client_secret' => $client->auth0_client_secret, | ||
'realm' => $client->auth0_tenant, | ||
'created_at' => $client->created_at, | ||
'updated_at' => $client->updated_at, | ||
'deleted_at' => $client->deleted_at, | ||
]); | ||
|
||
$this->info(sprintf('Converted client %s - last updated at %s', $client->id, $client->updated_at)); | ||
|
||
$bar->advance(); | ||
} | ||
|
||
$bar->finish(); | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
private function findAuth0Clients(?string $updatedAt): Collection | ||
{ | ||
$query = DB::table('auth0_clients') | ||
->orderBy('updated_at', 'asc') | ||
->whereNotIn('auth0_clients.id', function ($query) { | ||
$query->select('id') | ||
->from('keycloak_clients'); | ||
}) | ||
->whereNull('deleted_at'); | ||
|
||
if ($updatedAt !== null) { | ||
$query->where('updated_at', '>', $updatedAt); | ||
} | ||
|
||
return $query->get(); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
tests/Console/Commands/Migrations/MigrateAuth0ToKeycloakClientsTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Console\Commands\Migrations; | ||
|
||
use Illuminate\Foundation\Testing\RefreshDatabase; | ||
use Illuminate\Support\Facades\DB; | ||
use Illuminate\Testing\PendingCommand; | ||
use Symfony\Component\Console\Command\Command; | ||
use Tests\TestCase; | ||
|
||
final class MigrateAuth0ToKeycloakClientsTest extends TestCase | ||
{ | ||
use RefreshDatabase; | ||
|
||
public function test_invalid_date_format(): void | ||
{ | ||
$this->getPendingCommand('migrate:keycloak', ['updated_at' => 'invalid-date']) | ||
->expectsOutput('Invalid format used for updated at, use the format YYYY-MM-DD') | ||
->assertExitCode(Command::FAILURE); | ||
} | ||
|
||
public function test_no_clients_to_migrate(): void | ||
{ | ||
$this->getPendingCommand('migrate:keycloak') | ||
->expectsOutput('No clients found to migrate ') | ||
->assertExitCode(Command::FAILURE); | ||
} | ||
|
||
public function test_no_clients_to_migrate_with_date(): void | ||
{ | ||
$this->getPendingCommand('migrate:keycloak', ['updated_at' => '2023-01-01']) | ||
->expectsOutput('No clients found to migrate starting from 2023-01-01') | ||
->assertExitCode(Command::FAILURE); | ||
} | ||
|
||
public function test_migration_with_clients(): void | ||
{ | ||
DB::table('auth0_clients')->insert([ | ||
'id' => 'client1', | ||
'integration_id' => 'integration1', | ||
'auth0_client_id' => 'auth0_client1', | ||
'auth0_client_secret' => 'secret1', | ||
'auth0_tenant' => 'tenant1', | ||
'created_at' => now(), | ||
'updated_at' => now(), | ||
]); | ||
|
||
$this->getPendingCommand('migrate:keycloak') | ||
->expectsConfirmation('Are you sure you want to copy 1 auth0 clients to Keycloak ?', 'yes') | ||
->expectsOutput('Converted client client1 - last updated at ' . now()) | ||
->assertExitCode(Command::SUCCESS); | ||
|
||
$this->assertDatabaseHas('keycloak_clients', [ | ||
'id' => 'client1', | ||
'integration_id' => 'integration1', | ||
'client_id' => 'auth0_client1', | ||
'client_secret' => 'secret1', | ||
'realm' => 'tenant1', | ||
]); | ||
} | ||
|
||
protected function tearDown(): void | ||
{ | ||
DB::table('auth0_clients')->where('id', 'client1')->delete(); | ||
DB::table('keycloak_clients')->where('id', 'client1')->delete(); | ||
|
||
parent::tearDown(); | ||
} | ||
|
||
private function getPendingCommand(string $command, array $params = []): PendingCommand | ||
{ | ||
$command = $this->artisan($command, $params); | ||
$this->assertInstanceOf(PendingCommand::class, $command); | ||
return $command; | ||
} | ||
} |