keycloak-uncached

Details

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 4448aa1..757f0d7 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
@@ -20,10 +20,12 @@ package org.keycloak.protocol.oidc.endpoints;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.Config;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.utils.RedirectUtils;
 import org.keycloak.common.util.StreamUtil;
 import org.keycloak.common.util.UriUtils;
+import org.keycloak.services.util.P3PHelper;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Produces;
@@ -45,6 +47,9 @@ public class LoginStatusIframeEndpoint {
     @Context
     private UriInfo uriInfo;
 
+    @Context
+    private KeycloakSession session;
+
     private RealmModel realm;
 
     public LoginStatusIframeEndpoint(RealmModel realm) {
@@ -99,6 +104,8 @@ public class LoginStatusIframeEndpoint {
             String file = StreamUtil.readString(is);
             file = file.replace("ORIGIN", origin);
 
+            P3PHelper.addP3PHeader(session);
+
             CacheControl cacheControl = new CacheControl();
             cacheControl.setNoTransform(false);
             cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
index 29e0f56..d9a57cf 100755
--- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
@@ -36,7 +36,7 @@ public class AppAuthManager extends AuthenticationManager {
         AuthResult authResult = super.authenticateIdentityCookie(session, realm);
         if (authResult == null) return null;
         // refresh the cookies!
-        createLoginCookie(realm, authResult.getUser(), authResult.getSession(), session.getContext().getUri(), session.getContext().getConnection());
+        createLoginCookie(session, realm, authResult.getUser(), authResult.getSession(), session.getContext().getUri(), session.getContext().getConnection());
         if (authResult.getSession().isRememberMe()) createRememberMeCookie(realm, authResult.getUser().getUsername(), session.getContext().getUri(), session.getContext().getConnection());
         return authResult;
     }
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 91389f8..441ec1b 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -54,6 +54,7 @@ import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.IdentityBrokerService;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.util.CookieHelper;
+import org.keycloak.services.util.P3PHelper;
 
 import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
@@ -273,7 +274,7 @@ public class AuthenticationManager {
         return token;
     }
 
-    public static void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, ClientConnection connection) {
+    public static void createLoginCookie(KeycloakSession keycloakSession, RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, ClientConnection connection) {
         String cookiePath = getIdentityCookiePath(realm, uriInfo);
         String issuer = Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName());
         AccessToken identityToken = createIdentityToken(realm, user, session, issuer);
@@ -294,7 +295,7 @@ public class AuthenticationManager {
         // THIS SHOULD NOT BE A HTTPONLY COOKIE!  It is used for OpenID Connect Iframe Session support!
         // Max age should be set to the max lifespan of the session as it's used to invalidate old-sessions on re-login
         CookieHelper.addCookie(KEYCLOAK_SESSION_COOKIE, sessionCookieValue, cookiePath, null, null, realm.getSsoSessionMaxLifespan(), secureOnly, false);
-
+        P3PHelper.addP3PHeader(keycloakSession);
     }
 
     public static void createRememberMeCookie(RealmModel realm, String username, UriInfo uriInfo, ClientConnection connection) {
@@ -413,7 +414,7 @@ public class AuthenticationManager {
         session.getContext().resolveLocale(userSession.getUser());
 
         // refresh the cookies!
-        createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
+        createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection);
         if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN);
         if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection);
         return protocol.authenticated(userSession, new ClientSessionCode(realm, clientSession));
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index e413c72..5427a1a 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -330,7 +330,7 @@ public class UsersResource {
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
 
         UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
-        AuthenticationManager.createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
+        AuthenticationManager.createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection);
         URI redirect = AccountService.accountServiceApplicationPage(uriInfo).build(realm.getName());
         Map<String, Object> result = new HashMap<>();
         result.put("sameRealm", sameRealm);
diff --git a/services/src/main/java/org/keycloak/services/util/LocaleHelper.java b/services/src/main/java/org/keycloak/services/util/LocaleHelper.java
index 1871193..d8a7c80 100755
--- a/services/src/main/java/org/keycloak/services/util/LocaleHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/LocaleHelper.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.services.util;
 
+import org.keycloak.models.KeycloakContext;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
@@ -44,6 +45,25 @@ public class LocaleHelper {
         }
     }
 
+    public static Locale getLocaleFromCookie(KeycloakSession session) {
+        KeycloakContext ctx = session.getContext();
+
+        if (ctx.getRequestHeaders() != null && ctx.getRequestHeaders().getCookies().containsKey(LOCALE_COOKIE)) {
+            String localeString = ctx.getRequestHeaders().getCookies().get(LOCALE_COOKIE).getValue();
+            Locale locale = findLocale(ctx.getRealm().getSupportedLocales(), localeString);
+            if (locale != null) {
+                return locale;
+            }
+        }
+
+        String locale = ctx.getRealm().getDefaultLocale();
+        if (locale != null) {
+            return Locale.forLanguageTag(locale);
+        } else {
+            return Locale.ENGLISH;
+        }
+    }
+
     private static Locale getUserLocale(KeycloakSession session, RealmModel realm, UserModel user) {
         UriInfo uriInfo = session.getContext().getUri();
         HttpHeaders httpHeaders = session.getContext().getRequestHeaders();
diff --git a/services/src/main/java/org/keycloak/services/util/P3PHelper.java b/services/src/main/java/org/keycloak/services/util/P3PHelper.java
new file mode 100644
index 0000000..84e2e21
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/util/P3PHelper.java
@@ -0,0 +1,58 @@
+/*
+ * 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.services.util;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.validation.Validation;
+import org.keycloak.theme.Theme;
+import org.keycloak.theme.ThemeProvider;
+
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * IE requires P3P header to allow loading cookies from iframes when domain differs from main page (see KEYCLOAK-2828 for more details)
+ *
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class P3PHelper {
+
+    private static final Logger logger = Logger.getLogger(P3PHelper.class);
+
+    public static void addP3PHeader(KeycloakSession session) {
+        try {
+            ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
+            Theme theme = themeProvider.getTheme(session.getContext().getRealm().getLoginTheme(), Theme.Type.LOGIN);
+
+            Locale locale = LocaleHelper.getLocaleFromCookie(session);
+            String p3pValue = theme.getMessages(locale).getProperty("p3pPolicy");
+
+            if (!Validation.isBlank(p3pValue)) {
+                HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
+                response.getOutputHeaders().putSingle("P3P", p3pValue);
+            }
+        } catch (IOException e) {
+            logger.error("Failed to set P3P header", e);
+            return;
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..5386141
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.testsuite.oauth;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.cookie.Cookie;
+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.junit.Test;
+import org.keycloak.models.Constants;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LoginStatusIframeEndpointTest extends AbstractKeycloakTest {
+
+    @Test
+    public void checkIframeP3PHeader() throws IOException {
+        CookieStore cookieStore = new BasicCookieStore();
+
+        CloseableHttpClient client = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
+        try {
+            HttpGet get = new HttpGet(
+                    suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/auth?response_type=code&client_id=" + Constants.ADMIN_CONSOLE_CLIENT_ID);
+
+            CloseableHttpResponse response = client.execute(get);
+            String s = IOUtils.toString(response.getEntity().getContent());
+            response.close();
+
+            Matcher matcher = Pattern.compile("action=\"([^\"]*)\"").matcher(s);
+            matcher.find();
+
+            String action = matcher.group(1);
+
+            HttpPost post = new HttpPost(action);
+
+            List<NameValuePair> params = new LinkedList<>();
+            params.add(new BasicNameValuePair("username", "admin"));
+            params.add(new BasicNameValuePair("password", "admin"));
+
+            post.setHeader("Content-Type", "application/x-www-form-urlencoded");
+            post.setEntity(new UrlEncodedFormEntity(params));
+
+            response = client.execute(post);
+
+            assertEquals("CP=\"This is not a P3P policy!\"", response.getFirstHeader("P3P").getValue());
+
+            Header setIdentityCookieHeader = null;
+            Header setSessionCookieHeader = null;
+            for (Header h : response.getAllHeaders()) {
+                if (h.getName().equals("Set-Cookie")) {
+                    if (h.getValue().contains("KEYCLOAK_SESSION")) {
+                        setSessionCookieHeader = h;
+
+                    } else if (h.getValue().contains("KEYCLOAK_IDENTITY")) {
+                        setIdentityCookieHeader = h;
+                    }
+                }
+            }
+            assertNotNull(setIdentityCookieHeader);
+            assertTrue(setIdentityCookieHeader.getValue().contains("HttpOnly"));
+
+            assertNotNull(setSessionCookieHeader);
+            assertFalse(setSessionCookieHeader.getValue().contains("HttpOnly"));
+
+            response.close();
+
+            Cookie sessionCookie = null;
+            for (Cookie cookie : cookieStore.getCookies()) {
+                if (cookie.getName().equals("KEYCLOAK_SESSION")) {
+                    sessionCookie = cookie;
+                    break;
+                }
+            }
+            assertNotNull(sessionCookie);
+
+            get = new HttpGet(
+                    suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/protocol/openid-connect/login-status-iframe.html?client_id=" + Constants.ADMIN_CONSOLE_CLIENT_ID + "&origin=" + suiteContext.getAuthServerInfo().getContextRoot());
+            response = client.execute(get);
+
+            assertEquals(200, response.getStatusLine().getStatusCode());
+            s = IOUtils.toString(response.getEntity().getContent());
+            assertTrue(s.contains("function getCookie(cname)"));
+
+            assertEquals("CP=\"This is not a P3P policy!\"", response.getFirstHeader("P3P").getValue());
+
+            response.close();
+        } finally {
+            client.close();
+        }
+    }
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+    }
+
+}
diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties
index 3545b29..d0ffefc 100755
--- a/themes/src/main/resources/theme/base/login/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -222,3 +222,5 @@ missingParameterMessage=Missing parameters\: {0}
 clientNotFoundMessage=Client not found.
 invalidParameterMessage=Invalid parameter\: {0}
 alreadyLoggedIn=You are already logged in.
+
+p3pPolicy=CP="This is not a P3P policy!"
\ No newline at end of file