keycloak-aplcache

KEYCLOAK-4556 KEYCLOAK-5022 Only cache keycloak.js and iframe

7/4/2017 4:18:34 PM

Details

diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js
index 89b15b8..a784936 100755
--- a/adapters/oidc/js/src/main/resources/keycloak.js
+++ b/adapters/oidc/js/src/main/resources/keycloak.js
@@ -33,6 +33,13 @@
             interval: 5
         };
 
+        var scripts = document.getElementsByTagName('script');
+        for (var i = 0; i < scripts.length; i++) {
+            if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
+                kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
+            }
+        }
+
         kc.init = function (initOptions) {
             kc.authenticated = false;
 
@@ -831,6 +838,10 @@
             }
 
             var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
+            if (kc.iframeVersion) {
+                src = src + '?version=' + kc.iframeVersion;
+            }
+
             iframe.setAttribute('src', src );
             iframe.style.display = 'none';
             document.body.appendChild(iframe);
diff --git a/adapters/oidc/js/src/main/resources/login-status-iframe.html b/adapters/oidc/js/src/main/resources/login-status-iframe.html
index b1012f7..f58f76a 100755
--- a/adapters/oidc/js/src/main/resources/login-status-iframe.html
+++ b/adapters/oidc/js/src/main/resources/login-status-iframe.html
@@ -28,7 +28,7 @@
         } else  if (!init) {
             var req = new XMLHttpRequest();
 
-            var url = location.href + "/init";
+            var url = location.href.split("?")[0] + "/init";
             url += "?client_id=" + encodeURIComponent(clientId);
             url += "&origin=" + encodeURIComponent(origin);
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
index b4f8622..a478169 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.protocol.oidc.endpoints;
 
+import org.keycloak.common.Version;
 import org.keycloak.common.util.UriUtils;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
@@ -28,8 +29,10 @@ import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.CacheControl;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -42,25 +45,25 @@ import java.util.Set;
 public class LoginStatusIframeEndpoint {
 
     @Context
-    private UriInfo uriInfo;
-
-    @Context
     private KeycloakSession session;
 
-    private RealmModel realm;
-
-    public LoginStatusIframeEndpoint(RealmModel realm) {
-        this.realm = realm;
-    }
-
     @GET
     @Produces(MediaType.TEXT_HTML_UTF_8)
-    public Response getLoginStatusIframe(@QueryParam("client_id") String client_id,
-                                         @QueryParam("origin") String origin) {
+    public Response getLoginStatusIframe(@QueryParam("version") String version) {
+        CacheControl cacheControl;
+        if (version != null) {
+            if (!version.equals(Version.RESOURCES_VERSION)) {
+                return Response.status(Response.Status.NOT_FOUND).build();
+            }
+            cacheControl = CacheControlUtil.getDefaultCacheControl();
+        } else {
+            cacheControl = CacheControlUtil.noCache();
+        }
+
         InputStream resource = getClass().getClassLoader().getResourceAsStream("login-status-iframe.html");
         if (resource != null) {
             P3PHelper.addP3PHeader(session);
-            return Response.ok(resource).cacheControl(CacheControlUtil.getDefaultCacheControl()).build();
+            return Response.ok(resource).cacheControl(cacheControl).build();
         } else {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
@@ -70,6 +73,7 @@ public class LoginStatusIframeEndpoint {
     @Path("init")
     public Response preCheck(@QueryParam("client_id") String clientId, @QueryParam("origin") String origin) {
         try {
+            UriInfo uriInfo = session.getContext().getUri();
             RealmModel realm = session.getContext().getRealm();
             ClientModel client = session.realms().getClientByClientId(clientId, realm);
             if (client != null) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
index b535636..160013f 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
@@ -171,7 +171,7 @@ public class OIDCLoginProtocolService {
 
     @Path("login-status-iframe.html")
     public Object getLoginStatusIframe() {
-        LoginStatusIframeEndpoint endpoint = new LoginStatusIframeEndpoint(realm);
+        LoginStatusIframeEndpoint endpoint = new LoginStatusIframeEndpoint();
         ResteasyProviderFactory.getInstance().injectProperties(endpoint);
         return endpoint;
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/JsResource.java b/services/src/main/java/org/keycloak/services/resources/JsResource.java
index 3e41dad..404d3e4 100755
--- a/services/src/main/java/org/keycloak/services/resources/JsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/JsResource.java
@@ -17,14 +17,15 @@
 
 package org.keycloak.services.resources;
 
-import org.keycloak.Config;
 import org.keycloak.common.Version;
+import org.keycloak.services.util.CacheControlUtil;
 import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.CacheControl;
 import javax.ws.rs.core.Response;
 import java.io.InputStream;
@@ -45,37 +46,29 @@ public class JsResource {
     @GET
     @Path("/keycloak.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
-    public Response getKeycloakJs() {
-        return getJs("keycloak.js");
+    public Response getKeycloakJs(@QueryParam("version") String version) {
+        return getJs("keycloak.js", version);
     }
 
     @GET
     @Path("/{version}/keycloak.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
     public Response getKeycloakJsWithVersion(@PathParam("version") String version) {
-        if (!version.equals(Version.RESOURCES_VERSION)) {
-            return Response.status(Response.Status.NOT_FOUND).build();
-        }
-
-        return getKeycloakJs();
+        return getJs("keycloak.js", version);
     }
 
     @GET
     @Path("/keycloak.min.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
-    public Response getKeycloakMinJs() {
-        return getJs("keycloak.min.js");
+    public Response getKeycloakMinJs(@QueryParam("version") String version) {
+        return getJs("keycloak.min.js", version);
     }
 
     @GET
     @Path("/{version}/keycloak.min.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
     public Response getKeycloakMinJsWithVersion(@PathParam("version") String version) {
-        if (!version.equals(Version.RESOURCES_VERSION)) {
-            return Response.status(Response.Status.NOT_FOUND).build();
-        }
-
-        return getKeycloakMinJs();
+        return getJs("keycloak.min.js", version);
     }
 
     /**
@@ -86,46 +79,44 @@ public class JsResource {
     @GET
     @Path("/keycloak-authz.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
-    public Response getKeycloakAuthzJs() {
-        return getJs("keycloak-authz.js");
+    public Response getKeycloakAuthzJs(@QueryParam("version") String version) {
+        return getJs("keycloak-authz.js", version);
     }
 
     @GET
     @Path("/{version}/keycloak-authz.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
     public Response getKeycloakAuthzJsWithVersion(@PathParam("version") String version) {
-        if (!version.equals(Version.RESOURCES_VERSION)) {
-            return Response.status(Response.Status.NOT_FOUND).build();
-        }
-
-        return getKeycloakAuthzJs();
+        return getJs("keycloak-authz.js", version);
     }
 
     @GET
     @Path("/keycloak-authz.min.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
-    public Response getKeycloakAuthzMinJs() {
-        return getJs("keycloak-authz.min.js");
+    public Response getKeycloakAuthzMinJs(@QueryParam("version") String version) {
+        return getJs("keycloak-authz.min.js", version);
     }
 
     @GET
     @Path("/{version}/keycloak-authz.min.js")
     @Produces(MediaType.TEXT_PLAIN_JAVASCRIPT)
     public Response getKeycloakAuthzMinJsWithVersion(@PathParam("version") String version) {
-        if (!version.equals(Version.RESOURCES_VERSION)) {
-            return Response.status(Response.Status.NOT_FOUND).build();
-        }
-
-        return getKeycloakAuthzMinJs();
+        return getJs("keycloak-authz.min.js", version);
     }
 
-    private Response getJs(String name) {
+    private Response getJs(String name, String version) {
+        CacheControl cacheControl;
+        if (version != null) {
+            if (!version.equals(Version.RESOURCES_VERSION)) {
+                return Response.status(Response.Status.NOT_FOUND).build();
+            }
+            cacheControl = CacheControlUtil.getDefaultCacheControl();
+        } else {
+            cacheControl = CacheControlUtil.noCache();
+        }
+
         InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name);
         if (inputStream != null) {
-            CacheControl cacheControl = new CacheControl();
-            cacheControl.setNoTransform(false);
-            cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
-
             return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build();
         } else {
             return Response.status(Response.Status.NOT_FOUND).build();
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/runonserver/SerializationUtil.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/runonserver/SerializationUtil.java
index cd1b216..1f56a80 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/runonserver/SerializationUtil.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/runonserver/SerializationUtil.java
@@ -50,7 +50,7 @@ public class SerializationUtil {
             oos.writeObject(t);
             oos.close();
 
-            return Base64.encodeBytes(os.toByteArray());
+            return "EXCEPTION:" + Base64.encodeBytes(os.toByteArray());
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -58,6 +58,7 @@ public class SerializationUtil {
 
     public static Throwable decodeException(String result) {
         try {
+            result = result.substring("EXCEPTION:".length());
             byte[] bytes = Base64.decode(result);
             ByteArrayInputStream is = new ByteArrayInputStream(bytes);
             ObjectInputStream ois = new ObjectInputStream(is);
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
index 684c383..533dfbb 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
@@ -101,7 +101,7 @@ public class KeycloakTestingClient {
             String encoded = SerializationUtil.encode(function);
 
             String result = testing(realm != null ? realm : "master").runOnServer(encoded);
-            if (result != null && !result.isEmpty() && !result.trim().startsWith("{")) {
+            if (result != null && !result.isEmpty() && result.trim().startsWith("EXCEPTION:")) {
                 Throwable t = SerializationUtil.decodeException(result);
                 if (t instanceof AssertionError) {
                     throw (AssertionError) t;
@@ -117,7 +117,7 @@ public class KeycloakTestingClient {
             String encoded = SerializationUtil.encode(function);
 
             String result = testing(realm != null ? realm : "master").runOnServer(encoded);
-            if (result != null && !result.isEmpty() && !result.trim().startsWith("{")) {
+            if (result != null && !result.isEmpty() && result.trim().startsWith("EXCEPTION:")) {
                 Throwable t = SerializationUtil.decodeException(result);
                 if (t instanceof AssertionError) {
                     throw (AssertionError) t;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java
index 84144f6..e2ab0f8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java
@@ -30,13 +30,18 @@ import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.message.BasicNameValuePair;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.common.Version;
 import org.keycloak.models.Constants;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.ActionURIUtils;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
 
 import java.io.IOException;
 import java.net.URLEncoder;
@@ -56,6 +61,11 @@ import static org.junit.Assert.assertTrue;
  */
 public class LoginStatusIframeEndpointTest extends AbstractKeycloakTest {
 
+    @Deployment
+    public static WebArchive deploy() {
+        return RunOnServerDeployment.create(LoginStatusIframeEndpointTest.class);
+    }
+
     @Test
     public void checkIframe() throws IOException {
         CookieStore cookieStore = new BasicCookieStore();
@@ -185,6 +195,24 @@ public class LoginStatusIframeEndpointTest extends AbstractKeycloakTest {
         }
     }
 
+    @Test
+    public void checkIframeCache() throws IOException {
+        String version = testingClient.server().fetch(session -> Version.RESOURCES_VERSION, String.class);
+
+        CloseableHttpClient client = HttpClients.createDefault();
+        HttpGet get = new HttpGet(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/login-status-iframe.html");
+        CloseableHttpResponse response = client.execute(get);
+
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        assertEquals("no-cache, must-revalidate, no-transform, no-store", response.getHeaders("Cache-Control")[0].getValue());
+
+        get = new HttpGet(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/login-status-iframe.html?version=" + version);
+        response = client.execute(get);
+
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        assertTrue(response.getHeaders("Cache-Control")[0].getValue().contains("max-age"));
+    }
+
     @Override
     public void addTestRealms(List<RealmRepresentation> testRealms) {
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/RunOnServerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/RunOnServerTest.java
index 4101ffc..4c0862d 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/RunOnServerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/runonserver/RunOnServerTest.java
@@ -45,6 +45,12 @@ public class RunOnServerTest extends AbstractKeycloakTest {
     }
 
     @Test
+    public void runOnServerString() throws IOException {
+        String string = testingClient.server().fetch(session -> "Hello world!", String.class);
+        assertEquals("Hello world!", string);
+    }
+
+    @Test
     public void runOnServerRep() throws IOException {
         final String realmName = "master";
 
diff --git a/themes/src/main/resources/theme/base/admin/index.ftl b/themes/src/main/resources/theme/base/admin/index.ftl
index 3feb9e3..aebc488 100755
--- a/themes/src/main/resources/theme/base/admin/index.ftl
+++ b/themes/src/main/resources/theme/base/admin/index.ftl
@@ -20,6 +20,7 @@
         var consoleBaseUrl = '${consoleBaseUrl}';
         var resourceUrl = '${resourceUrl}';
         var masterRealm = '${masterRealm}';
+        var resourceVersion = '${resourceVersion}';
     </script>
 
     <!-- Minimized versions (for those that have one) -->
@@ -58,7 +59,7 @@
     <script src="${resourceUrl}/lib/ui-ace/min/ace.js"></script>
     <script src="${resourceUrl}/lib/ui-ace/ui-ace.min.js"></script>
 
-    <script src="${authUrl}/js/${resourceVersion}/keycloak.js" type="text/javascript"></script>
+    <script src="${authUrl}/js/keycloak.js?version=${resourceVersion}" type="text/javascript"></script>
 
     <script src="${resourceUrl}/js/app.js" type="text/javascript"></script>
     <script src="${resourceUrl}/js/controllers/realm.js" type="text/javascript"></script>