Encode realm name in console URIs (#29102)
Before this fix console uris (including the client redirect uris) did not contain the url encoded realm name and therefore were invalid. closes #25807 Signed-off-by: Philip Sanetra <code@psanetra.de> Signed-off-by: rmartinc <rmartinc@redhat.com> Co-authored-by: Philip Sanetra <code@psanetra.de> Co-authored-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
879fe082dd
commit
65bdf1a604
4 changed files with 71 additions and 4 deletions
|
@ -59,7 +59,7 @@ export const PersonalInfoRoute: IndexRouteObject = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RootRoute: RouteObject = {
|
export const RootRoute: RouteObject = {
|
||||||
path: new URL(environment.baseUrl).pathname,
|
path: decodeURIComponent(new URL(environment.baseUrl).pathname),
|
||||||
element: <Root />,
|
element: <Root />,
|
||||||
errorElement: <ErrorPage />,
|
errorElement: <ErrorPage />,
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.services.managers;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.enums.SslRequired;
|
import org.keycloak.common.enums.SslRequired;
|
||||||
|
import org.keycloak.common.util.Encode;
|
||||||
import org.keycloak.models.AbstractKeycloakTransaction;
|
import org.keycloak.models.AbstractKeycloakTransaction;
|
||||||
import org.keycloak.models.AccountRoles;
|
import org.keycloak.models.AccountRoles;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
|
@ -178,7 +179,8 @@ public class RealmManager {
|
||||||
adminConsole.setName("${client_" + Constants.ADMIN_CONSOLE_CLIENT_ID + "}");
|
adminConsole.setName("${client_" + Constants.ADMIN_CONSOLE_CLIENT_ID + "}");
|
||||||
|
|
||||||
adminConsole.setRootUrl(Constants.AUTH_ADMIN_URL_PROP);
|
adminConsole.setRootUrl(Constants.AUTH_ADMIN_URL_PROP);
|
||||||
String baseUrl = "/admin/" + realm.getName() + "/console/";
|
|
||||||
|
String baseUrl = "/admin/" + Encode.encodePathAsIs(realm.getName()) + "/console/";
|
||||||
adminConsole.setBaseUrl(baseUrl);
|
adminConsole.setBaseUrl(baseUrl);
|
||||||
adminConsole.addRedirectUri(baseUrl + "*");
|
adminConsole.addRedirectUri(baseUrl + "*");
|
||||||
adminConsole.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
adminConsole.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
|
@ -424,7 +426,8 @@ public class RealmManager {
|
||||||
accountClient.setFullScopeAllowed(false);
|
accountClient.setFullScopeAllowed(false);
|
||||||
|
|
||||||
accountClient.setRootUrl(Constants.AUTH_BASE_URL_PROP);
|
accountClient.setRootUrl(Constants.AUTH_BASE_URL_PROP);
|
||||||
String baseUrl = "/realms/" + realm.getName() + "/account/";
|
|
||||||
|
String baseUrl = "/realms/" + Encode.encodePathAsIs(realm.getName()) + "/account/";
|
||||||
accountClient.setBaseUrl(baseUrl);
|
accountClient.setBaseUrl(baseUrl);
|
||||||
accountClient.addRedirectUri(baseUrl + "*");
|
accountClient.addRedirectUri(baseUrl + "*");
|
||||||
accountClient.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
accountClient.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
|
@ -529,6 +532,7 @@ public class RealmManager {
|
||||||
try {
|
try {
|
||||||
session.getContext().setRealm(realm);
|
session.getContext().setRealm(realm);
|
||||||
ReservedCharValidator.validate(rep.getRealm());
|
ReservedCharValidator.validate(rep.getRealm());
|
||||||
|
ReservedCharValidator.validateLocales(rep.getSupportedLocales());
|
||||||
realm.setName(rep.getRealm());
|
realm.setName(rep.getRealm());
|
||||||
|
|
||||||
// setup defaults
|
// setup defaults
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.http.HttpResponse;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.NotAuthorizedException;
|
import jakarta.ws.rs.NotAuthorizedException;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.common.util.Encode;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.jose.jws.JWSInputException;
|
import org.keycloak.jose.jws.JWSInputException;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -170,7 +171,7 @@ public class AdminRoot {
|
||||||
} catch (JWSInputException e) {
|
} catch (JWSInputException e) {
|
||||||
throw new NotAuthorizedException("Bearer token format error");
|
throw new NotAuthorizedException("Bearer token format error");
|
||||||
}
|
}
|
||||||
String realmName = token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1);
|
String realmName = Encode.decodePath(token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1));
|
||||||
RealmManager realmManager = new RealmManager(session);
|
RealmManager realmManager = new RealmManager(session);
|
||||||
RealmModel realm = realmManager.getRealmByName(realmName);
|
RealmModel realm = realmManager.getRealmByName(realmName);
|
||||||
if (realm == null) {
|
if (realm == null) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.admin.realm;
|
package org.keycloak.testsuite.admin.realm;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import jakarta.ws.rs.BadRequestException;
|
import jakarta.ws.rs.BadRequestException;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
@ -36,6 +37,7 @@ import org.keycloak.events.EventType;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.events.admin.ResourceType;
|
import org.keycloak.events.admin.ResourceType;
|
||||||
import org.keycloak.events.log.JBossLoggingEventListenerProviderFactory;
|
import org.keycloak.events.log.JBossLoggingEventListenerProviderFactory;
|
||||||
|
import org.keycloak.models.AdminRoles;
|
||||||
import org.keycloak.models.CibaConfig;
|
import org.keycloak.models.CibaConfig;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.OAuth2DeviceConfig;
|
import org.keycloak.models.OAuth2DeviceConfig;
|
||||||
|
@ -67,6 +69,7 @@ import org.keycloak.testsuite.client.KeycloakTestingClient;
|
||||||
import org.keycloak.testsuite.events.TestEventsListenerProviderFactory;
|
import org.keycloak.testsuite.events.TestEventsListenerProviderFactory;
|
||||||
import org.keycloak.testsuite.runonserver.RunHelpers;
|
import org.keycloak.testsuite.runonserver.RunHelpers;
|
||||||
import org.keycloak.testsuite.updaters.Creator;
|
import org.keycloak.testsuite.updaters.Creator;
|
||||||
|
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
import org.keycloak.testsuite.util.CredentialBuilder;
|
import org.keycloak.testsuite.util.CredentialBuilder;
|
||||||
|
@ -86,15 +89,20 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.hamcrest.Matchers.everyItem;
|
||||||
import static org.hamcrest.Matchers.hasItems;
|
import static org.hamcrest.Matchers.hasItems;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
@ -217,6 +225,57 @@ public class RealmTest extends AbstractAdminTest {
|
||||||
Assert.assertNames(adminClient.realms().findAll(), "master", AuthRealm.TEST, REALM_NAME);
|
Assert.assertNames(adminClient.realms().findAll(), "master", AuthRealm.TEST, REALM_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createRealmWithValidConsoleUris() throws Exception {
|
||||||
|
var realmNameWithSpaces = "new realm";
|
||||||
|
|
||||||
|
getCleanup()
|
||||||
|
.addCleanup(() -> adminClient.realms().realm(realmNameWithSpaces).remove());
|
||||||
|
|
||||||
|
RealmRepresentation rep = new RealmRepresentation();
|
||||||
|
rep.setRealm(realmNameWithSpaces);
|
||||||
|
rep.setEnabled(Boolean.TRUE);
|
||||||
|
rep.setUsers(Collections.singletonList(UserBuilder.create()
|
||||||
|
.username("new-realm-admin")
|
||||||
|
.firstName("new-realm-admin")
|
||||||
|
.lastName("new-realm-admin")
|
||||||
|
.email("new-realm-admin@keycloak.org")
|
||||||
|
.password("password")
|
||||||
|
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN)
|
||||||
|
.build()));
|
||||||
|
|
||||||
|
adminClient.realms().create(rep);
|
||||||
|
|
||||||
|
Assert.assertNames(adminClient.realms().findAll(), "master", AuthRealm.TEST, REALM_NAME, realmNameWithSpaces);
|
||||||
|
|
||||||
|
final var urlPlaceHolders = ImmutableSet.of("${authBaseUrl}", "${authAdminUrl}");
|
||||||
|
|
||||||
|
RealmResource newRealm = adminClient.realms().realm(realmNameWithSpaces);
|
||||||
|
List<String> clientUris = newRealm.clients()
|
||||||
|
.findAll()
|
||||||
|
.stream()
|
||||||
|
.flatMap(client -> Stream.concat(Stream.concat(Stream.concat(
|
||||||
|
client.getRedirectUris().stream(),
|
||||||
|
Stream.of(client.getBaseUrl())),
|
||||||
|
Stream.of(client.getRootUrl())),
|
||||||
|
Stream.of(client.getAdminUrl())))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(uri -> !urlPlaceHolders.contains(uri))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
assertThat(clientUris, not(empty()));
|
||||||
|
assertThat(clientUris, everyItem(containsString("/new%20realm/")));
|
||||||
|
|
||||||
|
try (Keycloak client = AdminClientUtil.createAdminClient(true, realmNameWithSpaces,
|
||||||
|
"new-realm-admin", "password", Constants.ADMIN_CLI_CLIENT_ID, null)) {
|
||||||
|
Assert.assertNotNull(client.serverInfo().getInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
adminClient.realms().realm(realmNameWithSpaces).remove();
|
||||||
|
|
||||||
|
Assert.assertNames(adminClient.realms().findAll(), "master", AuthRealm.TEST, REALM_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createRealmRejectReservedCharOrEmptyName() {
|
public void createRealmRejectReservedCharOrEmptyName() {
|
||||||
RealmRepresentation rep = new RealmRepresentation();
|
RealmRepresentation rep = new RealmRepresentation();
|
||||||
|
@ -224,6 +283,9 @@ public class RealmTest extends AbstractAdminTest {
|
||||||
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
|
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
|
||||||
rep.setRealm("");
|
rep.setRealm("");
|
||||||
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
|
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
|
||||||
|
rep.setRealm("new-realm");
|
||||||
|
rep.setId("invalid;id");
|
||||||
|
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue