keycloak-uncached
Changes
distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli 8(+8 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli 8(+8 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli 8(+8 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli 8(+8 -0)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java 3(+1 -2)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java 23(+12 -11)
services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java 17(+16 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java 14(+11 -3)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/FixedHostnameTest.java 165(+165 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json 13(+12 -1)
travis-run-tests.sh 2(+1 -1)
Details
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli
index 3498794..d75dd70 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli
@@ -439,4 +439,12 @@ if (outcome == failed) of /profile=$clusteredProfile/subsystem=keycloak-server/s
echo
end-if
+# Migrate from 4.2.0 to 4.3.0
+if (outcome == failed) of /subsystem=keycloak-server/spi=hostname/:read-resource
+ echo Adding spi=hostname...
+ /subsystem=keycloak-server/spi=hostname/:add(default-provider=request)
+ /subsystem=keycloak-server/spi=hostname/provider=fixed/:add(properties={hostname => "localhost",httpPort => "-1",httpsPort => "-1"},enabled=true)
+ echo
+end-if
+
echo *** End Migration of /profile=$clusteredProfile ***
\ No newline at end of file
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli
index 33a2334..d0af97a 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli
@@ -396,4 +396,12 @@ if (outcome == failed) of /profile=$standaloneProfile/subsystem=keycloak-server/
echo
end-if
+# Migrate from 4.2.0 to 4.3.0
+if (outcome == failed) of /subsystem=keycloak-server/spi=hostname/:read-resource
+ echo Adding spi=hostname...
+ /subsystem=keycloak-server/spi=hostname/:add(default-provider=request)
+ /subsystem=keycloak-server/spi=hostname/provider=fixed/:add(properties={hostname => "localhost",httpPort => "-1",httpsPort => "-1"},enabled=true)
+ echo
+end-if
+
echo *** End Migration of /profile=$standaloneProfile ***
\ No newline at end of file
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli
index 9bf0b42..5194c45 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone.cli
@@ -367,4 +367,12 @@ if (outcome == failed) of /subsystem=keycloak-server/spi=x509cert-lookup/:read-r
echo
end-if
+# Migrate from 4.2.0 to 4.3.0
+if (outcome == failed) of /subsystem=keycloak-server/spi=hostname/:read-resource
+ echo Adding spi=hostname...
+ /subsystem=keycloak-server/spi=hostname/:add(default-provider=request)
+ /subsystem=keycloak-server/spi=hostname/provider=fixed/:add(properties={hostname => "localhost",httpPort => "-1",httpsPort => "-1"},enabled=true)
+ echo
+end-if
+
echo *** End Migration ***
\ No newline at end of file
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli
index aea4a6f..5b11647 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-standalone-ha.cli
@@ -423,4 +423,12 @@ if (outcome == failed) of /subsystem=keycloak-server/spi=x509cert-lookup/:read-r
echo
end-if
+# Migrate from 4.2.0 to 4.3.0
+if (outcome == failed) of /subsystem=keycloak-server/spi=hostname/:read-resource
+ echo Adding spi=hostname...
+ /subsystem=keycloak-server/spi=hostname/:add(default-provider=request)
+ /subsystem=keycloak-server/spi=hostname/provider=fixed/:add(properties={hostname => "localhost",httpPort => "-1",httpsPort => "-1"},enabled=true)
+ echo
+end-if
+
echo *** End Migration ***
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java b/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java
index be5f551..4407149 100755
--- a/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java
+++ b/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java
@@ -33,7 +33,7 @@ public interface KeycloakContext {
String getContextPath();
- UriInfo getUri();
+ KeycloakUriInfo getUri();
HttpHeaders getRequestHeaders();
diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakUriInfo.java b/server-spi/src/main/java/org/keycloak/models/KeycloakUriInfo.java
new file mode 100644
index 0000000..2643cc0
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/KeycloakUriInfo.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models;
+
+import org.jboss.resteasy.specimpl.ResteasyUriBuilder;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.urls.HostnameProvider;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+import java.util.List;
+
+public class KeycloakUriInfo implements UriInfo {
+
+ private final UriInfo delegate;
+ private final String hostname;
+ private final int port;
+
+ private URI absolutePath;
+ private URI requestURI;
+ private URI baseURI;
+
+ public KeycloakUriInfo(KeycloakSession session, UriInfo delegate) {
+ this.delegate = delegate;
+
+ HostnameProvider hostnameProvider = session.getProvider(HostnameProvider.class);
+ this.hostname = hostnameProvider.getHostname(delegate);
+ this.port = hostnameProvider.getPort(delegate);
+ }
+
+ public UriInfo getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public URI getRequestUri() {
+ if (requestURI == null) {
+ requestURI = delegate.getRequestUriBuilder().host(hostname).port(port).build();
+ }
+ return requestURI;
+ }
+
+ @Override
+ public UriBuilder getRequestUriBuilder() {
+ return UriBuilder.fromUri(getRequestUri());
+ }
+
+ @Override
+ public URI getAbsolutePath() {
+ if (absolutePath == null) {
+ absolutePath = delegate.getAbsolutePathBuilder().host(hostname).port(port).build();
+ }
+ return absolutePath;
+ }
+
+ @Override
+ public UriBuilder getAbsolutePathBuilder() {
+ return UriBuilder.fromUri(getAbsolutePath());
+ }
+
+ @Override
+ public URI getBaseUri() {
+ if (baseURI == null) {
+ baseURI = delegate.getBaseUriBuilder().host(hostname).port(port).build();
+ }
+ return baseURI;
+ }
+
+ @Override
+ public UriBuilder getBaseUriBuilder() {
+ return UriBuilder.fromUri(getBaseUri());
+ }
+
+ @Override
+ public URI resolve(URI uri) {
+ return getBaseUri().resolve(uri);
+ }
+
+ @Override
+ public URI relativize(URI uri) {
+ URI from = this.getRequestUri();
+ URI to = uri;
+ if (uri.getScheme() == null && uri.getHost() == null) {
+ to = this.getBaseUriBuilder().replaceQuery(null).path(uri.getPath()).replaceQuery(uri.getQuery()).fragment(uri.getFragment()).build(new Object[0]);
+ }
+
+ return ResteasyUriBuilder.relativize(from, to);
+ }
+
+ @Override
+ public String getPath() {
+ return delegate.getPath();
+ }
+
+ @Override
+ public String getPath(boolean decode) {
+ return delegate.getPath(decode);
+ }
+
+ @Override
+ public List<PathSegment> getPathSegments() {
+ return delegate.getPathSegments();
+ }
+
+ @Override
+ public List<PathSegment> getPathSegments(boolean decode) {
+ return delegate.getPathSegments(decode);
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getPathParameters() {
+ return delegate.getPathParameters();
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getPathParameters(boolean decode) {
+ return delegate.getPathParameters(decode);
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getQueryParameters() {
+ return delegate.getQueryParameters();
+ }
+
+ @Override
+ public MultivaluedMap<String, String> getQueryParameters(boolean decode) {
+ return delegate.getQueryParameters(decode);
+ }
+
+ @Override
+ public List<String> getMatchedURIs() {
+ return delegate.getMatchedURIs();
+ }
+
+ @Override
+ public List<String> getMatchedURIs(boolean decode) {
+ return delegate.getMatchedURIs(decode);
+ }
+
+ @Override
+ public List<Object> getMatchedResources() {
+ return delegate.getMatchedResources();
+ }
+}
diff --git a/server-spi/src/main/java/org/keycloak/urls/HostnameProvider.java b/server-spi/src/main/java/org/keycloak/urls/HostnameProvider.java
new file mode 100644
index 0000000..0d33b93
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/urls/HostnameProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.urls;
+
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.provider.Provider;
+
+import javax.ws.rs.core.UriInfo;
+
+public interface HostnameProvider extends Provider {
+
+ /**
+ * Return the hostname. Http headers, realm details, etc. can be retrieved from the KeycloakSession. Do NOT use
+ * {@link KeycloakContext#getUri()} as it will in turn call the HostnameProvider resulting in an infinite loop!
+ *
+ * @param originalUriInfo the original UriInfo before hostname is replaced by the HostnameProvider
+ * @return the hostname
+ */
+ String getHostname(UriInfo originalUriInfo);
+
+ int getPort(UriInfo originalUriInfo);
+
+ @Override
+ default void close() {
+ }
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/urls/HostnameProviderFactory.java b/server-spi/src/main/java/org/keycloak/urls/HostnameProviderFactory.java
new file mode 100644
index 0000000..73b4a2d
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/urls/HostnameProviderFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.urls;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderFactory;
+
+public interface HostnameProviderFactory extends ProviderFactory<HostnameProvider> {
+
+ @Override
+ default void close() {
+ }
+
+ @Override
+ default void init(Config.Scope config) {
+ }
+
+ @Override
+ default void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ default int order() {
+ return 0;
+ }
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/urls/HostnameSpi.java b/server-spi/src/main/java/org/keycloak/urls/HostnameSpi.java
new file mode 100644
index 0000000..624fac2
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/urls/HostnameSpi.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.urls;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+public class HostnameSpi implements Spi {
+
+ @Override
+ public boolean isInternal() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "hostname";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return HostnameProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return HostnameProviderFactory.class;
+ }
+
+}
diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 86539b5..8fb53ab 100755
--- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -34,4 +34,5 @@
org.keycloak.storage.UserStorageProviderSpi
org.keycloak.theme.ThemeResourceSpi
-org.keycloak.theme.ThemeSelectorSpi
\ No newline at end of file
+org.keycloak.theme.ThemeSelectorSpi
+org.keycloak.urls.HostnameSpi
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
index 88986b5..4e1a96f 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
@@ -71,7 +71,6 @@ public class ClientRegistrationAuth {
private void init() {
realm = session.getContext().getRealm();
- UriInfo uri = session.getContext().getUri();
String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (authorizationHeader == null) {
@@ -85,7 +84,7 @@ public class ClientRegistrationAuth {
token = split[1];
- ClientRegistrationTokenUtils.TokenVerification tokenVerification = ClientRegistrationTokenUtils.verifyToken(session, realm, uri, token);
+ ClientRegistrationTokenUtils.TokenVerification tokenVerification = ClientRegistrationTokenUtils.verifyToken(session, realm, token);
if (tokenVerification.getError() != null) {
throw unauthorized(tokenVerification.getError().getMessage());
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
index 270ca2a..111bd0d 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
@@ -31,6 +31,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.services.Urls;
import org.keycloak.services.clientregistration.policy.RegistrationAuth;
+import org.keycloak.urls.HostnameProvider;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.core.UriInfo;
@@ -66,25 +67,25 @@ public class ClientRegistrationTokenUtils {
}
public static String updateRegistrationAccessToken(KeycloakSession session, ClientModel client, RegistrationAuth registrationAuth) {
- return updateRegistrationAccessToken(session, session.getContext().getRealm(), session.getContext().getUri(), client, registrationAuth);
+ return updateRegistrationAccessToken(session, session.getContext().getRealm(), client, registrationAuth);
}
- public static String updateRegistrationAccessToken(KeycloakSession session, RealmModel realm, UriInfo uri, ClientModel client, RegistrationAuth registrationAuth) {
+ public static String updateRegistrationAccessToken(KeycloakSession session, RealmModel realm, ClientModel client, RegistrationAuth registrationAuth) {
String id = KeycloakModelUtils.generateId();
client.setRegistrationToken(id);
RegistrationAccessToken regToken = new RegistrationAccessToken();
regToken.setRegistrationAuth(registrationAuth.toString().toLowerCase());
- return setupToken(regToken, session, realm, uri, id, TYPE_REGISTRATION_ACCESS_TOKEN, 0);
+ return setupToken(regToken, session, realm, id, TYPE_REGISTRATION_ACCESS_TOKEN, 0);
}
- public static String createInitialAccessToken(KeycloakSession session, RealmModel realm, UriInfo uri, ClientInitialAccessModel model) {
+ public static String createInitialAccessToken(KeycloakSession session, RealmModel realm, ClientInitialAccessModel model) {
JsonWebToken initialToken = new JsonWebToken();
- return setupToken(initialToken, session, realm, uri, model.getId(), TYPE_INITIAL_ACCESS_TOKEN, model.getExpiration() > 0 ? model.getTimestamp() + model.getExpiration() : 0);
+ return setupToken(initialToken, session, realm, model.getId(), TYPE_INITIAL_ACCESS_TOKEN, model.getExpiration() > 0 ? model.getTimestamp() + model.getExpiration() : 0);
}
- public static TokenVerification verifyToken(KeycloakSession session, RealmModel realm, UriInfo uri, String token) {
+ public static TokenVerification verifyToken(KeycloakSession session, RealmModel realm, String token) {
if (token == null) {
return TokenVerification.error(new RuntimeException("Missing token"));
}
@@ -110,7 +111,7 @@ public class ClientRegistrationTokenUtils {
return TokenVerification.error(new RuntimeException("Token is not JWT", e));
}
- if (!getIssuer(realm, uri).equals(jwt.getIssuer())) {
+ if (!getIssuer(session, realm).equals(jwt.getIssuer())) {
return TokenVerification.error(new RuntimeException("Issuer from token don't match with the realm issuer."));
}
@@ -127,8 +128,8 @@ public class ClientRegistrationTokenUtils {
return TokenVerification.success(kid, jwt);
}
- private static String setupToken(JsonWebToken jwt, KeycloakSession session, RealmModel realm, UriInfo uri, String id, String type, int expiration) {
- String issuer = getIssuer(realm, uri);
+ private static String setupToken(JsonWebToken jwt, KeycloakSession session, RealmModel realm, String id, String type, int expiration) {
+ String issuer = getIssuer(session, realm);
jwt.type(type);
jwt.id(id);
@@ -143,8 +144,8 @@ public class ClientRegistrationTokenUtils {
return token;
}
- private static String getIssuer(RealmModel realm, UriInfo uri) {
- return Urls.realmIssuer(uri.getBaseUri(), realm.getName());
+ private static String getIssuer(KeycloakSession session, RealmModel realm) {
+ return Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());
}
protected static class TokenVerification {
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
index e1e0880..4f482ef 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
@@ -22,6 +22,7 @@ import org.keycloak.common.ClientConnection;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakUriInfo;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.resources.KeycloakApplication;
@@ -45,6 +46,8 @@ public class DefaultKeycloakContext implements KeycloakContext {
private KeycloakSession session;
+ private KeycloakUriInfo uriInfo;
+
public DefaultKeycloakContext(KeycloakSession session) {
this.session = session;
}
@@ -64,8 +67,11 @@ public class DefaultKeycloakContext implements KeycloakContext {
}
@Override
- public UriInfo getUri() {
- return getContextObject(UriInfo.class);
+ public KeycloakUriInfo getUri() {
+ if (uriInfo == null) {
+ uriInfo = new KeycloakUriInfo(session, getContextObject(UriInfo.class));
+ }
+ return uriInfo;
}
@Override
@@ -86,6 +92,7 @@ public class DefaultKeycloakContext implements KeycloakContext {
@Override
public void setRealm(RealmModel realm) {
this.realm = realm;
+ this.uriInfo = null;
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
index f6ddd11..8d0fae3 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
@@ -83,7 +83,7 @@ public class ClientInitialAccessResource {
ClientInitialAccessPresentation rep = wrap(clientInitialAccessModel);
- String token = ClientRegistrationTokenUtils.createInitialAccessToken(session, realm, session.getContext().getUri(), clientInitialAccessModel);
+ String token = ClientRegistrationTokenUtils.createInitialAccessToken(session, realm, clientInitialAccessModel);
rep.setToken(token);
response.setStatus(Response.Status.CREATED.getStatusCode());
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index a78a255..1baf710 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -251,7 +251,7 @@ public class ClientResource {
public ClientRepresentation regenerateRegistrationAccessToken() {
auth.clients().requireManage(client);
- String token = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, realm, session.getContext().getUri(), client, RegistrationAuth.AUTHENTICATED);
+ String token = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, realm, client, RegistrationAuth.AUTHENTICATED);
ClientRepresentation rep = ModelToRepresentation.toRepresentation(client);
rep.setRegistrationAccessToken(token);
diff --git a/services/src/main/java/org/keycloak/url/FixedHostnameProvider.java b/services/src/main/java/org/keycloak/url/FixedHostnameProvider.java
new file mode 100644
index 0000000..d871270
--- /dev/null
+++ b/services/src/main/java/org/keycloak/url/FixedHostnameProvider.java
@@ -0,0 +1,40 @@
+package org.keycloak.url;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.urls.HostnameProvider;
+
+import javax.ws.rs.core.UriInfo;
+
+public class FixedHostnameProvider implements HostnameProvider {
+
+ private final KeycloakSession session;
+ private final String globalHostname;
+ private final int httpPort;
+ private final int httpsPort;
+
+ public FixedHostnameProvider(KeycloakSession session, String globalHostname, int httpPort, int httpsPort) {
+ this.session = session;
+ this.globalHostname = globalHostname;
+ this.httpPort = httpPort;
+ this.httpsPort = httpsPort;
+ }
+
+ @Override
+ public String getHostname(UriInfo originalUriInfo) {
+ RealmModel realm = session.getContext().getRealm();
+ if (realm != null) {
+ String realmHostname = session.getContext().getRealm().getAttribute("hostname");
+ if (realmHostname != null && !realmHostname.isEmpty()) {
+ return realmHostname;
+ }
+ }
+ return this.globalHostname;
+ }
+
+ @Override
+ public int getPort(UriInfo originalUriInfo) {
+ return originalUriInfo.getRequestUri().getScheme().equals("https") ? httpsPort : httpPort;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/url/FixedHostnameProviderFactory.java b/services/src/main/java/org/keycloak/url/FixedHostnameProviderFactory.java
new file mode 100644
index 0000000..1899cda
--- /dev/null
+++ b/services/src/main/java/org/keycloak/url/FixedHostnameProviderFactory.java
@@ -0,0 +1,35 @@
+package org.keycloak.url;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.urls.HostnameProvider;
+import org.keycloak.urls.HostnameProviderFactory;
+
+public class FixedHostnameProviderFactory implements HostnameProviderFactory {
+
+ private String hostname;
+ private int httpPort;
+ private int httpsPort;
+
+ @Override
+ public HostnameProvider create(KeycloakSession session) {
+ return new FixedHostnameProvider(session, hostname, httpPort, httpsPort);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ this.hostname = config.get("hostname");
+ if (this.hostname == null) {
+ throw new RuntimeException("hostname not set");
+ }
+
+ this.httpPort = config.getInt("httpPort", -1);
+ this.httpsPort = config.getInt("httpsPort", -1);
+ }
+
+ @Override
+ public String getId() {
+ return "fixed";
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/url/RequestHostnameProvider.java b/services/src/main/java/org/keycloak/url/RequestHostnameProvider.java
new file mode 100644
index 0000000..f5a6f00
--- /dev/null
+++ b/services/src/main/java/org/keycloak/url/RequestHostnameProvider.java
@@ -0,0 +1,19 @@
+package org.keycloak.url;
+
+import org.keycloak.urls.HostnameProvider;
+
+import javax.ws.rs.core.UriInfo;
+
+public class RequestHostnameProvider implements HostnameProvider {
+
+ @Override
+ public String getHostname(UriInfo originalUriInfo) {
+ return originalUriInfo.getBaseUri().getHost();
+ }
+
+ @Override
+ public int getPort(UriInfo originalUriInfo) {
+ return originalUriInfo.getRequestUri().getPort();
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/url/RequestHostnameProviderFactory.java b/services/src/main/java/org/keycloak/url/RequestHostnameProviderFactory.java
new file mode 100644
index 0000000..d5cc384
--- /dev/null
+++ b/services/src/main/java/org/keycloak/url/RequestHostnameProviderFactory.java
@@ -0,0 +1,21 @@
+package org.keycloak.url;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.urls.HostnameProvider;
+import org.keycloak.urls.HostnameProviderFactory;
+
+import javax.ws.rs.core.UriInfo;
+
+public class RequestHostnameProviderFactory implements HostnameProviderFactory {
+
+ @Override
+ public HostnameProvider create(KeycloakSession session) {
+ return new RequestHostnameProvider();
+ }
+
+ @Override
+ public String getId() {
+ return "request";
+ }
+
+}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.urls.HostnameProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.urls.HostnameProviderFactory
new file mode 100644
index 0000000..9177b0f
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.urls.HostnameProviderFactory
@@ -0,0 +1,2 @@
+org.keycloak.url.FixedHostnameProviderFactory
+org.keycloak.url.RequestHostnameProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
index 8ce4d2e..4a6af70 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
@@ -34,6 +34,7 @@ import org.junit.Assert;
import org.keycloak.OAuth2Constants;
import org.keycloak.RSATokenVerifier;
import org.keycloak.admin.client.Keycloak;
+import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.common.util.PemUtils;
@@ -46,6 +47,7 @@ import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
@@ -633,6 +635,14 @@ public class OAuthClient {
}
}
+ public OIDCConfigurationRepresentation doWellKnownRequest(String realm) {
+ try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+ return SimpleHttp.doGet(AUTH_SERVER_ROOT + "/realms/" + realm + "/.well-known/openid-configuration", client).asJson(OIDCConfigurationRepresentation.class);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
public void closeClient(CloseableHttpClient client) {
try {
client.close();
@@ -729,7 +739,7 @@ public class OAuthClient {
}
public String getLoginFormUrl() {
- UriBuilder b = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(AUTH_SERVER_ROOT));
+ UriBuilder b = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(baseUrl));
if (responseType != null) {
b.queryParam(OAuth2Constants.RESPONSE_TYPE, responseType);
}
@@ -824,6 +834,11 @@ public class OAuthClient {
return b.build(realm).toString();
}
+ public OAuthClient baseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ return this;
+ }
+
public OAuthClient realm(String realm) {
this.realm = realm;
return this;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
index 93778fd..e47121c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -149,9 +149,7 @@ public abstract class AbstractKeycloakTest {
public void beforeAbstractKeycloakTest() throws Exception {
adminClient = testContext.getAdminClient();
if (adminClient == null || adminClient.isClosed()) {
- String authServerContextRoot = suiteContext.getAuthServerInfo().getContextRoot().toString();
- adminClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(), authServerContextRoot);
- testContext.setAdminClient(adminClient);
+ reconnectAdminClient();
}
getTestingClient();
@@ -181,6 +179,16 @@ public abstract class AbstractKeycloakTest {
}
+ public void reconnectAdminClient() throws Exception {
+ if (adminClient != null && !adminClient.isClosed()) {
+ adminClient.close();
+ }
+
+ String authServerContextRoot = suiteContext.getAuthServerInfo().getContextRoot().toString();
+ adminClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(), authServerContextRoot);
+ testContext.setAdminClient(adminClient);
+ }
+
protected void beforeAbstractKeycloakTestRealmImport() throws Exception {
}
protected void postAfterAbstractKeycloak() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/FixedHostnameTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/FixedHostnameTest.java
new file mode 100644
index 0000000..4b4828c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/FixedHostnameTest.java
@@ -0,0 +1,165 @@
+package org.keycloak.testsuite.url;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.jboss.arquillian.container.test.api.ContainerController;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.junit.Test;
+import org.keycloak.client.registration.Auth;
+import org.keycloak.client.registration.ClientRegistration;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.JWSInputException;
+import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.JsonWebToken;
+import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
+import org.keycloak.representations.idm.ClientInitialAccessPresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
+import org.wildfly.extras.creaper.core.online.operations.admin.Administration;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
+
+public class FixedHostnameTest extends AbstractKeycloakTest {
+
+ @ArquillianResource
+ protected ContainerController controller;
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation realm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
+ testRealms.add(realm);
+
+ RealmRepresentation customHostname = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
+ customHostname.setId("hostname");
+ customHostname.setRealm("hostname");
+ customHostname.setAttributes(new HashMap<>());
+ customHostname.getAttributes().put("hostname", "custom-domain.127.0.0.1.nip.io");
+ testRealms.add(customHostname);
+ }
+
+ @Test
+ public void fixedHostname() throws Exception {
+ try {
+ assertWellKnown("test", "localhost");
+
+ configureFixedHostname();
+
+ assertWellKnown("test", "keycloak.127.0.0.1.nip.io");
+ assertWellKnown("hostname", "custom-domain.127.0.0.1.nip.io");
+
+ assertTokenIssuer("test", "keycloak.127.0.0.1.nip.io");
+ assertTokenIssuer("hostname", "custom-domain.127.0.0.1.nip.io");
+
+ assertInitialAccessTokenFromMasterRealm("test", "keycloak.127.0.0.1.nip.io");
+ assertInitialAccessTokenFromMasterRealm("hostname", "custom-domain.127.0.0.1.nip.io");
+ } finally {
+ clearFixedHostname();
+ }
+ }
+
+ private void assertInitialAccessTokenFromMasterRealm(String realm, String expectedHostname) throws JWSInputException, ClientRegistrationException {
+ ClientInitialAccessCreatePresentation rep = new ClientInitialAccessCreatePresentation();
+ rep.setCount(1);
+ rep.setExpiration(10000);
+
+ ClientInitialAccessPresentation initialAccess = adminClient.realm(realm).clientInitialAccess().create(rep);
+ JsonWebToken token = new JWSInput(initialAccess.getToken()).readJsonContent(JsonWebToken.class);
+ assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm, token.getIssuer());
+
+ ClientRegistration clientReg = ClientRegistration.create().url(suiteContext.getAuthServerInfo().getContextRoot() + "/auth", realm).build();
+ clientReg.auth(Auth.token(initialAccess.getToken()));
+
+ ClientRepresentation client = new ClientRepresentation();
+ client.setEnabled(true);
+ ClientRepresentation response = clientReg.create(client);
+
+ String registrationAccessToken = response.getRegistrationAccessToken();
+ JsonWebToken registrationToken = new JWSInput(registrationAccessToken).readJsonContent(JsonWebToken.class);
+ assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm, registrationToken.getIssuer());
+ }
+
+ private void assertTokenIssuer(String realm, String expectedHostname) throws JWSInputException, IOException {
+ oauth.baseUrl("http://" + expectedHostname + ":8180/auth");
+
+ OAuthClient.AuthorizationEndpointResponse response = oauth.realm(realm).doLogin("test-user@localhost", "password");
+
+ OAuthClient.AccessTokenResponse tokenResponse = oauth.baseUrl(OAuthClient.AUTH_SERVER_ROOT).doAccessTokenRequest(response.getCode(), "password");
+
+ AccessToken token = new JWSInput(tokenResponse.getAccessToken()).readJsonContent(AccessToken.class);
+ assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm, token.getIssuer());
+
+ String introspection = oauth.introspectAccessTokenWithClientCredential(oauth.getClientId(), "password", tokenResponse.getAccessToken());
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode introspectionNode = objectMapper.readTree(introspection);
+ assertTrue(introspectionNode.get("active").asBoolean());
+ assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm, introspectionNode.get("iss").asText());
+ }
+
+ private void assertWellKnown(String realm, String expectedHostname) {
+ OIDCConfigurationRepresentation config = oauth.doWellKnownRequest(realm);
+ assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm + "/protocol/openid-connect/token", config.getTokenEndpoint());
+ }
+
+ private void configureFixedHostname() throws Exception {
+ if (suiteContext.getAuthServerInfo().isUndertow()) {
+ configureUndertow("fixed", "keycloak.127.0.0.1.nip.io", "8180", "8543");
+ } else if (suiteContext.getAuthServerInfo().isJBossBased()) {
+ configureWildFly("fixed", "keycloak.127.0.0.1.nip.io", "8180", "8543");
+ } else {
+ throw new RuntimeException("Don't know how to config");
+ }
+
+ reconnectAdminClient();
+
+ }
+
+ private void clearFixedHostname() throws Exception {
+ if (suiteContext.getAuthServerInfo().isUndertow()) {
+ configureUndertow("request", "localhost", "-1", "-1");
+ } else if (suiteContext.getAuthServerInfo().isJBossBased()) {
+ configureWildFly("request", "localhost", "-1", "-1");
+ } else {
+ throw new RuntimeException("Don't know how to config");
+ }
+
+ reconnectAdminClient();
+ }
+
+ private void configureUndertow(String provider, String hostname, String httpPort, String httpsPort) {
+ controller.stop(suiteContext.getAuthServerInfo().getQualifier());
+
+ System.setProperty("keycloak.hostname.provider", provider);
+ System.setProperty("keycloak.hostname.fixed.hostname", hostname);
+ System.setProperty("keycloak.hostname.fixed.httpPort", httpPort);
+ System.setProperty("keycloak.hostname.fixed.httpsPort", httpsPort);
+
+ controller.start(suiteContext.getAuthServerInfo().getQualifier());
+ }
+
+ private void configureWildFly(String provider, String hostname, String httpPort, String httpsPort) throws Exception {
+ OnlineManagementClient client = AuthServerTestEnricher.getManagementClient();
+ Administration administration = new Administration(client);
+
+ client.execute("/subsystem=keycloak-server/spi=hostname:write-attribute(name=default-provider, value=" + provider + ")");
+ client.execute("/subsystem=keycloak-server/spi=hostname/provider=fixed:write-attribute(name=properties.hostname,value=" + hostname + ")");
+ client.execute("/subsystem=keycloak-server/spi=hostname/provider=fixed:write-attribute(name=properties.httpPort,value=" + httpPort + ")");
+ client.execute("/subsystem=keycloak-server/spi=hostname/provider=fixed:write-attribute(name=properties.httpsPort,value=" + httpsPort + ")");
+
+ administration.reloadIfRequired();
+
+ client.close();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index 3bb4d2a..bc06479 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -1,4 +1,15 @@
{
+
+ "hostname": {
+ "provider": "${keycloak.hostname.provider:request}",
+
+ "fixed": {
+ "hostname": "${keycloak.hostname.fixed.hostname:localhost}",
+ "httpPort": "${keycloak.hostname.fixed.httpPort:-1}",
+ "httpsPort": "${keycloak.hostname.fixed.httpPorts:-1}"
+ }
+ },
+
"admin": {
"realm": "master"
},
@@ -85,7 +96,7 @@
"connectionsJpa": {
"default": {
- "url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test;MVCC=TRUE}",
+ "url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test;MVCC=TRUE;DB_CLOSE_DELAY=-1}",
"driver": "${keycloak.connectionsJpa.driver:org.h2.Driver}",
"driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
"user": "${keycloak.connectionsJpa.user:sa}",
diff --git a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json
index eb58977..d7aa39a 100755
--- a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json
@@ -1,4 +1,15 @@
{
+
+ "hostname": {
+ "provider": "request",
+
+ "fixed": {
+ "hostname": "localhost",
+ "httpPort": "-1",
+ "httpsPort": "-1"
+ }
+ },
+
"admin": {
"realm": "master"
},
@@ -61,7 +72,6 @@
"default": {}
},
-
"connectionsJpa": {
"default": {
"url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test;DB_CLOSE_DELAY=-1}",
@@ -114,4 +124,4 @@
}
}
-}
\ No newline at end of file
+}
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 6f45c53..93d19b3 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -108,6 +108,8 @@ sso-session-idle.tooltip=Time a session is allowed to be idle before it expires.
sso-session-max.tooltip=Max time before a session is expired. Tokens and browser sessions are invalidated when a session is expired.
offline-session-idle=Offline Session Idle
offline-session-idle.tooltip=Time an offline session is allowed to be idle before it expires. You need to use offline token to refresh at least once within this period, otherwise offline session will expire.
+realm-detail.hostname=Hostname
+realm-detail.hostname.tooltip=Set the hostname for the realm. Use in combination with the fixed hostname provider to override the server hostname for a specific realm.
## KEYCLOAK-7688 Offline Session Max for Offline Token
offline-session-max-limited=Offline Session Max Limited
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
index dadb0c7..622bbe8 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
@@ -23,6 +23,14 @@
</div>
</div>
+ <div class="form-group" data-ng-show="serverInfo.listProviderIds('hostname').includes('fixed')">
+ <label class="col-md-2 control-label" for="name">{{:: 'realm-detail.hostname' | translate}}</label>
+ <div class="col-md-6">
+ <input class="form-control" type="text" id="hostname" name="hostname" data-ng-model="realm.attributes.hostname">
+ </div>
+ <kc-tooltip>{{:: 'realm-detail.hostname.tooltip' | translate}}</kc-tooltip>
+ </div>
+
<div class="form-group">
<label class="col-md-2 control-label" for="enabled">{{:: 'enabled' | translate}}</label>
<div class="col-md-6">
travis-run-tests.sh 2(+1 -1)
diff --git a/travis-run-tests.sh b/travis-run-tests.sh
index c731f84..6630ecf 100755
--- a/travis-run-tests.sh
+++ b/travis-run-tests.sh
@@ -77,7 +77,7 @@ if [ $1 == "server-group3" ]; then
fi
if [ $1 == "server-group4" ]; then
- run-server-tests org.keycloak.testsuite.k*.**.*Test,org.keycloak.testsuite.m*.**.*Test,org.keycloak.testsuite.o*.**.*Test,org.keycloak.testsuite.s*.**.*Test
+ run-server-tests org.keycloak.testsuite.k*.**.*Test,org.keycloak.testsuite.m*.**.*Test,org.keycloak.testsuite.o*.**.*Test,org.keycloak.testsuite.s*.**.*Test,org.keycloak.testsuite.u*.**.*Test
fi
if [ $1 == "crossdc-server" ]; then
diff --git a/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties b/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
index 7cfa64a..aaecc2c 100644
--- a/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
+++ b/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
@@ -77,4 +77,14 @@ keycloak.server.subsys.default.config=\
<default-provider>${keycloak.x509cert.lookup.provider:default}</default-provider>\
<provider name="default" enabled="true"/>\
</spi>\
+ <spi name="hostname">\
+ <default-provider>request</default-provider>\
+ <provider name="fixed" enabled="true">\
+ <properties>\
+ <property name="hostname" value="localhost"/>\
+ <property name="httpPort" value="-1"/>\
+ <property name="httpsPort" value="-1"/>\
+ </properties>\
+ </provider>\
+ </spi>\
</subsystem>\
diff --git a/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli b/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
index f76cd45..26c6a8a 100644
--- a/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
+++ b/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
@@ -1,5 +1,7 @@
/subsystem=keycloak-server:add(web-context=auth,master-realm-name=master,scheduled-task-interval=900)
/subsystem=keycloak-server/theme=defaults/:add(dir=${jboss.home.dir}/themes,staticMaxAge=2592000,cacheTemplates=true,cacheThemes=true)
+/subsystem=keycloak-server/spi=hostname/:add(default-provider=request)
+/subsystem=keycloak-server/spi=hostname/provider=fixed/:add(properties={hostname => "localhost",httpPort => "-1",httpsPort => "-1"},enabled=true)
/subsystem=keycloak-server/spi=eventsStore/:add
/subsystem=keycloak-server/spi=eventsStore/provider=jpa/:add(properties={exclude-events => "[\"REFRESH_TOKEN\"]"},enabled=true)
/subsystem=keycloak-server/spi=userCache/:add