KEYCLOAK-8310 Add support to set fixed scheme on fixed hostname provider

This commit is contained in:
stianst 2018-10-04 12:05:44 +02:00 committed by Stian Thorgersen
parent 0d9b1e73b8
commit 86a2f28561
7 changed files with 90 additions and 33 deletions

View file

@ -31,6 +31,7 @@ public class KeycloakUriInfo implements UriInfo {
private final UriInfo delegate;
private final String hostname;
private final String scheme;
private final int port;
private URI absolutePath;
@ -41,8 +42,10 @@ public class KeycloakUriInfo implements UriInfo {
this.delegate = delegate;
HostnameProvider hostnameProvider = session.getProvider(HostnameProvider.class);
this.scheme = hostnameProvider.getScheme(delegate);
this.hostname = hostnameProvider.getHostname(delegate);
this.port = hostnameProvider.getPort(delegate);
}
public UriInfo getDelegate() {
@ -52,7 +55,7 @@ public class KeycloakUriInfo implements UriInfo {
@Override
public URI getRequestUri() {
if (requestURI == null) {
requestURI = delegate.getRequestUriBuilder().host(hostname).port(port).build();
requestURI = delegate.getRequestUriBuilder().scheme(scheme).host(hostname).port(port).build();
}
return requestURI;
}
@ -65,7 +68,7 @@ public class KeycloakUriInfo implements UriInfo {
@Override
public URI getAbsolutePath() {
if (absolutePath == null) {
absolutePath = delegate.getAbsolutePathBuilder().host(hostname).port(port).build();
absolutePath = delegate.getAbsolutePathBuilder().scheme(scheme).host(hostname).port(port).build();
}
return absolutePath;
}
@ -78,7 +81,7 @@ public class KeycloakUriInfo implements UriInfo {
@Override
public URI getBaseUri() {
if (baseURI == null) {
baseURI = delegate.getBaseUriBuilder().host(hostname).port(port).build();
baseURI = delegate.getBaseUriBuilder().scheme(scheme).host(hostname).port(port).build();
}
return baseURI;
}

View file

@ -23,6 +23,8 @@ import javax.ws.rs.core.UriInfo;
public interface HostnameProvider extends Provider {
String getScheme(UriInfo originalUriInfo);
/**
* 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!

View file

@ -10,16 +10,23 @@ public class FixedHostnameProvider implements HostnameProvider {
private final KeycloakSession session;
private final String globalHostname;
private final String scheme;
private final int httpPort;
private final int httpsPort;
public FixedHostnameProvider(KeycloakSession session, String globalHostname, int httpPort, int httpsPort) {
public FixedHostnameProvider(KeycloakSession session, String scheme, String globalHostname, int httpPort, int httpsPort) {
this.session = session;
this.scheme = scheme;
this.globalHostname = globalHostname;
this.httpPort = httpPort;
this.httpsPort = httpsPort;
}
@Override
public String getScheme(UriInfo originalUriInfo) {
return scheme != null ? scheme : originalUriInfo.getRequestUri().getScheme();
}
@Override
public String getHostname(UriInfo originalUriInfo) {
RealmModel realm = session.getContext().getRealm();

View file

@ -10,10 +10,11 @@ public class FixedHostnameProviderFactory implements HostnameProviderFactory {
private String hostname;
private int httpPort;
private int httpsPort;
private String scheme;
@Override
public HostnameProvider create(KeycloakSession session) {
return new FixedHostnameProvider(session, hostname, httpPort, httpsPort);
return new FixedHostnameProvider(session, scheme, hostname, httpPort, httpsPort);
}
@Override
@ -25,6 +26,10 @@ public class FixedHostnameProviderFactory implements HostnameProviderFactory {
this.httpPort = config.getInt("httpPort", -1);
this.httpsPort = config.getInt("httpsPort", -1);
this.scheme = config.get("scheme");
if (scheme != null && scheme.trim().isEmpty()) {
scheme = null;
}
}
@Override

View file

@ -6,6 +6,11 @@ import javax.ws.rs.core.UriInfo;
public class RequestHostnameProvider implements HostnameProvider {
@Override
public String getScheme(UriInfo originalUriInfo) {
return originalUriInfo.getRequestUri().getScheme();
}
@Override
public String getHostname(UriInfo originalUriInfo) {
return originalUriInfo.getBaseUri().getHost();

View file

@ -19,6 +19,7 @@ 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.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
import org.wildfly.extras.creaper.core.online.operations.admin.Administration;
@ -46,37 +47,62 @@ public class FixedHostnameTest extends AbstractKeycloakTest {
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 {
oauth.clientId("direct-grant");
try {
assertWellKnown("test", "localhost");
assertWellKnown("test", "http","localhost");
configureFixedHostname();
configureFixedHostname(null);
assertWellKnown("test", "keycloak.127.0.0.1.nip.io");
assertWellKnown("hostname", "custom-domain.127.0.0.1.nip.io");
assertWellKnown("test", "http","keycloak.127.0.0.1.nip.io");
assertWellKnown("hostname", "http","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");
assertTokenIssuer("test", "http","keycloak.127.0.0.1.nip.io");
assertTokenIssuer("hostname", "http","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");
assertInitialAccessTokenFromMasterRealm("test","http","keycloak.127.0.0.1.nip.io");
assertInitialAccessTokenFromMasterRealm("hostname", "http","custom-domain.127.0.0.1.nip.io");
} finally {
clearFixedHostname();
}
}
private void assertInitialAccessTokenFromMasterRealm(String realm, String expectedHostname) throws JWSInputException, ClientRegistrationException {
@Test
public void fixedHostnameAndScheme() throws Exception {
oauth.clientId("direct-grant");
try {
assertWellKnown("test", "http","localhost");
configureFixedHostname("https");
assertWellKnown("test", "https","keycloak.127.0.0.1.nip.io");
assertWellKnown("hostname", "https","custom-domain.127.0.0.1.nip.io");
assertTokenIssuer("test", "https","keycloak.127.0.0.1.nip.io");
assertTokenIssuer("hostname", "https","custom-domain.127.0.0.1.nip.io");
assertInitialAccessTokenFromMasterRealm("test", "https", "keycloak.127.0.0.1.nip.io");
assertInitialAccessTokenFromMasterRealm("hostname", "https", "custom-domain.127.0.0.1.nip.io");
} finally {
clearFixedHostname();
}
}
private void assertInitialAccessTokenFromMasterRealm(String realm, String expectedScheme, 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());
assertEquals(expectedScheme + "://" + expectedHostname + ":8180/auth/realms/" + realm, token.getIssuer());
ClientRegistration clientReg = ClientRegistration.create().url(suiteContext.getAuthServerInfo().getContextRoot() + "/auth", realm).build();
clientReg.auth(Auth.token(initialAccess.getToken()));
@ -87,36 +113,34 @@ public class FixedHostnameTest extends AbstractKeycloakTest {
String registrationAccessToken = response.getRegistrationAccessToken();
JsonWebToken registrationToken = new JWSInput(registrationAccessToken).readJsonContent(JsonWebToken.class);
assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm, registrationToken.getIssuer());
assertEquals(expectedScheme + "://" + expectedHostname + ":8180/auth/realms/" + realm, registrationToken.getIssuer());
}
private void assertTokenIssuer(String realm, String expectedHostname) throws JWSInputException, IOException {
oauth.baseUrl("http://" + expectedHostname + ":8180/auth");
private void assertTokenIssuer(String realm, String expectedScheme, String expectedHostname) throws Exception {
oauth.realm(realm);
OAuthClient.AuthorizationEndpointResponse response = oauth.realm(realm).doLogin("test-user@localhost", "password");
OAuthClient.AccessTokenResponse tokenResponse = oauth.baseUrl(OAuthClient.AUTH_SERVER_ROOT).doAccessTokenRequest(response.getCode(), "password");
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
AccessToken token = new JWSInput(tokenResponse.getAccessToken()).readJsonContent(AccessToken.class);
assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm, token.getIssuer());
assertEquals(expectedScheme + "://" + 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());
assertEquals(expectedScheme + "://" + expectedHostname + ":8180/auth/realms/" + realm, introspectionNode.get("iss").asText());
}
private void assertWellKnown(String realm, String expectedHostname) {
private void assertWellKnown(String realm, String expectedScheme, String expectedHostname) {
OIDCConfigurationRepresentation config = oauth.doWellKnownRequest(realm);
assertEquals("http://" + expectedHostname + ":8180/auth/realms/" + realm + "/protocol/openid-connect/token", config.getTokenEndpoint());
assertEquals(expectedScheme + "://" + expectedHostname + ":8180/auth/realms/" + realm + "/protocol/openid-connect/token", config.getTokenEndpoint());
}
private void configureFixedHostname() throws Exception {
private void configureFixedHostname(String scheme) throws Exception {
if (suiteContext.getAuthServerInfo().isUndertow()) {
configureUndertow("fixed", "keycloak.127.0.0.1.nip.io");
configureUndertow("fixed", "keycloak.127.0.0.1.nip.io", scheme);
} else if (suiteContext.getAuthServerInfo().isJBossBased()) {
configureWildFly("fixed", "keycloak.127.0.0.1.nip.io");
configureWildFly("fixed", "keycloak.127.0.0.1.nip.io", scheme);
} else {
throw new RuntimeException("Don't know how to config");
}
@ -127,9 +151,9 @@ public class FixedHostnameTest extends AbstractKeycloakTest {
private void clearFixedHostname() throws Exception {
if (suiteContext.getAuthServerInfo().isUndertow()) {
configureUndertow("request", "localhost");
configureUndertow("request", "localhost", null);
} else if (suiteContext.getAuthServerInfo().isJBossBased()) {
configureWildFly("request", "localhost");
configureWildFly("request", "localhost", null);
} else {
throw new RuntimeException("Don't know how to config");
}
@ -137,21 +161,31 @@ public class FixedHostnameTest extends AbstractKeycloakTest {
reconnectAdminClient();
}
private void configureUndertow(String provider, String hostname) {
private void configureUndertow(String provider, String hostname, String scheme) {
controller.stop(suiteContext.getAuthServerInfo().getQualifier());
System.setProperty("keycloak.hostname.provider", provider);
System.setProperty("keycloak.hostname.fixed.hostname", hostname);
if (scheme != null) {
System.setProperty("keycloak.hostname.fixed.scheme", scheme);
} else {
System.getProperties().remove("keycloak.hostname.fixed.scheme");
}
controller.start(suiteContext.getAuthServerInfo().getQualifier());
}
private void configureWildFly(String provider, String hostname) throws Exception {
private void configureWildFly(String provider, String hostname, String scheme) 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 + ")");
if (scheme != null) {
client.execute("/subsystem=keycloak-server/spi=hostname/provider=fixed:write-attribute(name=properties.scheme,value=" + scheme + ")");
} else {
client.execute("/subsystem=keycloak-server/spi=hostname/provider=fixed:map-remove(name=properties,key=scheme)");
}
administration.reloadIfRequired();

View file

@ -6,7 +6,8 @@
"fixed": {
"hostname": "${keycloak.hostname.fixed.hostname:localhost}",
"httpPort": "${keycloak.hostname.fixed.httpPort:-1}",
"httpsPort": "${keycloak.hostname.fixed.httpPorts:-1}"
"httpsPort": "${keycloak.hostname.fixed.httpPorts:-1}",
"scheme": "${keycloak.hostname.fixed.scheme:}"
}
},