From 402f075e4b4c93c923cc278eb44a2a76e9b53558 Mon Sep 17 00:00:00 2001 From: Anthony CAPELLI Date: Fri, 31 Oct 2025 12:32:04 +0100 Subject: [PATCH 1/9] add organization update --- src/Resource/Organizations.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Resource/Organizations.php b/src/Resource/Organizations.php index fd96314..ad32a15 100644 --- a/src/Resource/Organizations.php +++ b/src/Resource/Organizations.php @@ -49,6 +49,21 @@ public function create(string $realm, Organization $organization): void ); } + public function update(string $realm, string $id, Organization $organization): void + { + $this->commandExecutor->executeCommand( + new Command( + '/admin/realms/{realm}/organizations/{$id}', + Method::PUT, + [ + 'realm' => $realm, + 'id' => $id, + ], + $organization, + ), + ); + } + public function delete(string $realm, string $id): void { $this->commandExecutor->executeCommand( From 4f6604382f14ee20459a8a1be6dff6684271f9c6 Mon Sep 17 00:00:00 2001 From: Anthony CAPELLI Date: Fri, 31 Oct 2025 13:31:13 +0100 Subject: [PATCH 2/9] add tests --- tests/Integration/Resource/OrganizationsTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Integration/Resource/OrganizationsTest.php b/tests/Integration/Resource/OrganizationsTest.php index 8c57660..99567ab 100644 --- a/tests/Integration/Resource/OrganizationsTest.php +++ b/tests/Integration/Resource/OrganizationsTest.php @@ -74,6 +74,19 @@ public function testOrganizations(): void // Create user and add it to the organization $this->getKeycloak()->organizations()->addUser(self::REALM, $organization->getId(), $this->createAndGetUser()->getId()); + // Update organization + $updatedOrganization = new Organization( + name: 'updated-organization', + domains: new OrganizationDomainCollection([ + new OrganizationDomain('foo.bar.updated', true), + new OrganizationDomain('bar.foo.updated', false), + ]), + ); + $this->getKeycloak()->organizations()->update(self::REALM, $organizations->first()->getId(), $updatedOrganization); + $organizations = $this->getKeycloak()->organizations()->all(self::REALM); + static::assertCount(1, $organizations); + static::assertSame($updatedOrganization->getName(), $organizations->first()->getName()); + // Delete newly created organization $this->getKeycloak()->organizations()->delete(self::REALM, $organizations->first()->getId()); $organizations = $this->getKeycloak()->organizations()->all(self::REALM); From 467dc189ee56bb31bdffe9b3ddd30671c0fb8dfc Mon Sep 17 00:00:00 2001 From: Anthony CAPELLI Date: Fri, 31 Oct 2025 14:50:11 +0100 Subject: [PATCH 3/9] phpcs --- tests/Integration/Resource/OrganizationsTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Integration/Resource/OrganizationsTest.php b/tests/Integration/Resource/OrganizationsTest.php index 99567ab..3571314 100644 --- a/tests/Integration/Resource/OrganizationsTest.php +++ b/tests/Integration/Resource/OrganizationsTest.php @@ -78,9 +78,9 @@ public function testOrganizations(): void $updatedOrganization = new Organization( name: 'updated-organization', domains: new OrganizationDomainCollection([ - new OrganizationDomain('foo.bar.updated', true), - new OrganizationDomain('bar.foo.updated', false), - ]), + new OrganizationDomain('foo.bar.updated', true), + new OrganizationDomain('bar.foo.updated', false), + ]), ); $this->getKeycloak()->organizations()->update(self::REALM, $organizations->first()->getId(), $updatedOrganization); $organizations = $this->getKeycloak()->organizations()->all(self::REALM); From e3a8a23aae3bc8184eb8a805782dc6062b06a69f Mon Sep 17 00:00:00 2001 From: Anthony CAPELLI Date: Fri, 31 Oct 2025 15:13:52 +0100 Subject: [PATCH 4/9] fix id parameter --- src/Resource/Organizations.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resource/Organizations.php b/src/Resource/Organizations.php index ad32a15..0cfbf26 100644 --- a/src/Resource/Organizations.php +++ b/src/Resource/Organizations.php @@ -53,7 +53,7 @@ public function update(string $realm, string $id, Organization $organization): v { $this->commandExecutor->executeCommand( new Command( - '/admin/realms/{realm}/organizations/{$id}', + '/admin/realms/{realm}/organizations/{id}', Method::PUT, [ 'realm' => $realm, From 9225ab6bf98eb0f928804a36f3b3c3237095c7aa Mon Sep 17 00:00:00 2001 From: fschmtt Date: Sun, 2 Nov 2025 10:34:14 +0100 Subject: [PATCH 5/9] feat: static keycloak instance for setup and teardown --- tests/Integration/IntegrationTestBehaviour.php | 14 +++++++------- tests/Integration/Resource/RealmsTest.php | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/Integration/IntegrationTestBehaviour.php b/tests/Integration/IntegrationTestBehaviour.php index f13cb66..33ab92a 100644 --- a/tests/Integration/IntegrationTestBehaviour.php +++ b/tests/Integration/IntegrationTestBehaviour.php @@ -8,31 +8,31 @@ trait IntegrationTestBehaviour { - private ?Keycloak $keycloak = null; + private static ?Keycloak $keycloak = null; - public function getKeycloak(): Keycloak + public static function getKeycloak(): Keycloak { - if (!$this->keycloak) { - $this->keycloak = new Keycloak( + if (!self::$keycloak) { + self::$keycloak = new Keycloak( $_SERVER['KEYCLOAK_BASE_URL'] ?? 'http://keycloak:8080', 'admin', 'admin', ); } - return $this->keycloak; + return self::$keycloak; } protected function skipIfKeycloakVersionIsLessThan(string $version): void { - if (version_compare($this->getKeycloak()->getVersion(), $version, '<')) { + if (version_compare(self::getKeycloak()->getVersion(), $version, '<')) { $this->markTestSkipped(sprintf('Keycloak version is less than %s', $version)); } } protected function skipIfKeycloakVersionIsGreaterThan(string $version): void { - if (version_compare($this->getKeycloak()->getVersion(), $version, '>')) { + if (version_compare(self::getKeycloak()->getVersion(), $version, '>')) { $this->markTestSkipped(sprintf('Keycloak version is greater than %s', $version)); } } diff --git a/tests/Integration/Resource/RealmsTest.php b/tests/Integration/Resource/RealmsTest.php index 83c3674..c2441ce 100644 --- a/tests/Integration/Resource/RealmsTest.php +++ b/tests/Integration/Resource/RealmsTest.php @@ -43,7 +43,7 @@ public function testCanUpdateRealm(): void static::assertFalse($realm->getRegistrationAllowed()); $realm = $realm->withRegistrationAllowed(true); - $realm = $this->keycloak->realms()->update($realm->getRealm(), $realm); + $realm = $this->getKeycloak()->realms()->update($realm->getRealm(), $realm); static::assertTrue($realm->getRegistrationAllowed()); } @@ -56,7 +56,7 @@ public function testCanImportRealm(): void static::assertEquals('testing-id', $realm->getId()); static::assertEquals('testing-realm', $realm->getRealm()); - static::assertCount(2, $this->keycloak->realms()->all()); + static::assertCount(2, $this->getKeycloak()->realms()->all()); } public function testCanClearCaches(): void From bc06776d4ef9252d9a15a6562f0298d1a3004c87 Mon Sep 17 00:00:00 2001 From: fschmtt Date: Sun, 2 Nov 2025 10:34:30 +0100 Subject: [PATCH 6/9] fix: add missing method annotations --- src/Representation/OrganizationDomain.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Representation/OrganizationDomain.php b/src/Representation/OrganizationDomain.php index 8e93ba9..c52a44e 100644 --- a/src/Representation/OrganizationDomain.php +++ b/src/Representation/OrganizationDomain.php @@ -6,6 +6,12 @@ /** * @codeCoverageIgnore + * + * @method string|null getName() + * @method self withName(?string $name) + * + * @method bool|null getVerified() + * @method self withVerified(?bool $verified) */ class OrganizationDomain extends Representation { From f8bb89aa4b6dff10284e9c996606279792a57e41 Mon Sep 17 00:00:00 2001 From: fschmtt Date: Sun, 2 Nov 2025 10:34:53 +0100 Subject: [PATCH 7/9] feat: set up isolated realm --- tests/Integration/Resource/GroupsTest.php | 38 ++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/Integration/Resource/GroupsTest.php b/tests/Integration/Resource/GroupsTest.php index 8eeba2a..c4bf28a 100644 --- a/tests/Integration/Resource/GroupsTest.php +++ b/tests/Integration/Resource/GroupsTest.php @@ -7,6 +7,7 @@ use Exception; use Fschmtt\Keycloak\Http\Criteria; use Fschmtt\Keycloak\Representation\Group; +use Fschmtt\Keycloak\Representation\Realm; use Fschmtt\Keycloak\Test\Integration\IntegrationTestBehaviour; use PHPUnit\Framework\TestCase; use Ramsey\Uuid\Uuid; @@ -15,6 +16,18 @@ class GroupsTest extends TestCase { use IntegrationTestBehaviour; + private const REALM = 'groups-tests'; + + public static function setUpBeforeClass(): void + { + self::getKeycloak()->realms()->import(new Realm(realm: self::REALM)); + } + + public static function tearDownAfterClass(): void + { + self::getKeycloak()->realms()->delete(self::REALM); + } + public function testImportSearchUpdateDeleteGroup(): void { $groups = $this->getKeycloak()->groups(); @@ -23,32 +36,29 @@ public function testImportSearchUpdateDeleteGroup(): void $updatedGroupName = Uuid::uuid4()->toString(); // Create group - $groups->create( - 'master', - new Group(name: $importedGroupName), - ); + $groups->create(self::REALM, new Group(name: $importedGroupName)); // Get all groups - $allGroups = $groups->all('master'); + $allGroups = $groups->all(self::REALM); static::assertGreaterThanOrEqual(1, $allGroups->count()); $group = $allGroups->first(); static::assertInstanceOf(Group::class, $group); // Search for single (imported) group - $importedGroup = $groups->all('master', new Criteria([ + $importedGroup = $groups->all(self::REALM, new Criteria([ 'name' => $importedGroupName, ]))->first(); static::assertInstanceOf(Group::class, $importedGroup); static::assertSame($importedGroupName, $importedGroup->getName()); // Update (imported) group - $groups->update('master', $importedGroup->getId(), $importedGroup->withName($updatedGroupName)); + $groups->update(self::REALM, $importedGroup->getId(), $importedGroup->withName($updatedGroupName)); // Delete (imported) user - $groups->delete('master', $importedGroup->getId()); + $groups->delete(self::REALM, $importedGroup->getId()); try { - $groups->get('master', $importedGroup->getId()); + $groups->get(self::REALM, $importedGroup->getId()); static::fail('Group should not exist anymore'); } catch (Exception $e) { static::assertSame(404, $e->getCode()); @@ -65,13 +75,13 @@ public function testCreateChildGroup(): void $groups = $this->getKeycloak()->groups(); // Create group - $groups->create('master', new Group(name: $importedGroupName)); - $group = $groups->all('master')->first(); + $groups->create(self::REALM, new Group(name: $importedGroupName)); + $group = $groups->all(self::REALM)->first(); static::assertInstanceOf(Group::class, $group); // Create child group - $groups->createChild('master', new Group(name: $childGroupName), $group->getId()); - $childGroups = $groups->children('master', $group->getId()); + $groups->createChild(self::REALM, new Group(name: $childGroupName), $group->getId()); + $childGroups = $groups->children(self::REALM, $group->getId()); static::assertCount(1, $childGroups); $childGroup = $childGroups->first(); @@ -79,7 +89,7 @@ public function testCreateChildGroup(): void static::assertSame($childGroupName, $childGroup->getName()); // get child group by path - $pathGroup = $groups->byPath('master', $importedGroupName . '/' . $childGroupName); + $pathGroup = $groups->byPath(self::REALM, $importedGroupName . '/' . $childGroupName); static::assertInstanceOf(Group::class, $pathGroup); static::assertSame($childGroup->getId(), $pathGroup->getId()); } From f2014b17df2101953d23878a1f18c6a194b3c9d1 Mon Sep 17 00:00:00 2001 From: fschmtt Date: Sun, 2 Nov 2025 10:35:09 +0100 Subject: [PATCH 8/9] fix: organization update test --- .../Resource/OrganizationsTest.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/Integration/Resource/OrganizationsTest.php b/tests/Integration/Resource/OrganizationsTest.php index 3571314..e95a53c 100644 --- a/tests/Integration/Resource/OrganizationsTest.php +++ b/tests/Integration/Resource/OrganizationsTest.php @@ -75,17 +75,20 @@ public function testOrganizations(): void $this->getKeycloak()->organizations()->addUser(self::REALM, $organization->getId(), $this->createAndGetUser()->getId()); // Update organization - $updatedOrganization = new Organization( - name: 'updated-organization', - domains: new OrganizationDomainCollection([ - new OrganizationDomain('foo.bar.updated', true), - new OrganizationDomain('bar.foo.updated', false), - ]), - ); - $this->getKeycloak()->organizations()->update(self::REALM, $organizations->first()->getId(), $updatedOrganization); + $updatedOrganization = $organization->withDomains(new OrganizationDomainCollection([ + new OrganizationDomain('foo.bar.updated', true), + new OrganizationDomain('bar.foo.updated', false), + ])); + $this->getKeycloak()->organizations()->update(self::REALM, $organization->getId(), $updatedOrganization); $organizations = $this->getKeycloak()->organizations()->all(self::REALM); static::assertCount(1, $organizations); static::assertSame($updatedOrganization->getName(), $organizations->first()->getName()); + $domains = $organizations->first()->getDomains(); + static::assertCount(2, $domains); + static::assertSame([ + 'foo.bar.updated', + 'bar.foo.updated', + ], array_map(static fn (OrganizationDomain $domain) => $domain->getName(), $domains->all())); // Delete newly created organization $this->getKeycloak()->organizations()->delete(self::REALM, $organizations->first()->getId()); From 6ba1d7bef1daf9f9bbb801f9da702c6ef09111b7 Mon Sep 17 00:00:00 2001 From: fschmtt Date: Sun, 2 Nov 2025 10:37:25 +0100 Subject: [PATCH 9/9] chore: rename test realm --- tests/Integration/Resource/GroupsTest.php | 2 +- tests/Integration/Resource/OrganizationsTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Resource/GroupsTest.php b/tests/Integration/Resource/GroupsTest.php index c4bf28a..b0ee4b2 100644 --- a/tests/Integration/Resource/GroupsTest.php +++ b/tests/Integration/Resource/GroupsTest.php @@ -16,7 +16,7 @@ class GroupsTest extends TestCase { use IntegrationTestBehaviour; - private const REALM = 'groups-tests'; + private const REALM = 'groups-test'; public static function setUpBeforeClass(): void { diff --git a/tests/Integration/Resource/OrganizationsTest.php b/tests/Integration/Resource/OrganizationsTest.php index e95a53c..7d918b2 100644 --- a/tests/Integration/Resource/OrganizationsTest.php +++ b/tests/Integration/Resource/OrganizationsTest.php @@ -19,7 +19,7 @@ class OrganizationsTest extends TestCase { use IntegrationTestBehaviour; - private const REALM = 'organizations-tests'; + private const REALM = 'organizations-test'; protected function setUp(): void {