From 7a9281d4092750c61a2b9d1191b5ad401300b412 Mon Sep 17 00:00:00 2001 From: Koen Eelen Date: Mon, 10 Jun 2024 12:05:36 +0200 Subject: [PATCH 1/5] Make scopeConfig dependent on chosen realm --- app/Keycloak/Client/KeycloakApiClient.php | 4 +- app/Keycloak/KeycloakServiceProvider.php | 11 --- app/Keycloak/Listeners/CreateClients.php | 9 +- app/Keycloak/Listeners/UpdateClients.php | 9 +- app/Keycloak/Realm.php | 6 +- app/Keycloak/Realms.php | 9 +- config/keycloak.php | 24 +++-- .../Keycloak/Client/KeycloakApiClientTest.php | 14 --- .../Keycloak/Listeners/CreateClientsTest.php | 10 --- .../Keycloak/Listeners/UpdateClientsTest.php | 11 --- tests/Keycloak/RealmFactory.php | 19 ++++ tests/Keycloak/RealmsTest.php | 87 +++++++++++++++++++ 12 files changed, 146 insertions(+), 67 deletions(-) create mode 100644 tests/Keycloak/RealmsTest.php diff --git a/app/Keycloak/Client/KeycloakApiClient.php b/app/Keycloak/Client/KeycloakApiClient.php index bfee18130..2421ca3fc 100644 --- a/app/Keycloak/Client/KeycloakApiClient.php +++ b/app/Keycloak/Client/KeycloakApiClient.php @@ -11,7 +11,6 @@ use App\Keycloak\Converters\IntegrationToKeycloakClientConverter; use App\Keycloak\Exception\KeyCloakApiFailed; use App\Keycloak\Realm; -use App\Keycloak\ScopeConfig; use GuzzleHttp\Psr7\Request; use Psr\Log\LoggerInterface; use Ramsey\Uuid\Uuid; @@ -22,7 +21,6 @@ { public function __construct( private KeycloakHttpClient $client, - private ScopeConfig $scopeConfig, private LoggerInterface $logger, ) { } @@ -89,7 +87,7 @@ public function addScopeToClient(Client $client, UuidInterface $scopeId): void public function deleteScopes(Client $client): void { - foreach ($this->scopeConfig->getAll() as $scope) { + foreach ($client->getRealm()->scopeConfig->getAll() as $scope) { try { $response = $this->client->sendWithBearer( new Request( diff --git a/app/Keycloak/KeycloakServiceProvider.php b/app/Keycloak/KeycloakServiceProvider.php index b2d1fc373..68b61de15 100644 --- a/app/Keycloak/KeycloakServiceProvider.php +++ b/app/Keycloak/KeycloakServiceProvider.php @@ -26,7 +26,6 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; use Psr\Log\LoggerInterface; -use Ramsey\Uuid\Uuid; final class KeycloakServiceProvider extends ServiceProvider { @@ -40,20 +39,10 @@ public function register(): void $this->app->get(LoggerInterface::class), ) ), - $this->app->get(ScopeConfig::class), $this->app->get(LoggerInterface::class), ); }); - $this->app->singleton(ScopeConfig::class, function () { - return new ScopeConfig( - Uuid::fromString(config('keycloak.scope.search_api_id')), - Uuid::fromString(config('keycloak.scope.entry_api_id')), - Uuid::fromString(config('keycloak.scope.widgets_id')), - Uuid::fromString(config('keycloak.scope.uitpas_id')), - ); - }); - $this->app->singleton(KeycloakClientRepository::class, function () { return $this->app->get(EloquentKeycloakClientRepository::class); }); diff --git a/app/Keycloak/Listeners/CreateClients.php b/app/Keycloak/Listeners/CreateClients.php index de8a35817..d97f77036 100644 --- a/app/Keycloak/Listeners/CreateClients.php +++ b/app/Keycloak/Listeners/CreateClients.php @@ -15,7 +15,6 @@ use App\Keycloak\Exception\KeyCloakApiFailed; use App\Keycloak\Realms; use App\Keycloak\Repositories\KeycloakClientRepository; -use App\Keycloak\ScopeConfig; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Psr\Log\LoggerInterface; @@ -30,7 +29,6 @@ public function __construct( private readonly KeycloakClientRepository $keycloakClientRepository, private readonly Realms $realms, private readonly ApiClient $client, - private readonly ScopeConfig $scopeConfig, private readonly LoggerInterface $logger ) { } @@ -75,14 +73,15 @@ private function handle(IntegrationCreated|MissingClientsDetected $event, Realms private function createClientsInKeycloak(Integration $integration, Realms $realms): Clients { - $scopeId = $this->scopeConfig->getScopeIdFromIntegrationType($integration); - $clientCollection = new Clients(); foreach ($realms as $realm) { try { $client = $this->client->createClient($realm, $integration, new ClientIdUuidStrategy()); - $this->client->addScopeToClient($client, $scopeId); + $this->client->addScopeToClient( + $client, + $realm->scopeConfig->getScopeIdFromIntegrationType($integration) + ); $clientCollection->add($client); } catch (KeyCloakApiFailed $e) { diff --git a/app/Keycloak/Listeners/UpdateClients.php b/app/Keycloak/Listeners/UpdateClients.php index 425d5d2b1..6aa6e4f1a 100644 --- a/app/Keycloak/Listeners/UpdateClients.php +++ b/app/Keycloak/Listeners/UpdateClients.php @@ -14,7 +14,6 @@ use App\Keycloak\Client\ApiClient; use App\Keycloak\Exception\KeyCloakApiFailed; use App\Keycloak\Repositories\KeycloakClientRepository; -use App\Keycloak\ScopeConfig; use App\Keycloak\Converters\IntegrationToKeycloakClientConverter; use App\Keycloak\Converters\IntegrationUrlConverter; use Illuminate\Bus\Queueable; @@ -31,7 +30,6 @@ public function __construct( private readonly IntegrationRepository $integrationRepository, private readonly KeycloakClientRepository $keycloakClientRepository, private readonly ApiClient $client, - private readonly ScopeConfig $scopeConfig, private readonly LoggerInterface $logger ) { } @@ -40,11 +38,14 @@ public function handle(IntegrationUpdated|IntegrationUrlCreated|IntegrationUrlUp { $integration = $this->integrationRepository->getById($event->id); $keycloakClients = $this->keycloakClientRepository->getByIntegrationId($event->id); - $scopeId = $this->scopeConfig->getScopeIdFromIntegrationType($integration); foreach ($keycloakClients as $keycloakClient) { try { - $this->updateClient($integration, $keycloakClient, $scopeId); + $this->updateClient( + $integration, + $keycloakClient, + $keycloakClient->getRealm()->scopeConfig->getScopeIdFromIntegrationType($integration) + ); } catch (KeyCloakApiFailed $e) { $this->failed($event, $e); } diff --git a/app/Keycloak/Realm.php b/app/Keycloak/Realm.php index eb54dad2a..cca90886e 100644 --- a/app/Keycloak/Realm.php +++ b/app/Keycloak/Realm.php @@ -16,7 +16,8 @@ public function __construct( string $baseUrl, public string $clientId, public string $clientSecret, - public Environment $environment + public Environment $environment, + public ScopeConfig $scopeConfig, ) { $this->baseUrl = $this->addTrailingSlash($baseUrl); } @@ -29,7 +30,8 @@ public function getMasterRealm(): self $this->baseUrl, $this->clientId, $this->clientSecret, - $this->environment + $this->environment, + $this->scopeConfig, ); } diff --git a/app/Keycloak/Realms.php b/app/Keycloak/Realms.php index 888be0539..acb78180a 100644 --- a/app/Keycloak/Realms.php +++ b/app/Keycloak/Realms.php @@ -6,6 +6,7 @@ use App\Domain\Integrations\Environment; use Illuminate\Support\Collection; +use Ramsey\Uuid\Uuid; /** * @extends Collection @@ -23,7 +24,13 @@ public static function build(): self $environment['base_url'], $environment['client_id'], $environment['client_secret'], - Environment::from($publicName) + Environment::from($publicName), + new ScopeConfig( + Uuid::fromString($environment['scope']['search_api_id']), + Uuid::fromString($environment['scope']['entry_api_id']), + Uuid::fromString($environment['scope']['widgets_id']), + Uuid::fromString($environment['scope']['uitpas_id']) + ) )); } diff --git a/config/keycloak.php b/config/keycloak.php index fa7879c41..ba4d878b3 100644 --- a/config/keycloak.php +++ b/config/keycloak.php @@ -10,24 +10,36 @@ 'base_url' => env('KEYCLOAK_ACC_BASE_URL', ''), 'client_id' => env('KEYCLOAK_ACC_CLIENT_ID', ''), 'client_secret' => env('KEYCLOAK_ACC_CLIENT_SECRET', ''), + 'scope' => [ + 'search_api_id' => env('KEYCLOAK_ACC_SCOPE_SEARCH_API_ID', ''), + 'entry_api_id' => env('KEYCLOAK_ACC_SCOPE_ENTRY_API_ID', ''), + 'widgets_id' => env('KEYCLOAK_ACC_SCOPE_WIDGETS_ID', ''), + 'uitpas_id' => env('KEYCLOAK_ACC_SCOPE_UITPAS_ID', ''), + ], ], 'test' => [ 'internalName' => env('KEYCLOAK_TEST_REALM_NAME', ''), 'base_url' => env('KEYCLOAK_TEST_BASE_URL', ''), 'client_id' => env('KEYCLOAK_TEST_CLIENT_ID', ''), 'client_secret' => env('KEYCLOAK_TEST_CLIENT_SECRET', ''), + 'scope' => [ + 'search_api_id' => env('KEYCLOAK_TEST_SCOPE_SEARCH_API_ID', ''), + 'entry_api_id' => env('KEYCLOAK_TEST_SCOPE_ENTRY_API_ID', ''), + 'widgets_id' => env('KEYCLOAK_TEST_SCOPE_WIDGETS_ID', ''), + 'uitpas_id' => env('KEYCLOAK_TEST_SCOPE_UITPAS_ID', ''), + ], ], 'prod' => [ 'internalName' => env('KEYCLOAK_PROD_REALM_NAME', ''), 'base_url' => env('KEYCLOAK_PROD_BASE_URL', ''), 'client_id' => env('KEYCLOAK_PROD_CLIENT_ID', ''), 'client_secret' => env('KEYCLOAK_PROD_CLIENT_SECRET', ''), + 'scope' => [ + 'search_api_id' => env('KEYCLOAK_PROD_SCOPE_SEARCH_API_ID', ''), + 'entry_api_id' => env('KEYCLOAK_PROD_SCOPE_ENTRY_API_ID', ''), + 'widgets_id' => env('KEYCLOAK_PROD_SCOPE_WIDGETS_ID', ''), + 'uitpas_id' => env('KEYCLOAK_PROD_SCOPE_UITPAS_ID', ''), + ], ], ], - 'scope' => [ - 'search_api_id' => env('KEYCLOAK_SCOPE_SEARCH_API_ID', ''), - 'entry_api_id' => env('KEYCLOAK_SCOPE_ENTRY_API_ID', ''), - 'widgets_id' => env('KEYCLOAK_SCOPE_WIDGETS_ID', ''), - 'uitpas_id' => env('KEYCLOAK_SCOPE_UITPAS_ID', ''), - ], ]; diff --git a/tests/Keycloak/Client/KeycloakApiClientTest.php b/tests/Keycloak/Client/KeycloakApiClientTest.php index d54065875..f6bbbd6a2 100644 --- a/tests/Keycloak/Client/KeycloakApiClientTest.php +++ b/tests/Keycloak/Client/KeycloakApiClientTest.php @@ -12,7 +12,6 @@ use App\Keycloak\ClientId\ClientIdUuidStrategy; use App\Keycloak\Exception\KeyCloakApiFailed; use App\Keycloak\Realm; -use App\Keycloak\ScopeConfig; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\MockObject\MockObject; @@ -37,7 +36,6 @@ final class KeycloakApiClientTest extends TestCase private Realm $realm; private Integration $integration; private LoggerInterface&MockObject $logger; - private ScopeConfig $scopeConfig; protected function setUp(): void { @@ -47,12 +45,6 @@ protected function setUp(): void $this->integration = $this->givenThereIsAnIntegration(Uuid::fromString(self::INTEGRATION_ID)); $this->logger = $this->createMock(LoggerInterface::class); - $this->scopeConfig = new ScopeConfig( - Uuid::fromString('824c09c0-2f3a-4fa0-bde2-8bf25c9a5b74'), - Uuid::fromString('d8a54568-26da-412b-a441-d5e2fad84478'), - Uuid::fromString('123ae05d-1c41-40c8-8716-c4654a3bfd98'), - Uuid::fromString('0743b1c7-0ea2-46af-906e-fbb6c0317514'), - ); } public function test_can_create_client(): void @@ -76,7 +68,6 @@ public function test_can_create_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - $this->scopeConfig, $this->logger ); @@ -112,7 +103,6 @@ public function test_fails_to_create_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - $this->scopeConfig, $this->logger ); @@ -137,7 +127,6 @@ public function test_fails_to_add_scope_to_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - $this->scopeConfig, $this->logger ); @@ -162,7 +151,6 @@ public function test_fetch_is_client_enabled(bool $enabled): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - $this->scopeConfig, $this->logger ); @@ -188,7 +176,6 @@ public function test_update_client_throws_exception_when_api_call_fails(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - $this->scopeConfig, $this->logger ); @@ -209,7 +196,6 @@ public function test_reset_scopes_throws_exception_when_api_call_fails(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - $this->scopeConfig, $this->logger ); diff --git a/tests/Keycloak/Listeners/CreateClientsTest.php b/tests/Keycloak/Listeners/CreateClientsTest.php index b6a53c6ac..191c05b92 100644 --- a/tests/Keycloak/Listeners/CreateClientsTest.php +++ b/tests/Keycloak/Listeners/CreateClientsTest.php @@ -16,7 +16,6 @@ use App\Keycloak\Realm; use App\Keycloak\Realms; use App\Keycloak\Repositories\KeycloakClientRepository; -use App\Keycloak\ScopeConfig; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Ramsey\Uuid\Uuid; @@ -32,7 +31,6 @@ final class CreateClientsTest extends TestCase use RealmFactory; private const SECRET = 'my-secret'; - private const SEARCH_SCOPE_ID = '06059529-74b5-422a-a499-ffcaf065d437'; private Integration $integration; private CreateClients $handler; @@ -54,13 +52,6 @@ protected function setUp(): void $this->keycloakClientRepository = $this->createMock(KeycloakClientRepository::class); $this->apiClient = $this->createMock(ApiClient::class); - $scopeConfig = new ScopeConfig( - Uuid::fromString(self::SEARCH_SCOPE_ID), - Uuid::fromString('d8a54568-26da-412b-a441-d5e2fad84478'), - Uuid::fromString('123ae05d-1c41-40c8-8716-c4654a3bfd98'), - Uuid::fromString('0743b1c7-0ea2-46af-906e-fbb6c0317514'), - ); - $this->realms = $this->givenAllRealms(); $this->handler = new CreateClients( @@ -68,7 +59,6 @@ protected function setUp(): void $this->keycloakClientRepository, $this->realms, $this->apiClient, - $scopeConfig, $this->logger, ); } diff --git a/tests/Keycloak/Listeners/UpdateClientsTest.php b/tests/Keycloak/Listeners/UpdateClientsTest.php index bf48084ce..99e7508ef 100644 --- a/tests/Keycloak/Listeners/UpdateClientsTest.php +++ b/tests/Keycloak/Listeners/UpdateClientsTest.php @@ -15,7 +15,6 @@ use App\Keycloak\Listeners\UpdateClients; use App\Keycloak\Realms; use App\Keycloak\Repositories\KeycloakClientRepository; -use App\Keycloak\ScopeConfig; use App\Keycloak\Converters\IntegrationToKeycloakClientConverter; use App\Keycloak\Converters\IntegrationUrlConverter; use PHPUnit\Framework\MockObject\MockObject; @@ -35,10 +34,8 @@ final class UpdateClientsTest extends TestCase use RealmFactory; private const SECRET = 'my-secret'; - private const SEARCH_SCOPE_ID = '06059529-74b5-422a-a499-ffcaf065d437'; private Integration $integration; - private ScopeConfig $scopeConfig; private ApiClient&MockObject $apiClient; private LoggerInterface&MockObject $logger; private IntegrationRepository&MockObject $integrationRepository; @@ -59,13 +56,6 @@ protected function setUp(): void new IntegrationUrl(Uuid::uuid4(), $this->integration->id, Environment::Acceptance, IntegrationUrlType::Logout, 'https://example.com/logout2'), ); - $this->scopeConfig = new ScopeConfig( - Uuid::fromString(self::SEARCH_SCOPE_ID), - Uuid::fromString('d8a54568-26da-412b-a441-d5e2fad84478'), - Uuid::fromString('123ae05d-1c41-40c8-8716-c4654a3bfd98'), - Uuid::fromString('0743b1c7-0ea2-46af-906e-fbb6c0317514'), - ); - $this->apiClient = $this->createMock(ApiClient::class); $this->logger = $this->createMock(LoggerInterface::class); $this->integrationRepository = $this->createMock(IntegrationRepository::class); @@ -140,7 +130,6 @@ public function test_update_client_for_integration(): void $this->integrationRepository, $keycloakClientRepository, $this->apiClient, - $this->scopeConfig, $this->logger ); diff --git a/tests/Keycloak/RealmFactory.php b/tests/Keycloak/RealmFactory.php index 8a6c5ed6f..b3b3dcb8d 100644 --- a/tests/Keycloak/RealmFactory.php +++ b/tests/Keycloak/RealmFactory.php @@ -7,9 +7,16 @@ use App\Domain\Integrations\Environment; use App\Keycloak\Realm; use App\Keycloak\Realms; +use App\Keycloak\ScopeConfig; +use Ramsey\Uuid\Uuid; trait RealmFactory { + protected const SEARCH_SCOPE_ID = '06059529-74b5-422a-a499-ffcaf065d437'; + protected const ENTRY_SCOPE_ID = 'd8a54568-26da-412b-a441-d5e2fad84478'; + protected const WIDGET_SCOPE_ID = '123ae05d-1c41-40c8-8716-c4654a3bfd98'; + protected const UITPAS_SCOPE_ID = '0743b1c7-0ea2-46af-906e-fbb6c0317514'; + public function givenAllRealms(): Realms { return new Realms([ @@ -28,6 +35,7 @@ public function givenAcceptanceRealm(): Realm 'php_client', 'dfgopopzjcvijogdrg', Environment::Acceptance, + $this->getScopeConfig(), ); } @@ -40,6 +48,7 @@ public function givenTestRealm(): Realm 'php_client', 'dfgopopzjcvijogdrg', Environment::Testing, + $this->getScopeConfig(), ); } @@ -52,7 +61,17 @@ public function givenProductionRealm(): Realm 'php_client', 'dfgopopzjcvijogdrg', Environment::Production, + $this->getScopeConfig(), ); } + private function getScopeConfig(): ScopeConfig + { + return new ScopeConfig( + Uuid::fromString(self::SEARCH_SCOPE_ID), + Uuid::fromString(self::ENTRY_SCOPE_ID), + Uuid::fromString(self::WIDGET_SCOPE_ID), + Uuid::fromString(self::UITPAS_SCOPE_ID), + ); + } } diff --git a/tests/Keycloak/RealmsTest.php b/tests/Keycloak/RealmsTest.php new file mode 100644 index 000000000..d5145922f --- /dev/null +++ b/tests/Keycloak/RealmsTest.php @@ -0,0 +1,87 @@ +value => [ + 'search_api_id' => Uuid::uuid4()->toString(), + 'entry_api_id' => Uuid::uuid4()->toString(), + 'widgets_id' => Uuid::uuid4()->toString(), + 'uitpas_id' => Uuid::uuid4()->toString(), + ], + Environment::Testing->value => [ + 'search_api_id' => Uuid::uuid4()->toString(), + 'entry_api_id' => Uuid::uuid4()->toString(), + 'widgets_id' => Uuid::uuid4()->toString(), + 'uitpas_id' => Uuid::uuid4()->toString(), + ], + Environment::Production->value => [ + 'search_api_id' => Uuid::uuid4()->toString(), + 'entry_api_id' => Uuid::uuid4()->toString(), + 'widgets_id' => Uuid::uuid4()->toString(), + 'uitpas_id' => Uuid::uuid4()->toString(), + ], + ]; + + // Arrange + Config::set('keycloak.environments', [ + Environment::Acceptance->value => [ + 'internalName' => 'internal_name_0', + 'base_url' => 'https://publiq-platform.be/0', + 'client_id' => 'client_id_0', + 'client_secret' => 'client_secret_0', + 'scope' => $scopes[Environment::Acceptance->value], + ], + Environment::Testing->value => [ + 'internalName' => 'internal_name_1', + 'base_url' => 'https://publiq-platform.be/1', + 'client_id' => 'client_id_1', + 'client_secret' => 'client_secret_1', + 'scope' => $scopes[Environment::Testing->value], + ], + Environment::Production->value => [ + 'internalName' => 'internal_name_2', + 'base_url' => 'https://publiq-platform.be/2', + 'client_id' => 'client_id_2', + 'client_secret' => 'client_secret_2', + 'scope' => $scopes[Environment::Production->value], + ], + ]); + + $realms = Realms::build(); + + $this->assertCount(3, $realms); + + foreach (Environment::cases() as $i => $environment) { + $realm = $realms->get($i); + + $this->assertInstanceOf(Realm::class, $realm); + $this->assertEquals('internal_name_' . $i, $realm->internalName); + $this->assertEquals(ucfirst($environment->value), $realm->publicName); + $this->assertEquals('https://publiq-platform.be/' . $i . '/', $realm->baseUrl); // This tests if a trailing slash is added + $this->assertEquals('client_id_' . $i, $realm->clientId); + $this->assertEquals('client_secret_' . $i, $realm->clientSecret); + $this->assertEquals($environment, $realm->environment); + + $this->assertInstanceOf(ScopeConfig::class, $realm->scopeConfig); + $this->assertEquals($scopes[$environment->value]['search_api_id'], $realm->scopeConfig->searchApiScopeId->toString()); + $this->assertEquals($scopes[$environment->value]['entry_api_id'], $realm->scopeConfig->entryApiScopeId->toString()); + $this->assertEquals($scopes[$environment->value]['widgets_id'], $realm->scopeConfig->widgetScopeId->toString()); + $this->assertEquals($scopes[$environment->value]['uitpas_id'], $realm->scopeConfig->uitpasScopeId->toString()); + } + } +} From 66b6964f6ce90683f7e8795ebd6ed80b4978c8ff Mon Sep 17 00:00:00 2001 From: Koen Eelen Date: Mon, 10 Jun 2024 12:25:33 +0200 Subject: [PATCH 2/5] update .env.ci with new config --- .env.ci | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/.env.ci b/.env.ci index 46297d2ff..1f13a72a7 100644 --- a/.env.ci +++ b/.env.ci @@ -153,25 +153,37 @@ VITE_UITPAS_INTEGRATION_TYPE_ENABLED=${UITPAS_INTEGRATION_TYPE_ENABLED} KEYCLOAK_ENABLED=true KEYCLOAK_ACC_BASE_URL='https://account.kcpoc.lodgon.com/' -KEYCLOAK_ACC_REALM_NAME='uitidpoc' +KEYCLOAK_ACC_REALM_NAME='myrealm' KEYCLOAK_ACC_CLIENT_ID='php_client' -KEYCLOAK_ACC_CLIENT_SECRET='xxx' +KEYCLOAK_ACC_CLIENT_SECRET='super-secret' + +# Incorrect values, but need to contain a valid UUID formatted string +KEYCLOAK_ACC_SCOPE_SEARCH_API_ID='06059529-74b5-422a-a499-ffcaf065d437' #publiq-api-sapi-scope +KEYCLOAK_ACC_SCOPE_ENTRY_API_ID='d8a54568-26da-412b-a441-d5e2fad84478' #publiq-api-entry-scope +KEYCLOAK_ACC_SCOPE_WIDGETS_ID='123ae05d-1c41-40c8-8716-c4654a3bfd98'#publiq-widget-scope +KEYCLOAK_ACC_SCOPE_UITPAS_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e'#uitpas-scope KEYCLOAK_TEST_BASE_URL='https://account.kcpoc.lodgon.com/' -KEYCLOAK_TEST_REALM_NAME='uitidpoc' +KEYCLOAK_TEST_REALM_NAME='myrealm' KEYCLOAK_TEST_CLIENT_ID='php_client' -KEYCLOAK_TEST_CLIENT_SECRET='xxx' +KEYCLOAK_TEST_CLIENT_SECRET='super-secret' + +# Incorrect values, but need to contain a valid UUID formatted string +KEYCLOAK_TEST_SCOPE_SEARCH_API_ID='06059529-74b5-422a-a499-ffcaf065d437' #publiq-api-sapi-scope +KEYCLOAK_TEST_SCOPE_ENTRY_API_ID='d8a54568-26da-412b-a441-d5e2fad84478' #publiq-api-entry-scope +KEYCLOAK_TEST_SCOPE_WIDGETS_ID='123ae05d-1c41-40c8-8716-c4654a3bfd98'#publiq-widget-scope +KEYCLOAK_TEST_SCOPE_UITPAS_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e'#uitpas-scope KEYCLOAK_PROD_BASE_URL='https://account.kcpoc.lodgon.com/' -KEYCLOAK_PROD_REALM_NAME='uitidpoc' +KEYCLOAK_PROD_REALM_NAME='myrealm' KEYCLOAK_PROD_CLIENT_ID='php_client' -KEYCLOAK_PROD_CLIENT_SECRET='xxx' +KEYCLOAK_PROD_CLIENT_SECRET='super-secret' # Incorrect values, but need to contain a valid UUID formatted string -KEYCLOAK_SCOPE_SEARCH_API_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e' #publiq-api-sapi-scope -KEYCLOAK_SCOPE_ENTRY_API_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e' #publiq-api-entry-scope -KEYCLOAK_SCOPE_WIDGETS_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e'#publiq-widget-scope -KEYCLOAK_SCOPE_UITPAS_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e'#uitpas-scope +KEYCLOAK_PROD_SCOPE_SEARCH_API_ID='06059529-74b5-422a-a499-ffcaf065d437' #publiq-api-sapi-scope +KEYCLOAK_PROD_SCOPE_ENTRY_API_ID='d8a54568-26da-412b-a441-d5e2fad84478' #publiq-api-entry-scope +KEYCLOAK_PROD_SCOPE_WIDGETS_ID='123ae05d-1c41-40c8-8716-c4654a3bfd98'#publiq-widget-scope +KEYCLOAK_PROD_SCOPE_UITPAS_ID='bcfb28cc-454f-488a-b080-6a29d9c0158e'#uitpas-scope SEARCH_BASE_URI=https://search-acc.uitdatabank.be/ SEARCH_API_KEY= From 118868fb836b39ff63207f1b16f59b20ec87a935 Mon Sep 17 00:00:00 2001 From: Koen Eelen Date: Tue, 11 Jun 2024 14:56:30 +0200 Subject: [PATCH 3/5] Remove getRealm() --- app/Keycloak/Client.php | 22 ++----------- app/Keycloak/Client/KeycloakApiClient.php | 32 ++++++++++++------- app/Keycloak/KeycloakServiceProvider.php | 5 +-- app/Keycloak/Listeners/BlockClients.php | 2 +- app/Keycloak/Listeners/CreateClients.php | 2 +- app/Keycloak/Listeners/UpdateClients.php | 5 ++- app/Keycloak/Realms.php | 27 ++++++++++++++++ .../EloquentKeycloakClientRepository.php | 3 +- .../Keycloak/Client/KeycloakApiClientTest.php | 7 ++++ tests/Keycloak/Listeners/BlockClientsTest.php | 2 +- .../Keycloak/Listeners/CreateClientsTest.php | 4 +-- .../EloquentKeycloakClientRepositoryTest.php | 8 +++-- 12 files changed, 74 insertions(+), 45 deletions(-) diff --git a/app/Keycloak/Client.php b/app/Keycloak/Client.php index eea7a64f3..1ec366279 100644 --- a/app/Keycloak/Client.php +++ b/app/Keycloak/Client.php @@ -5,7 +5,6 @@ namespace App\Keycloak; use App\Domain\Integrations\Environment; -use Illuminate\Support\Facades\App; use InvalidArgumentException; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; @@ -41,24 +40,7 @@ public static function createFromJson( public function getKeycloakUrl(): string { - $baseUrl = $this->getRealm()->baseUrl; - - return $baseUrl . 'admin/master/console/#/' . $this->getRealm()->internalName . '/clients/' . $this->id->toString() . '/settings'; - } - - public function getRealm(): Realm - { - /** @var Realms $realmCollection */ - $realmCollection = App::get(Realms::class); - - foreach ($realmCollection as $realm) { - if ($realm->environment === $this->environment) { - return $realm; - } - } - - throw new InvalidArgumentException( - sprintf('Could not convert environment %s to realm:', $this->environment->value) - ); + $realm = Realms::getInstance()->getRealmByEnvironment($this->environment); + return $realm->baseUrl . 'admin/master/console/#/' . $realm->internalName . '/clients/' . $this->id->toString() . '/settings'; } } diff --git a/app/Keycloak/Client/KeycloakApiClient.php b/app/Keycloak/Client/KeycloakApiClient.php index 2421ca3fc..c51651bc0 100644 --- a/app/Keycloak/Client/KeycloakApiClient.php +++ b/app/Keycloak/Client/KeycloakApiClient.php @@ -11,6 +11,7 @@ use App\Keycloak\Converters\IntegrationToKeycloakClientConverter; use App\Keycloak\Exception\KeyCloakApiFailed; use App\Keycloak\Realm; +use App\Keycloak\Realms; use GuzzleHttp\Psr7\Request; use Psr\Log\LoggerInterface; use Ramsey\Uuid\Uuid; @@ -21,6 +22,7 @@ { public function __construct( private KeycloakHttpClient $client, + private Realms $realms, private LoggerInterface $logger, ) { } @@ -68,13 +70,15 @@ public function createClient( */ public function addScopeToClient(Client $client, UuidInterface $scopeId): void { + $realm = $this->realms->getRealmByEnvironment($client->environment); + try { $response = $this->client->sendWithBearer( new Request( 'PUT', - sprintf('admin/realms/%s/clients/%s/default-client-scopes/%s', $client->getRealm()->internalName, $client->id->toString(), $scopeId->toString()) + sprintf('admin/realms/%s/clients/%s/default-client-scopes/%s', $realm->internalName, $client->id->toString(), $scopeId->toString()) ), - $client->getRealm() + $realm ); } catch (Throwable $e) { throw KeyCloakApiFailed::failedToAddScopeToClient($e->getMessage()); @@ -87,14 +91,16 @@ public function addScopeToClient(Client $client, UuidInterface $scopeId): void public function deleteScopes(Client $client): void { - foreach ($client->getRealm()->scopeConfig->getAll() as $scope) { + $realm = $this->realms->getRealmByEnvironment($client->environment); + + foreach ($realm->scopeConfig->getAll() as $scope) { try { $response = $this->client->sendWithBearer( new Request( 'DELETE', - sprintf('admin/realms/%s/clients/%s/default-client-scopes/%s', $client->getRealm()->internalName, $client->id->toString(), $scope->toString()), + sprintf('admin/realms/%s/clients/%s/default-client-scopes/%s', $realm->internalName, $client->id->toString(), $scope->toString()), ), - $client->getRealm() + $realm ); // Will throw a 404 when scope not attached to client, but this is no problem. @@ -138,26 +144,28 @@ private function fetchClient(Realm $realm, Integration $integration, string $id) */ public function fetchIsClientActive(Client $client): bool { + $realm = $this->realms->getRealmByEnvironment($client->environment); + try { $response = $this->client->sendWithBearer( new Request( 'GET', - sprintf('admin/realms/%s/clients/%s', $client->getRealm()->internalName, $client->id->toString()) + sprintf('admin/realms/%s/clients/%s', $realm->internalName, $client->id->toString()) ), - $client->getRealm() + $realm ); $body = $response->getBody()->getContents(); if (empty($body) || $response->getStatusCode() !== 200) { - throw KeyCloakApiFailed::failedToFetchClient($client->getRealm(), $body); + throw KeyCloakApiFailed::failedToFetchClient($realm, $body); } $data = Json::decodeAssociatively($body); return $data['enabled']; } catch (Throwable $e) { - throw KeyCloakApiFailed::failedToFetchClient($client->getRealm(), $e->getMessage()); + throw KeyCloakApiFailed::failedToFetchClient($realm, $e->getMessage()); } } @@ -182,15 +190,17 @@ public function blockClient(Client $client): void */ public function updateClient(Client $client, array $body): void { + $realm = $this->realms->getRealmByEnvironment($client->environment); + try { $response = $this->client->sendWithBearer( new Request( 'PUT', - 'admin/realms/' . $client->getRealm()->internalName . '/clients/' . $client->id->toString(), + 'admin/realms/' . $realm->internalName . '/clients/' . $client->id->toString(), [], Json::encode($body) ), - $client->getRealm() + $realm ); if ($response->getStatusCode() !== 204) { diff --git a/app/Keycloak/KeycloakServiceProvider.php b/app/Keycloak/KeycloakServiceProvider.php index 68b61de15..375a5ebc1 100644 --- a/app/Keycloak/KeycloakServiceProvider.php +++ b/app/Keycloak/KeycloakServiceProvider.php @@ -39,6 +39,7 @@ public function register(): void $this->app->get(LoggerInterface::class), ) ), + Realms::getInstance(), $this->app->get(LoggerInterface::class), ); }); @@ -47,10 +48,6 @@ public function register(): void return $this->app->get(EloquentKeycloakClientRepository::class); }); - $this->app->singleton(Realms::class, function () { - return Realms::build(); - }); - $this->app->singleton(CachedKeycloakClientStatus::class, function () { return new CachedKeycloakClientStatus( App::get(ApiClient::class), diff --git a/app/Keycloak/Listeners/BlockClients.php b/app/Keycloak/Listeners/BlockClients.php index 241673feb..8fc802080 100644 --- a/app/Keycloak/Listeners/BlockClients.php +++ b/app/Keycloak/Listeners/BlockClients.php @@ -38,7 +38,7 @@ public function handle(IntegrationBlocked $integrationBlocked): void $this->logger->info('Keycloak client blocked', [ 'integration_id' => $integration->id->toString(), 'client_id' => $keycloakClient->id->toString(), - 'realm' => $keycloakClient->getRealm()->internalName, + 'environment' => $keycloakClient->environment->value, ]); } catch (KeyCloakApiFailed $e) { $this->failed($integrationBlocked, $e); diff --git a/app/Keycloak/Listeners/CreateClients.php b/app/Keycloak/Listeners/CreateClients.php index d97f77036..c466c6273 100644 --- a/app/Keycloak/Listeners/CreateClients.php +++ b/app/Keycloak/Listeners/CreateClients.php @@ -66,7 +66,7 @@ private function handle(IntegrationCreated|MissingClientsDetected $event, Realms $this->logger->info('Keycloak client created', [ 'integration_id' => $event->id->toString(), 'client_id' => $client->id->toString(), - 'realm' => $client->getRealm()->internalName, + 'environment' => $client->environment->value, ]); } } diff --git a/app/Keycloak/Listeners/UpdateClients.php b/app/Keycloak/Listeners/UpdateClients.php index 6aa6e4f1a..c239f7a2c 100644 --- a/app/Keycloak/Listeners/UpdateClients.php +++ b/app/Keycloak/Listeners/UpdateClients.php @@ -13,6 +13,7 @@ use App\Keycloak\Client; use App\Keycloak\Client\ApiClient; use App\Keycloak\Exception\KeyCloakApiFailed; +use App\Keycloak\Realms; use App\Keycloak\Repositories\KeycloakClientRepository; use App\Keycloak\Converters\IntegrationToKeycloakClientConverter; use App\Keycloak\Converters\IntegrationUrlConverter; @@ -41,10 +42,12 @@ public function handle(IntegrationUpdated|IntegrationUrlCreated|IntegrationUrlUp foreach ($keycloakClients as $keycloakClient) { try { + $realm = Realms::getInstance()->getRealmByEnvironment($keycloakClient->environment); + $this->updateClient( $integration, $keycloakClient, - $keycloakClient->getRealm()->scopeConfig->getScopeIdFromIntegrationType($integration) + $realm->scopeConfig->getScopeIdFromIntegrationType($integration) ); } catch (KeyCloakApiFailed $e) { $this->failed($event, $e); diff --git a/app/Keycloak/Realms.php b/app/Keycloak/Realms.php index acb78180a..84d5d57e3 100644 --- a/app/Keycloak/Realms.php +++ b/app/Keycloak/Realms.php @@ -13,6 +13,22 @@ */ final class Realms extends Collection { + private static ?self $instance = null; + + public function __construct(array $realms=[]) + { + parent::__construct($realms); + } + + public static function getInstance(): self + { + if (self::$instance === null) { + self::$instance = self::build(); + } + + return self::$instance; + } + public static function build(): self { $realms = new self(); @@ -36,4 +52,15 @@ public static function build(): self return $realms; } + + public function getRealmByEnvironment(Environment $environment): Realm + { + foreach ($this->items as $realm) { + if ($realm->environment === $environment) { + return $realm; + } + } + + throw new \InvalidArgumentException(sprintf('Could not determine realm with the provided environment %s', $environment->value)); + } } diff --git a/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php b/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php index bc727624f..29bfed894 100644 --- a/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php +++ b/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php @@ -8,6 +8,7 @@ use App\Domain\Integrations\Environments; use App\Keycloak\Client; use App\Keycloak\Models\KeycloakClientModel; +use App\Keycloak\Realms; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Facades\DB; @@ -30,7 +31,7 @@ public function create(Client ...$clients): void 'integration_id' => $client->integrationId->toString(), 'client_id' => $client->clientId, 'client_secret' => $client->clientSecret, - 'realm' => $client->getRealm()->publicName, + 'realm' => Realms::getInstance()->getRealmByEnvironment($client->environment)->publicName, ] ); } diff --git a/tests/Keycloak/Client/KeycloakApiClientTest.php b/tests/Keycloak/Client/KeycloakApiClientTest.php index f6bbbd6a2..28082f1cb 100644 --- a/tests/Keycloak/Client/KeycloakApiClientTest.php +++ b/tests/Keycloak/Client/KeycloakApiClientTest.php @@ -12,6 +12,7 @@ use App\Keycloak\ClientId\ClientIdUuidStrategy; use App\Keycloak\Exception\KeyCloakApiFailed; use App\Keycloak\Realm; +use App\Keycloak\Realms; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\MockObject\MockObject; @@ -68,6 +69,7 @@ public function test_can_create_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), + Realms::getInstance(), $this->logger ); @@ -103,6 +105,7 @@ public function test_fails_to_create_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), + Realms::getInstance(), $this->logger ); @@ -127,6 +130,7 @@ public function test_fails_to_add_scope_to_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), + Realms::getInstance(), $this->logger ); @@ -151,6 +155,7 @@ public function test_fetch_is_client_enabled(bool $enabled): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), + Realms::getInstance(), $this->logger ); @@ -176,6 +181,7 @@ public function test_update_client_throws_exception_when_api_call_fails(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), + Realms::getInstance(), $this->logger ); @@ -196,6 +202,7 @@ public function test_reset_scopes_throws_exception_when_api_call_fails(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), + Realms::getInstance(), $this->logger ); diff --git a/tests/Keycloak/Listeners/BlockClientsTest.php b/tests/Keycloak/Listeners/BlockClientsTest.php index 76db88e7f..5068dbb5c 100644 --- a/tests/Keycloak/Listeners/BlockClientsTest.php +++ b/tests/Keycloak/Listeners/BlockClientsTest.php @@ -71,7 +71,7 @@ public function test_block_clients_when_integration_is_blocked(): void ->willReturnCallback(function ($message, $options) { $this->assertEquals('Keycloak client blocked', $message); $this->assertArrayHasKey('integration_id', $options); - $this->assertArrayHasKey('realm', $options); + $this->assertArrayHasKey('environment', $options); $this->assertEquals($this->integration->id->toString(), $options['integration_id']); }); diff --git a/tests/Keycloak/Listeners/CreateClientsTest.php b/tests/Keycloak/Listeners/CreateClientsTest.php index 191c05b92..ca23d3300 100644 --- a/tests/Keycloak/Listeners/CreateClientsTest.php +++ b/tests/Keycloak/Listeners/CreateClientsTest.php @@ -111,7 +111,7 @@ function (Realm $realm, Integration $integrationArgument) use ($clients) { ->willReturnCallback(function ($message, $options) { $this->assertEquals('Keycloak client created', $message); $this->assertArrayHasKey('integration_id', $options); - $this->assertArrayHasKey('realm', $options); + $this->assertArrayHasKey('environment', $options); $this->assertEquals($this->integration->id->toString(), $options['integration_id']); }); @@ -191,7 +191,7 @@ function (Realm $realm, Integration $integrationArgument) use ($clients) { ->willReturnCallback(function ($message, $options) { $this->assertEquals('Keycloak client created', $message); $this->assertArrayHasKey('integration_id', $options); - $this->assertArrayHasKey('realm', $options); + $this->assertArrayHasKey('environment', $options); $this->assertEquals($this->integration->id->toString(), $options['integration_id']); }); diff --git a/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php b/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php index 733831f2c..0d668ed4e 100644 --- a/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php +++ b/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php @@ -6,6 +6,7 @@ use App\Domain\Integrations\Environment; use App\Keycloak\Client; +use App\Keycloak\Realm; use App\Keycloak\Realms; use App\Keycloak\Repositories\EloquentKeycloakClientRepository; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -176,10 +177,11 @@ public function test_it_can_get_missing_realms_by_integration_id(): void $integrationId = Uuid::uuid4(); $clients = []; - $missingRealmCollection = new Realms(); - foreach (new Realms([$this->givenAcceptanceRealm()]) as $realm) { - $missingRealmCollection->add($realm); + $missingRealmCollection = Realms::getInstance()->filter(function (Realm $realm) { + return $realm->environment === Environment::Acceptance; + }); + foreach ($missingRealmCollection as $realm) { $clients[] = new Client( Uuid::uuid4(), $integrationId, From 277cd034b346faca73be840f2194b5873668eb3f Mon Sep 17 00:00:00 2001 From: Koen Eelen Date: Wed, 12 Jun 2024 10:25:36 +0200 Subject: [PATCH 4/5] Put realm back in Service Container layer --- app/Keycloak/Client.php | 6 ------ app/Keycloak/KeycloakServiceProvider.php | 8 ++++++-- app/Keycloak/Listeners/UpdateClients.php | 3 ++- app/Keycloak/Realms.php | 11 ----------- .../EloquentKeycloakClientRepository.php | 8 ++++++-- tests/Keycloak/Client/KeycloakApiClientTest.php | 13 ++++++------- tests/Keycloak/Listeners/UpdateClientsTest.php | 3 ++- .../EloquentKeycloakClientRepositoryTest.php | 8 ++------ 8 files changed, 24 insertions(+), 36 deletions(-) diff --git a/app/Keycloak/Client.php b/app/Keycloak/Client.php index 1ec366279..d15b6f89b 100644 --- a/app/Keycloak/Client.php +++ b/app/Keycloak/Client.php @@ -37,10 +37,4 @@ public static function createFromJson( $realm->environment, ); } - - public function getKeycloakUrl(): string - { - $realm = Realms::getInstance()->getRealmByEnvironment($this->environment); - return $realm->baseUrl . 'admin/master/console/#/' . $realm->internalName . '/clients/' . $this->id->toString() . '/settings'; - } } diff --git a/app/Keycloak/KeycloakServiceProvider.php b/app/Keycloak/KeycloakServiceProvider.php index 375a5ebc1..fcd13188d 100644 --- a/app/Keycloak/KeycloakServiceProvider.php +++ b/app/Keycloak/KeycloakServiceProvider.php @@ -39,13 +39,17 @@ public function register(): void $this->app->get(LoggerInterface::class), ) ), - Realms::getInstance(), + $this->app->get(Realms::class), $this->app->get(LoggerInterface::class), ); }); + $this->app->singleton(Realms::class, function () { + return Realms::build(); + }); + $this->app->singleton(KeycloakClientRepository::class, function () { - return $this->app->get(EloquentKeycloakClientRepository::class); + return new EloquentKeycloakClientRepository($this->app->get(Realms::class)); }); $this->app->singleton(CachedKeycloakClientStatus::class, function () { diff --git a/app/Keycloak/Listeners/UpdateClients.php b/app/Keycloak/Listeners/UpdateClients.php index c239f7a2c..6ff8bfdf2 100644 --- a/app/Keycloak/Listeners/UpdateClients.php +++ b/app/Keycloak/Listeners/UpdateClients.php @@ -31,6 +31,7 @@ public function __construct( private readonly IntegrationRepository $integrationRepository, private readonly KeycloakClientRepository $keycloakClientRepository, private readonly ApiClient $client, + private readonly Realms $realms, private readonly LoggerInterface $logger ) { } @@ -42,7 +43,7 @@ public function handle(IntegrationUpdated|IntegrationUrlCreated|IntegrationUrlUp foreach ($keycloakClients as $keycloakClient) { try { - $realm = Realms::getInstance()->getRealmByEnvironment($keycloakClient->environment); + $realm = $this->realms->getRealmByEnvironment($keycloakClient->environment); $this->updateClient( $integration, diff --git a/app/Keycloak/Realms.php b/app/Keycloak/Realms.php index 84d5d57e3..5e8781856 100644 --- a/app/Keycloak/Realms.php +++ b/app/Keycloak/Realms.php @@ -13,22 +13,11 @@ */ final class Realms extends Collection { - private static ?self $instance = null; - public function __construct(array $realms=[]) { parent::__construct($realms); } - public static function getInstance(): self - { - if (self::$instance === null) { - self::$instance = self::build(); - } - - return self::$instance; - } - public static function build(): self { $realms = new self(); diff --git a/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php b/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php index 29bfed894..78229ec31 100644 --- a/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php +++ b/app/Keycloak/Repositories/EloquentKeycloakClientRepository.php @@ -16,13 +16,17 @@ final class EloquentKeycloakClientRepository implements KeycloakClientRepository { + public function __construct(private readonly Realms $realms) + { + } + public function create(Client ...$clients): void { if (count($clients) === 0) { return; } - DB::transaction(static function () use ($clients) { + DB::transaction(function () use ($clients) { foreach ($clients as $client) { KeycloakClientModel::query() ->create( @@ -31,7 +35,7 @@ public function create(Client ...$clients): void 'integration_id' => $client->integrationId->toString(), 'client_id' => $client->clientId, 'client_secret' => $client->clientSecret, - 'realm' => Realms::getInstance()->getRealmByEnvironment($client->environment)->publicName, + 'realm' => $this->realms->getRealmByEnvironment($client->environment)->publicName, ] ); } diff --git a/tests/Keycloak/Client/KeycloakApiClientTest.php b/tests/Keycloak/Client/KeycloakApiClientTest.php index 28082f1cb..11c7f71f3 100644 --- a/tests/Keycloak/Client/KeycloakApiClientTest.php +++ b/tests/Keycloak/Client/KeycloakApiClientTest.php @@ -12,7 +12,6 @@ use App\Keycloak\ClientId\ClientIdUuidStrategy; use App\Keycloak\Exception\KeyCloakApiFailed; use App\Keycloak\Realm; -use App\Keycloak\Realms; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\MockObject\MockObject; @@ -69,7 +68,7 @@ public function test_can_create_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - Realms::getInstance(), + $this->givenAllRealms(), $this->logger ); @@ -105,7 +104,7 @@ public function test_fails_to_create_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - Realms::getInstance(), + $this->givenAllRealms(), $this->logger ); @@ -130,7 +129,7 @@ public function test_fails_to_add_scope_to_client(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - Realms::getInstance(), + $this->givenAllRealms(), $this->logger ); @@ -155,7 +154,7 @@ public function test_fetch_is_client_enabled(bool $enabled): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - Realms::getInstance(), + $this->givenAllRealms(), $this->logger ); @@ -181,7 +180,7 @@ public function test_update_client_throws_exception_when_api_call_fails(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - Realms::getInstance(), + $this->givenAllRealms(), $this->logger ); @@ -202,7 +201,7 @@ public function test_reset_scopes_throws_exception_when_api_call_fails(): void $apiClient = new KeycloakApiClient( $this->givenKeycloakHttpClient($this->logger, $mock), - Realms::getInstance(), + $this->givenAllRealms(), $this->logger ); diff --git a/tests/Keycloak/Listeners/UpdateClientsTest.php b/tests/Keycloak/Listeners/UpdateClientsTest.php index 99e7508ef..c61990081 100644 --- a/tests/Keycloak/Listeners/UpdateClientsTest.php +++ b/tests/Keycloak/Listeners/UpdateClientsTest.php @@ -130,7 +130,8 @@ public function test_update_client_for_integration(): void $this->integrationRepository, $keycloakClientRepository, $this->apiClient, - $this->logger + $this->realms, + $this->logger, ); $updateClients->handle(new IntegrationUpdated($this->integration->id)); diff --git a/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php b/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php index 0d668ed4e..a2a14fc0b 100644 --- a/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php +++ b/tests/Keycloak/Repositories/EloquentKeycloakClientRepositoryTest.php @@ -6,7 +6,6 @@ use App\Domain\Integrations\Environment; use App\Keycloak\Client; -use App\Keycloak\Realm; use App\Keycloak\Realms; use App\Keycloak\Repositories\EloquentKeycloakClientRepository; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -27,8 +26,8 @@ protected function setUp(): void { parent::setUp(); - $this->repository = new EloquentKeycloakClientRepository(); $this->realms = $this->givenAllRealms(); + $this->repository = new EloquentKeycloakClientRepository($this->realms); } public function test_it_can_save_one_or_more_clients(): void @@ -177,10 +176,7 @@ public function test_it_can_get_missing_realms_by_integration_id(): void $integrationId = Uuid::uuid4(); $clients = []; - $missingRealmCollection = Realms::getInstance()->filter(function (Realm $realm) { - return $realm->environment === Environment::Acceptance; - }); - + $missingRealmCollection = new Realms([$this->givenAcceptanceRealm()]); foreach ($missingRealmCollection as $realm) { $clients[] = new Client( Uuid::uuid4(), From 8462a2f851f2d1bc46793f13359be954b5cd128b Mon Sep 17 00:00:00 2001 From: Koen Eelen Date: Wed, 12 Jun 2024 11:00:27 +0200 Subject: [PATCH 5/5] Move generateUrl inside KeycloakClient --- app/Nova/Resources/KeycloakClient.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/Nova/Resources/KeycloakClient.php b/app/Nova/Resources/KeycloakClient.php index accee8e65..8e739c421 100644 --- a/app/Nova/Resources/KeycloakClient.php +++ b/app/Nova/Resources/KeycloakClient.php @@ -7,6 +7,7 @@ use App\Domain\Integrations\Environment; use App\Keycloak\CachedKeycloakClientStatus; use App\Keycloak\Models\KeycloakClientModel; +use App\Keycloak\Realms; use App\Nova\ActionGuards\ActionGuard; use App\Nova\ActionGuards\Keycloak\BlockKeycloakClientGuard; use App\Nova\ActionGuards\Keycloak\UnblockKeycloakClientGuard; @@ -83,7 +84,11 @@ public function fields(NovaRequest $request): array Text::make('client_secret') ->readonly(), Text::make('Open', function (KeycloakClientModel $model) { - return sprintf('Open in Keycloak', $model->toDomain()->getKeycloakUrl()); + $client = $model->toDomain(); + $realm = App::get(Realms::class)->getRealmByEnvironment($client->environment); + $url = $realm->baseUrl . 'admin/master/console/#/' . $realm->internalName . '/clients/' . $client->id->toString() . '/settings'; + + return sprintf('Open in Keycloak', $url); })->asHtml(), ]; }