KEYCLOAK-6866 Error 404 after changing locale while authenticating using X.509
This commit is contained in:
parent
ea8eaaff07
commit
524ab44160
10 changed files with 123 additions and 26 deletions
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -81,6 +81,8 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
Response createCode();
|
||||
|
||||
Response createX509ConfirmPage();
|
||||
|
||||
LoginFormsProvider setAuthenticationSession(AuthenticationSessionModel authenticationSession);
|
||||
|
||||
LoginFormsProvider setClientSessionCode(String accessCode);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue