keycloak-aplcache

Details

diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java
index 476e3aa..b09340a 100755
--- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java
+++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java
@@ -24,6 +24,7 @@ public enum LoginFormsPages {
 
     LOGIN, LOGIN_TOTP, LOGIN_CONFIG_TOTP, LOGIN_VERIFY_EMAIL,
     LOGIN_IDP_LINK_CONFIRM, LOGIN_IDP_LINK_EMAIL,
-    OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, INFO, ERROR, LOGIN_UPDATE_PROFILE, LOGIN_PAGE_EXPIRED, CODE;
+    OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, REGISTER, INFO, ERROR, LOGIN_UPDATE_PROFILE, 
+    LOGIN_PAGE_EXPIRED, CODE, X509_CONFIRM;
 
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
index eb8013e..011946f 100755
--- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
@@ -80,7 +80,9 @@ public interface LoginFormsProvider extends Provider {
     Response createOAuthGrant();
 
     Response createCode();
-    
+
+    Response createX509ConfirmPage();
+
     LoginFormsProvider setAuthenticationSession(AuthenticationSessionModel authenticationSession);
 
     LoginFormsProvider setClientSessionCode(String accessCode);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java
index 01339f6..5ba4e3c 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java
@@ -22,6 +22,7 @@ import java.security.cert.X509Certificate;
 import java.util.Enumeration;
 import java.util.LinkedList;
 import java.util.List;
+import javax.ws.rs.core.MultivaluedHashMap;
 
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -30,11 +31,12 @@ import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
+import org.keycloak.forms.login.LoginFormsPages;
 import org.keycloak.forms.login.LoginFormsProvider;
+import org.keycloak.forms.login.freemarker.Templates;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.FormMessage;
-import org.keycloak.services.ServicesLogger;
 
 /**
  * @author <a href="mailto:pnalyvayko@agi.com">Peter Nalyvayko</a>
@@ -43,8 +45,6 @@ import org.keycloak.services.ServicesLogger;
  */
 public class X509ClientCertificateAuthenticator extends AbstractX509ClientCertificateAuthenticator {
 
-    protected static ServicesLogger logger = ServicesLogger.LOGGER;
-
     @Override
     public void close() {
 
@@ -216,11 +216,14 @@ public class X509ClientCertificateAuthenticator extends AbstractX509ClientCertif
             form.setErrors(errors);
         }
 
-        return form
-                .setAttribute("username", context.getUser() != null ? context.getUser().getUsername() : "unknown user")
-                .setAttribute("subjectDN", subjectDN)
-                .setAttribute("isUserEnabled", isUserEnabled)
-                .createForm("login-x509-info.ftl");
+        MultivaluedMap<String,String> formData = new MultivaluedHashMap<>();
+        formData.add("username", context.getUser() != null ? context.getUser().getUsername() : "unknown user");
+        formData.add("subjectDN", subjectDN);
+        formData.add("isUserEnabled", String.valueOf(isUserEnabled));
+
+        form.setFormData(formData);
+
+        return form.createX509ConfirmPage();
     }
 
     private void dumpContainerAttributes(AuthenticationFlowContext context) {
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
index 8b92110..80cfe53 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -35,6 +35,7 @@ import org.keycloak.forms.login.freemarker.model.RegisterBean;
 import org.keycloak.forms.login.freemarker.model.RequiredActionUrlFormatterMethod;
 import org.keycloak.forms.login.freemarker.model.TotpBean;
 import org.keycloak.forms.login.freemarker.model.UrlBean;
+import org.keycloak.forms.login.freemarker.model.X509ConfirmBean;
 import org.keycloak.models.*;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.services.Urls;
@@ -62,6 +63,7 @@ import java.net.URI;
 import java.text.MessageFormat;
 import java.util.*;
 
+
 import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PASSWORD;
 
 /**
@@ -75,7 +77,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
     protected Response.Status status;
     protected javax.ws.rs.core.MediaType contentType;
     protected List<ClientScopeModel> clientScopesRequested;
-    protected Map<String, String> httpResponseHeaders = new HashMap<String, String>();
+    protected Map<String, String> httpResponseHeaders = new HashMap<>();
     protected URI actionUri;
     protected String execution;
 
@@ -95,12 +97,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
 
     protected UserModel user;
 
-    protected final Map<String, Object> attributes = new HashMap<String, Object>();
+    protected final Map<String, Object> attributes = new HashMap<>();
 
     public FreeMarkerLoginFormsProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
         this.session = session;
         this.freeMarker = freeMarker;
-        this.attributes.put("scripts", new LinkedList<String>());
+        this.attributes.put("scripts", new LinkedList<>());
         this.realm = session.getContext().getRealm();
         this.client = session.getContext().getClient();
         this.uriInfo = session.getContext().getUri();
@@ -204,6 +206,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
             case CODE:
                 attributes.put(OAuth2Constants.CODE, new CodeBean(accessCode, messageType == MessageType.ERROR ? getFirstMessageUnformatted() : null));
                 break;
+            case X509_CONFIRM:
+                attributes.put("x509", new X509ConfirmBean(formData));
+                break;
         }
 
         return processTemplate(theme, Templates.getTemplate(page), locale);
@@ -342,7 +347,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
 
         Locale locale = session.getContext().resolveLocale(user);
         Properties messagesBundle = handleThemeResources(theme, locale);
-        FormMessage msg = new FormMessage(message, parameters);
+        FormMessage msg = new FormMessage(message, (Object[]) parameters);
         return formatMessage(msg, messagesBundle, locale);
     }
 
@@ -385,6 +390,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
                         case LOGIN:
                             b = UriBuilder.fromUri(Urls.realmLoginPage(baseUri, realm.getName()));
                             break;
+                        case X509_CONFIRM:
+                            b = UriBuilder.fromUri(Urls.realmLoginPage(baseUri, realm.getName()));
+                            break;
                         case REGISTER:
                             b = UriBuilder.fromUri(Urls.realmRegisterPage(baseUri, realm.getName()));
                             break;
@@ -508,6 +516,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
         return createResponse(LoginFormsPages.CODE);
     }
 
+    @Override
+    public Response createX509ConfirmPage() {
+        return createResponse(LoginFormsPages.X509_CONFIRM);
+    }
+
     protected void setMessage(MessageType type, String message, Object... parameters) {
         messageType = type;
         messages = new ArrayList<>();
@@ -629,15 +642,13 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
         this.status = status;
         return this;
     }
+
     @Override
     public LoginFormsProvider setMediaType(javax.ws.rs.core.MediaType type) {
         this.contentType = type;
         return this;
     }
 
-
-
-
     @Override
     public LoginFormsProvider setActionUri(URI actionUri) {
         this.actionUri = actionUri;
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/X509ConfirmBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/X509ConfirmBean.java
new file mode 100644
index 0000000..20d87f4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/X509ConfirmBean.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 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.forms.login.freemarker.model;
+
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author vramik
+ */
+public class X509ConfirmBean {
+
+    private Map<String, String> formData = new HashMap<>();
+
+    public X509ConfirmBean(MultivaluedMap<String, String> formData) {
+        this.formData = new HashMap<>();
+
+        if (formData != null) {
+            formData.keySet().stream().forEach((key) -> this.formData.put(key, formData.getFirst(key)));
+        }
+    }
+
+    public Map<String, String> getFormData() {
+        return formData;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java b/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
index f2a9d75..73921a5 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
@@ -56,6 +56,8 @@ public class Templates {
                 return "code.ftl";
             case LOGIN_PAGE_EXPIRED:
                 return "login-page-expired.ftl";
+            case X509_CONFIRM:
+                return "login-x509-info.ftl";
             default:
                 throw new IllegalArgumentException();
         }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/x509/X509IdentityConfirmationPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/x509/X509IdentityConfirmationPage.java
index a656d50..f229ca4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/x509/X509IdentityConfirmationPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/x509/X509IdentityConfirmationPage.java
@@ -20,6 +20,7 @@ package org.keycloak.testsuite.pages.x509;
 
 import org.jboss.arquillian.test.api.ArquillianResource;
 import org.keycloak.testsuite.pages.AbstractPage;
+import org.keycloak.testsuite.pages.LanguageComboboxAwarePage;
 import org.keycloak.testsuite.util.OAuthClient;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
@@ -30,7 +31,7 @@ import org.openqa.selenium.support.FindBy;
  * @since 10/24/2016
  */
 
-public class X509IdentityConfirmationPage extends AbstractPage {
+public class X509IdentityConfirmationPage extends LanguageComboboxAwarePage {
 
     @ArquillianResource
     protected OAuthClient oauth;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java
index 2218288..4d1b224 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java
@@ -98,6 +98,7 @@ public abstract class AbstractX509AuthenticationTest extends AbstractTestRealmKe
     @Rule
     public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this);
 
+    @Override
     protected boolean isImportAfterEachMethod() {
         return true;
     }
@@ -113,9 +114,9 @@ public abstract class AbstractX509AuthenticationTest extends AbstractTestRealmKe
 
                 cliArgs.append("--ignore-ssl-errors=true ");
                 cliArgs.append("--web-security=false ");
-                cliArgs.append("--ssl-certificates-path=" + authServerHome + "/ca.crt ");
-                cliArgs.append("--ssl-client-certificate-file=" + authServerHome + "/client.crt ");
-                cliArgs.append("--ssl-client-key-file=" + authServerHome + "/client.key ");
+                cliArgs.append("--ssl-certificates-path=").append(authServerHome).append("/ca.crt ");
+                cliArgs.append("--ssl-client-certificate-file=").append(authServerHome).append("/client.crt ");
+                cliArgs.append("--ssl-client-key-file=").append(authServerHome).append("/client.key ");
                 cliArgs.append("--ssl-client-key-passphrase=secret ");
 
                 System.setProperty("keycloak.phantomjs.cli.args", cliArgs.toString());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509BrowserLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509BrowserLoginTest.java
index a714998..51f4453 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509BrowserLoginTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509BrowserLoginTest.java
@@ -33,11 +33,16 @@ import org.keycloak.testsuite.pages.x509.X509IdentityConfirmationPage;
 import javax.ws.rs.core.Response;
 
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
 import static org.junit.Assert.assertEquals;
 import static org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.IdentityMapperType.USERNAME_EMAIL;
 import static org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.IdentityMapperType.USER_ATTRIBUTE;
 import static org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.MappingSourceType.SUBJECTDN;
 import static org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.MappingSourceType.SUBJECTDN_EMAIL;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.util.DroneUtils;
 
 /**
  * @author <a href="mailto:brat000012001@gmail.com">Peter Nalyvayko</a>
@@ -483,4 +488,33 @@ public class X509BrowserLoginTest extends AbstractX509AuthenticationTest {
         loginAsUserFromCertSubjectEmail();
     }
 
+    // KEYCLOAK-6866
+    @Test
+    public void changeLocaleOnX509InfoPage() {
+        ProfileAssume.assumeCommunity();
+        
+        AuthenticatorConfigRepresentation cfg = newConfig("x509-browser-config", createLoginSubjectEmail2UsernameOrEmailConfig().getConfig());
+        String cfgId = createConfig(browserExecution.getId(), cfg);
+        Assert.assertNotNull(cfgId);
+
+        log.debug("Open confirm page");
+        loginConfirmationPage.open();
+        
+        log.debug("check if on confirm page");
+        Assert.assertThat(loginConfirmationPage.getSubjectDistinguishedNameText(), startsWith("EMAILADDRESS=test-user@localhost"));
+        log.debug("check if locale is EN");
+        Assert.assertThat(loginConfirmationPage.getLanguageDropdownText(), is(equalTo("English")));
+        
+        log.debug("change locale to DE");
+        loginConfirmationPage.openLanguage("Deutsch");
+        log.debug("check if locale is DE");
+        Assert.assertThat(loginConfirmationPage.getLanguageDropdownText(), is(equalTo("Deutsch")));
+        Assert.assertThat(DroneUtils.getCurrentDriver().getPageSource(), containsString("X509 Client Zertifikat:"));
+        
+        log.debug("confirm cert");
+        loginConfirmationPage.confirm();
+        
+        log.debug("check if logged in");
+        Assert.assertThat(appPage.getRequestType(), is(equalTo(AppPage.RequestType.AUTH_RESPONSE)));
+    }
 }
diff --git a/themes/src/main/resources/theme/base/login/login-x509-info.ftl b/themes/src/main/resources/theme/base/login/login-x509-info.ftl
index c3ff2fd..0228b06 100644
--- a/themes/src/main/resources/theme/base/login/login-x509-info.ftl
+++ b/themes/src/main/resources/theme/base/login/login-x509-info.ftl
@@ -10,9 +10,9 @@
                 <div class="${properties.kcLabelWrapperClass!}">
                     <label for="certificate_subjectDN" class="${properties.kcLabelClass!}">${msg("clientCertificate")}</label>
                 </div>
-                <#if subjectDN??>
+                <#if x509.formData.subjectDN??>
                     <div class="${properties.kcLabelWrapperClass!}">
-                         <label id="certificate_subjectDN" class="${properties.kcLabelClass!}">${(subjectDN!"")}</label>
+                         <label id="certificate_subjectDN" class="${properties.kcLabelClass!}">${(x509.formData.subjectDN!"")}</label>
                     </div>
                 <#else>
                     <div class="${properties.kcLabelWrapperClass!}">
@@ -23,12 +23,12 @@
 
             <div class="${properties.kcFormGroupClass!}">
 
-                    <#if isUserEnabled>
+                    <#if x509.formData.isUserEnabled??>
                           <div class="${properties.kcLabelWrapperClass!}">
                              <label for="username" class="${properties.kcLabelClass!}">${msg("doX509Login")}</label>
                           </div>
                           <div class="${properties.kcLabelWrapperClass!}">
-                             <label id="username" class="${properties.kcLabelClass!}">${(username!'')}</label>
+                             <label id="username" class="${properties.kcLabelClass!}">${(x509.formData.username!'')}</label>
                          </div>
                     </#if>
 
@@ -43,7 +43,7 @@
                 <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
                     <div class="${properties.kcFormButtonsWrapperClass!}">
                         <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doContinue")}"/>
-                        <#if isUserEnabled>
+                        <#if x509.formData.isUserEnabled??>
                             <input class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="cancel" id="kc-cancel" type="submit" value="${msg("doIgnore")}"/>
                         </#if>
                     </div>