keycloak-aplcache

Details

diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
index acb4243..fd54ce2 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
@@ -10,14 +10,14 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder;
  * @version $Revision: 1 $
  */
 @JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
-        "resource", "credentials",
+        "resource", "public-client", "credentials",
         "use-resource-role-mappings",
         "enable-cors", "cors-max-age", "cors-allowed-methods",
         "expose-token", "bearer-only",
         "connection-pool-size",
         "allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
         "client-keystore", "client-keystore-password", "client-key-password",
-        "use-hostname-for-local-requests", "local-requests-scheme", "local-requests-port"
+        "auth-server-url-for-backend-requests"
 })
 public class AdapterConfig extends BaseAdapterConfig {
 
@@ -37,12 +37,8 @@ public class AdapterConfig extends BaseAdapterConfig {
     protected String clientKeyPassword;
     @JsonProperty("connection-pool-size")
     protected int connectionPoolSize = 20;
-    @JsonProperty("use-hostname-for-local-requests")
-    protected boolean useHostnameForLocalRequests;
-    @JsonProperty("local-requests-scheme")
-    protected String localRequestsScheme = "http";
-    @JsonProperty("local-requests-port")
-    protected int localRequestsPort = 8080;
+    @JsonProperty("auth-server-url-for-backend-requests")
+    protected String authServerUrlForBackendRequests;
 
     public boolean isAllowAnyHostname() {
         return allowAnyHostname;
@@ -108,27 +104,11 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.connectionPoolSize = connectionPoolSize;
     }
 
-    public boolean isUseHostnameForLocalRequests() {
-        return useHostnameForLocalRequests;
+    public String getAuthServerUrlForBackendRequests() {
+        return authServerUrlForBackendRequests;
     }
 
-    public void setUseHostnameForLocalRequests(boolean useHostnameForLocalRequests) {
-        this.useHostnameForLocalRequests = useHostnameForLocalRequests;
-    }
-
-    public String getLocalRequestsScheme() {
-        return localRequestsScheme;
-    }
-
-    public void setLocalRequestsScheme(String localRequestsScheme) {
-        this.localRequestsScheme = localRequestsScheme;
-    }
-
-    public int getLocalRequestsPort() {
-        return localRequestsPort;
-    }
-
-    public void setLocalRequestsPort(int localRequestsPort) {
-        this.localRequestsPort = localRequestsPort;
+    public void setAuthServerUrlForBackendRequests(String authServerUrlForBackendRequests) {
+        this.authServerUrlForBackendRequests = authServerUrlForBackendRequests;
     }
 }
diff --git a/core/src/main/java/org/keycloak/util/JsonSerialization.java b/core/src/main/java/org/keycloak/util/JsonSerialization.java
index d0bf6a0..0575e13 100755
--- a/core/src/main/java/org/keycloak/util/JsonSerialization.java
+++ b/core/src/main/java/org/keycloak/util/JsonSerialization.java
@@ -17,6 +17,7 @@ import java.io.OutputStream;
 public class JsonSerialization {
     public static final ObjectMapper mapper = new ObjectMapper();
     public static final ObjectMapper prettyMapper = new ObjectMapper();
+    public static final ObjectMapper sysPropertiesAwareMapper = new ObjectMapper(new SystemPropertiesJsonParserFactory());
 
     static {
         mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
@@ -49,6 +50,10 @@ public class JsonSerialization {
         return mapper.readValue(bytes, type);
     }
 
+    public static <T> T readValueAndReplaceSysProperties(InputStream bytes, Class<T> type) throws IOException {
+        return sysPropertiesAwareMapper.readValue(bytes, type);
+    }
+
 
 
 }
diff --git a/core/src/main/java/org/keycloak/util/SystemPropertiesJsonParserFactory.java b/core/src/main/java/org/keycloak/util/SystemPropertiesJsonParserFactory.java
new file mode 100644
index 0000000..52f893d
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/SystemPropertiesJsonParserFactory.java
@@ -0,0 +1,51 @@
+package org.keycloak.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.io.IOContext;
+import org.codehaus.jackson.map.MappingJsonFactory;
+import org.codehaus.jackson.util.JsonParserDelegate;
+
+/**
+ * Provides replacing of system properties for parsed values
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
+
+    @Override
+    protected JsonParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
+        JsonParser delegate = super._createJsonParser(data, offset, len, ctxt);
+        return new SystemPropertiesAwareJsonParser(delegate);
+    }
+
+    @Override
+    protected JsonParser _createJsonParser(Reader r, IOContext ctxt) throws IOException {
+        JsonParser delegate = super._createJsonParser(r, ctxt);
+        return new SystemPropertiesAwareJsonParser(delegate);
+    }
+
+    @Override
+    protected JsonParser _createJsonParser(InputStream in, IOContext ctxt) throws IOException {
+        JsonParser delegate = super._createJsonParser(in, ctxt);
+        return new SystemPropertiesAwareJsonParser(delegate);
+    }
+
+
+
+    public static class SystemPropertiesAwareJsonParser extends JsonParserDelegate {
+
+        public SystemPropertiesAwareJsonParser(JsonParser d) {
+            super(d);
+        }
+
+        @Override
+        public String getText() throws IOException {
+            String orig = super.getText();
+            return StringPropertyReplacer.replaceProperties(orig);
+        }
+    }
+}
diff --git a/core/src/test/java/org/keycloak/JsonParserTest.java b/core/src/test/java/org/keycloak/JsonParserTest.java
new file mode 100644
index 0000000..8cdbd41
--- /dev/null
+++ b/core/src/test/java/org/keycloak/JsonParserTest.java
@@ -0,0 +1,33 @@
+package org.keycloak;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.representations.adapters.config.AdapterConfig;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class JsonParserTest {
+
+    @Test
+    public void testParsingSystemProps() throws IOException {
+        System.setProperty("my.host", "foo");
+        System.setProperty("con.pool.size", "200");
+        System.setProperty("allow.any.hostname", "true");
+
+        InputStream is = getClass().getClassLoader().getResourceAsStream("keycloak.json");
+
+        AdapterConfig config = JsonSerialization.readValueAndReplaceSysProperties(is, AdapterConfig.class);
+        Assert.assertEquals("http://foo:8080/auth", config.getAuthServerUrl());
+        Assert.assertEquals("external", config.getSslRequired());
+        Assert.assertEquals("angular-product${non.existing}", config.getResource());
+        Assert.assertTrue(config.isPublicClient());
+        Assert.assertTrue(config.isAllowAnyHostname());
+        Assert.assertEquals(100, config.getCorsMaxAge());
+        Assert.assertEquals(200, config.getConnectionPoolSize());
+    }
+}
diff --git a/core/src/test/resources/keycloak.json b/core/src/test/resources/keycloak.json
new file mode 100644
index 0000000..b0a8935
--- /dev/null
+++ b/core/src/test/resources/keycloak.json
@@ -0,0 +1,9 @@
+{
+  "auth-server-url" : "http://${my.host}:8080/auth",
+  "ssl-required" : "external",
+  "resource" : "angular-product${non.existing}",
+  "public-client" : true,
+  "allow-any-hostname": "${allow.any.hostname}",
+  "cors-max-age": 100,
+  "connection-pool-size": "${con.pool.size}"
+}
\ No newline at end of file
diff --git a/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json
index ca0707e..c2241b3 100755
--- a/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json
@@ -7,6 +7,5 @@
     "expose-token": true,
     "credentials": {
         "secret": "password"
-    },
-    "use-hostname-for-local-requests": false
+    }
 }
diff --git a/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json
index c1ae517..0a86c04 100755
--- a/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json
@@ -6,6 +6,5 @@
   "ssl-required" : "external",
   "credentials" : {
       "secret": "password"
-  },
-  "use-hostname-for-local-requests": false
+  }
 }
diff --git a/examples/demo-template/third-party/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/third-party/src/main/webapp/WEB-INF/keycloak.json
index 14bbd79..559df05 100755
--- a/examples/demo-template/third-party/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/third-party/src/main/webapp/WEB-INF/keycloak.json
@@ -5,6 +5,5 @@
   "ssl-required" : "external",
    "credentials" : {
        "secret": "password"
-   },
-   "use-hostname-for-local-requests": false
+   }
 }
\ No newline at end of file
diff --git a/examples/demo-template/third-party-cdi/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/third-party-cdi/src/main/webapp/WEB-INF/keycloak.json
index 14bbd79..559df05 100755
--- a/examples/demo-template/third-party-cdi/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/demo-template/third-party-cdi/src/main/webapp/WEB-INF/keycloak.json
@@ -5,6 +5,5 @@
   "ssl-required" : "external",
    "credentials" : {
        "secret": "password"
-   },
-   "use-hostname-for-local-requests": false
+   }
 }
\ No newline at end of file
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index 9709294..db284b1 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -7,7 +7,6 @@ import org.keycloak.enums.RelativeUrlsUsed;
 import org.keycloak.enums.SslRequired;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.util.KeycloakUriBuilder;
-import org.keycloak.util.UriUtils;
 
 import java.net.URI;
 import java.security.PublicKey;
@@ -87,15 +86,18 @@ public class KeycloakDeployment {
 
         URI uri = URI.create(authServerBaseUrl);
         if (uri.getHost() == null) {
-            if (config.isUseHostnameForLocalRequests()) {
+            String authServerURLForBackendReqs = config.getAuthServerUrlForBackendRequests();
+            if (authServerURLForBackendReqs != null) {
                 relativeUrls = RelativeUrlsUsed.BROWSER_ONLY;
 
-                KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(authServerBaseUrl);
-                serverBuilder.host(UriUtils.getHostName()).port(config.getLocalRequestsPort()).scheme(config.getLocalRequestsScheme());
+                KeycloakUriBuilder serverBuilder = KeycloakUriBuilder.fromUri(authServerURLForBackendReqs);
+                if (serverBuilder.getHost() == null || serverBuilder.getScheme() == null) {
+                    throw new IllegalStateException("Relative URL not supported for auth-server-url-for-backend-requests option. URL used: "
+                            + authServerURLForBackendReqs + ", Client: " + config.getResource());
+                }
                 resolveNonBrowserUrls(serverBuilder);
             } else {
                 relativeUrls = RelativeUrlsUsed.ALL_REQUESTS;
-                return;
             }
         } else {
             // We have absolute URI in config
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
index 0a3ab33..5aa996b 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
@@ -6,6 +6,7 @@ import org.jboss.logging.Logger;
 import org.keycloak.enums.SslRequired;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.util.PemUtils;
+import org.keycloak.util.SystemPropertiesJsonParserFactory;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -79,7 +80,7 @@ public class KeycloakDeploymentBuilder {
     }
 
     public static KeycloakDeployment build(InputStream is) {
-        ObjectMapper mapper = new ObjectMapper();
+        ObjectMapper mapper = new ObjectMapper(new SystemPropertiesJsonParserFactory());
         mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
         AdapterConfig adapterConfig = null;
         try {
diff --git a/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClientBuilder.java b/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClientBuilder.java
index e1ccd5d..ba41356 100755
--- a/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClientBuilder.java
+++ b/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClientBuilder.java
@@ -25,7 +25,7 @@ public class ServletOAuthClientBuilder {
 
     public static AdapterConfig getAdapterConfig(InputStream is) {
         try {
-            return JsonSerialization.readValue(is, AdapterConfig.class);
+            return JsonSerialization.readValueAndReplaceSysProperties(is, AdapterConfig.class);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
@@ -57,13 +57,17 @@ public class ServletOAuthClientBuilder {
 
         String authUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGIN_PATH).build(adapterConfig.getRealm()).toString();
 
-        KeycloakUriBuilder tokenUrlBuilder = serverBuilder.clone();
-        KeycloakUriBuilder refreshUrlBuilder = serverBuilder.clone();
+        KeycloakUriBuilder tokenUrlBuilder;
+        KeycloakUriBuilder refreshUrlBuilder;
 
         if (useRelative == RelativeUrlsUsed.BROWSER_ONLY) {
             // Use absolute URI for refreshToken and codeToToken requests
-            tokenUrlBuilder.scheme(adapterConfig.getLocalRequestsScheme()).host(UriUtils.getHostName()).port(adapterConfig.getLocalRequestsPort());
-            refreshUrlBuilder.scheme(adapterConfig.getLocalRequestsScheme()).host(UriUtils.getHostName()).port(adapterConfig.getLocalRequestsPort());
+            KeycloakUriBuilder nonBrowsersServerBuilder = KeycloakUriBuilder.fromUri(adapterConfig.getAuthServerUrlForBackendRequests());
+            tokenUrlBuilder = nonBrowsersServerBuilder.clone();
+            refreshUrlBuilder = nonBrowsersServerBuilder.clone();
+        } else {
+            tokenUrlBuilder = serverBuilder.clone();
+            refreshUrlBuilder = serverBuilder.clone();
         }
         String tokenUrl = tokenUrlBuilder.path(ServiceUrlConstants.TOKEN_SERVICE_ACCESS_CODE_PATH).build(adapterConfig.getRealm()).toString();
         String refreshUrl = refreshUrlBuilder.path(ServiceUrlConstants.TOKEN_SERVICE_REFRESH_PATH).build(adapterConfig.getRealm()).toString();
@@ -74,7 +78,7 @@ public class ServletOAuthClientBuilder {
 
     private static RelativeUrlsUsed relativeUrls(KeycloakUriBuilder serverBuilder, AdapterConfig adapterConfig) {
         if (serverBuilder.clone().getHost() == null) {
-            return (adapterConfig.isUseHostnameForLocalRequests()) ? RelativeUrlsUsed.BROWSER_ONLY : RelativeUrlsUsed.ALL_REQUESTS;
+            return (adapterConfig.getAuthServerUrlForBackendRequests() != null) ? RelativeUrlsUsed.BROWSER_ONLY : RelativeUrlsUsed.ALL_REQUESTS;
         } else {
             return RelativeUrlsUsed.NEVER;
         }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowUserSessionManagement.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowUserSessionManagement.java
index b7112d7..352b3a3 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowUserSessionManagement.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowUserSessionManagement.java
@@ -151,6 +151,7 @@ public class UndertowUserSessionManagement implements SessionListener {
     public void sessionDestroyed(Session session, HttpServerExchange exchange, SessionDestroyedReason reason) {
         // Look up the single session id associated with this session (if any)
         String username = getUsernameFromSession(session);
+        log.debugf("Session destroyed for user: %s, sessionId: %s", username, session.getId());
         if (username == null) return;
         String sessionId = session.getId();
         UserSessions userSessions = userSessionMap.get(username);
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index 903b5e2..7d23393 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -23,6 +23,7 @@ import org.keycloak.representations.adapters.action.UserStats;
 import org.keycloak.representations.adapters.action.UserStatsAction;
 import org.keycloak.services.util.HttpClientBuilder;
 import org.keycloak.services.util.ResolveRelative;
+import org.keycloak.util.StringPropertyReplacer;
 import org.keycloak.util.Time;
 
 import javax.ws.rs.core.MediaType;
@@ -108,8 +109,10 @@ public class ResourceAdminManager {
         }
 
         // this is to support relative admin urls when keycloak and applications are deployed on the same machine
-        return ResolveRelative.resolveRelativeUri(requestUri, mgmtUrl);
+        String absoluteURI = ResolveRelative.resolveRelativeUri(requestUri, mgmtUrl);
 
+        // this is for resolving URI like "http://${jboss.home.name}:8080/..." in order to send request to same machine and avoid LB in cluster env
+        return StringPropertyReplacer.replaceProperties(absoluteURI);
     }
 
     public UserStats getUserStats(URI requestUri, RealmModel realm, ApplicationModel application, UserModel user) {
diff --git a/testsuite/docker-cluster/wildfly/deploy-examples.sh b/testsuite/docker-cluster/wildfly/deploy-examples.sh
index 7da2946..4cafe69 100644
--- a/testsuite/docker-cluster/wildfly/deploy-examples.sh
+++ b/testsuite/docker-cluster/wildfly/deploy-examples.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# Deploy and configure all examples
+## Deploy and configure all examples
 
 # Deploy examples
 cd /keycloak-docker-cluster/examples
@@ -25,10 +25,13 @@ sed -i -e 's/false/true/' admin-access.war/WEB-INF/web.xml
 
 # Configure other examples
 for I in *.war/WEB-INF/keycloak.json; do
-  sed -i -e 's/\"use-hostname-for-local-requests\": false/\"use-hostname-for-local-requests\": true/' $I;
+  sed -i -e 's/\"\/auth\",/&\n    \"auth-server-url-for-backend-requests\": \"http:\/\/\$\{jboss.host.name\}:8080\/auth\",/' $I;
 done;
 
 # Enable distributable for customer-portal
 sed -i -e 's/<\/module-name>/&\n    <distributable \/>/' customer-portal.war/WEB-INF/web.xml
 
+# Configure testrealm.json - Enable adminUrl to access adapters on local machine
+sed -i -e 's/\"adminUrl\": \"/&http:\/\/\$\{jboss.host.name\}:8080/' /keycloak-docker-cluster/examples/testrealm.json
+