From 7e6dd682d4721b2283298e0f093ef481edf8ab53 Mon Sep 17 00:00:00 2001 From: Martin Kanis Date: Wed, 28 Aug 2024 10:29:03 +0200 Subject: [PATCH] Validate organization alias for forbidden chars Closes #32392 Signed-off-by: Martin Kanis --- .../jpa/JpaOrganizationProvider.java | 6 +++ .../admin/resource/OrganizationsResource.java | 3 ++ .../organization/admin/OrganizationTest.java | 39 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/model/jpa/src/main/java/org/keycloak/organization/jpa/JpaOrganizationProvider.java b/model/jpa/src/main/java/org/keycloak/organization/jpa/JpaOrganizationProvider.java index ec380038ef..4579db438a 100644 --- a/model/jpa/src/main/java/org/keycloak/organization/jpa/JpaOrganizationProvider.java +++ b/model/jpa/src/main/java/org/keycloak/organization/jpa/JpaOrganizationProvider.java @@ -59,6 +59,7 @@ import org.keycloak.models.jpa.entities.UserGroupMembershipEntity; import org.keycloak.organization.OrganizationProvider; import org.keycloak.representations.idm.MembershipType; import org.keycloak.organization.utils.Organizations; +import org.keycloak.utils.ReservedCharValidator; import org.keycloak.utils.StringUtil; public class JpaOrganizationProvider implements OrganizationProvider { @@ -82,6 +83,11 @@ public class JpaOrganizationProvider implements OrganizationProvider { } if (StringUtil.isBlank(alias)) { + try { + ReservedCharValidator.validateNoSpace(name); + } catch (ReservedCharValidator.ReservedCharException e) { + throw new ModelValidationException("Name contains a reserved character and cannot be used as alias"); + } alias = name; } diff --git a/services/src/main/java/org/keycloak/organization/admin/resource/OrganizationsResource.java b/services/src/main/java/org/keycloak/organization/admin/resource/OrganizationsResource.java index 4d5a91d67b..f4e8dce36b 100644 --- a/services/src/main/java/org/keycloak/organization/admin/resource/OrganizationsResource.java +++ b/services/src/main/java/org/keycloak/organization/admin/resource/OrganizationsResource.java @@ -49,6 +49,7 @@ import org.keycloak.services.ErrorResponse; import org.keycloak.services.resources.KeycloakOpenAPI; import org.keycloak.services.resources.admin.AdminEventBuilder; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.utils.ReservedCharValidator; import org.keycloak.utils.SearchQueryUtils; import org.keycloak.utils.StringUtil; @@ -91,6 +92,8 @@ public class OrganizationsResource { throw ErrorResponse.error("Organization cannot be null.", Response.Status.BAD_REQUEST); } + ReservedCharValidator.validateNoSpace(organization.getAlias()); + try { OrganizationModel model = provider.create(organization.getName(), organization.getAlias()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/OrganizationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/OrganizationTest.java index 53f4433eea..440308296a 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/OrganizationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/admin/OrganizationTest.java @@ -60,6 +60,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.OrganizationDomainRepresentation; import org.keycloak.representations.idm.OrganizationRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.runonserver.RunOnServer; import org.keycloak.testsuite.updaters.RealmAttributeUpdater; @@ -515,4 +516,42 @@ public class OrganizationTest extends AbstractOrganizationTest { assertEquals("A organization with the same alias already exists", error.getErrorMessage()); } } + + @Test + public void testSpecialCharsAlias() { + OrganizationRepresentation org = createRepresentation("acme"); + OrganizationDomainRepresentation orgDomain = new OrganizationDomainRepresentation(); + orgDomain.setName("acme.com"); + org.addDomain(orgDomain); + + org.setAlias("acme&@#!"); + try (Response response = testRealm().organizations().create(org)) { + assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + // blank alias will be replaced with org name, which is valid + org.setAlias(""); + try (Response response = testRealm().organizations().create(org)) { + assertEquals(Status.CREATED.getStatusCode(), response.getStatus()); + String id = ApiUtil.getCreatedId(response); + getCleanup().addCleanup(() -> testRealm().organizations().get(id).delete().close()); + } + + org.setAlias(" "); + try (Response response = testRealm().organizations().create(org)) { + assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + org.setAlias("\n"); + try (Response response = testRealm().organizations().create(org)) { + assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + // when alias is empty, name is used as alias + org.setName("acme@"); + org.setAlias(""); + try (Response response = testRealm().organizations().create(org)) { + assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + } }