KEYCLOAK-4327 Switching language on User consent gives error
This commit is contained in:
parent
edc6d99c9c
commit
f363dbcad0
13 changed files with 180 additions and 28 deletions
|
@ -120,4 +120,6 @@ public interface LoginFormsProvider extends Provider {
|
|||
public LoginFormsProvider setStatus(Response.Status status);
|
||||
|
||||
LoginFormsProvider setActionUri(URI requestUri);
|
||||
|
||||
LoginFormsProvider setExecution(String execution);
|
||||
}
|
||||
|
|
|
@ -471,6 +471,7 @@ public class AuthenticationProcessor {
|
|||
LoginFormsProvider provider = getSession().getProvider(LoginFormsProvider.class)
|
||||
.setUser(getUser())
|
||||
.setActionUri(action)
|
||||
.setExecution(getExecution().getId())
|
||||
.setFormData(request.getDecodedFormParameters())
|
||||
.setClientSessionCode(accessCode);
|
||||
if (getForwardedErrorMessage() != null) {
|
||||
|
|
|
@ -270,6 +270,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
|||
URI actionUrl = getActionUrl(executionId, code);
|
||||
LoginFormsProvider form = processor.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setActionUri(actionUrl)
|
||||
.setExecution(executionId)
|
||||
.setClientSessionCode(code)
|
||||
.setFormData(formData)
|
||||
.setErrors(errors);
|
||||
|
|
|
@ -137,11 +137,15 @@ public class RequiredActionContextResult implements RequiredActionContext {
|
|||
ClientModel client = authenticationSession.getClient();
|
||||
return LoginActionsService.requiredActionProcessor(getUriInfo())
|
||||
.queryParam(OAuth2Constants.CODE, code)
|
||||
.queryParam(Constants.EXECUTION, factory.getId())
|
||||
.queryParam(Constants.EXECUTION, getExecution())
|
||||
.queryParam(Constants.CLIENT_ID, client.getClientId())
|
||||
.build(getRealm().getName());
|
||||
}
|
||||
|
||||
private String getExecution() {
|
||||
return factory.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateCode() {
|
||||
ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, getRealm(), getAuthenticationSession());
|
||||
|
@ -164,6 +168,7 @@ public class RequiredActionContextResult implements RequiredActionContext {
|
|||
LoginFormsProvider provider = getSession().getProvider(LoginFormsProvider.class)
|
||||
.setUser(getUser())
|
||||
.setActionUri(action)
|
||||
.setExecution(getExecution())
|
||||
.setClientSessionCode(accessCode);
|
||||
return provider;
|
||||
}
|
||||
|
|
|
@ -169,6 +169,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
|
|||
.setStatus(Response.Status.OK)
|
||||
.setAttribute(LoginFormsProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext)
|
||||
.setActionUri(action)
|
||||
.setExecution(context.getExecution().getId())
|
||||
.createIdpLinkEmailPage();
|
||||
context.forceChallenge(challenge);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
private Map<String, String> httpResponseHeaders = new HashMap<String, String>();
|
||||
private String accessRequestMessage;
|
||||
private URI actionUri;
|
||||
private String execution;
|
||||
|
||||
private List<FormMessage> messages = null;
|
||||
private MessageType messageType = MessageType.ERROR;
|
||||
|
@ -230,6 +231,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
b = UriBuilder.fromUri(baseUri).path(uriInfo.getPath());
|
||||
break;
|
||||
}
|
||||
|
||||
if (execution != null) {
|
||||
b.queryParam(Constants.EXECUTION, execution);
|
||||
}
|
||||
|
||||
attributes.put("locale", new LocaleBean(realm, locale, b, messagesBundle));
|
||||
}
|
||||
}
|
||||
|
@ -366,7 +372,13 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
attributes.put("requiredActionUrl", new RequiredActionUrlFormatterMethod(realm, baseUri));
|
||||
|
||||
if (realm.isInternationalizationEnabled()) {
|
||||
UriBuilder b = UriBuilder.fromUri(baseUri).path(uriInfo.getPath());
|
||||
UriBuilder b = UriBuilder.fromUri(baseUri)
|
||||
.path(uriInfo.getPath());
|
||||
|
||||
if (execution != null) {
|
||||
b.queryParam(Constants.EXECUTION, execution);
|
||||
}
|
||||
|
||||
attributes.put("locale", new LocaleBean(realm, locale, b, messagesBundle));
|
||||
}
|
||||
}
|
||||
|
@ -590,6 +602,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setExecution(String execution) {
|
||||
this.execution = execution;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setResponseHeader(String headerName, String headerValue) {
|
||||
this.httpResponseHeaders.put(headerName, headerValue);
|
||||
|
|
|
@ -644,12 +644,15 @@ public class AuthenticationManager {
|
|||
|
||||
// Skip grant screen if everything was already approved by this user
|
||||
if (realmRoles.size() > 0 || resourceRoles.size() > 0 || protocolMappers.size() > 0) {
|
||||
String execution = AuthenticatedClientSessionModel.Action.OAUTH_GRANT.name();
|
||||
|
||||
accessCode.
|
||||
|
||||
setAction(AuthenticatedClientSessionModel.Action.REQUIRED_ACTIONS.name());
|
||||
authSession.setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, AuthenticatedClientSessionModel.Action.OAUTH_GRANT.name());
|
||||
authSession.setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution);
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setExecution(execution)
|
||||
.setClientSessionCode(accessCode.getCode())
|
||||
.setAccessRequest(realmRoles, resourceRoles, protocolMappers)
|
||||
.createOAuthGrant();
|
||||
|
|
|
@ -58,6 +58,7 @@ public class AuthenticationFlowURLHelper {
|
|||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setActionUri(lastStepUrl)
|
||||
.setExecution(getExecutionId(authSession))
|
||||
.createLoginExpiredPage();
|
||||
}
|
||||
|
||||
|
@ -76,7 +77,7 @@ public class AuthenticationFlowURLHelper {
|
|||
|
||||
|
||||
public URI getLastExecutionUrl(AuthenticationSessionModel authSession) {
|
||||
String executionId = authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
String executionId = getExecutionId(authSession);
|
||||
String latestFlowPath = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
||||
|
||||
if (latestFlowPath == null) {
|
||||
|
@ -90,4 +91,8 @@ public class AuthenticationFlowURLHelper {
|
|||
return getLastExecutionUrl(latestFlowPath, executionId, authSession.getClient().getClientId());
|
||||
}
|
||||
|
||||
private String getExecutionId(AuthenticationSessionModel authSession) {
|
||||
return authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.pages;
|
||||
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class LanguageComboboxAwarePage extends AbstractPage {
|
||||
|
||||
@FindBy(id = "kc-current-locale-link")
|
||||
private WebElement languageText;
|
||||
|
||||
@FindBy(id = "kc-locale-dropdown")
|
||||
private WebElement localeDropdown;
|
||||
|
||||
public String getLanguageDropdownText() {
|
||||
return languageText.getText();
|
||||
}
|
||||
|
||||
public void openLanguage(String language){
|
||||
WebElement langLink = localeDropdown.findElement(By.xpath("//a[text()='" + language + "']"));
|
||||
String url = langLink.getAttribute("href");
|
||||
driver.navigate().to(url);
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ import org.openqa.selenium.support.FindBy;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LoginPage extends AbstractPage {
|
||||
public class LoginPage extends LanguageComboboxAwarePage {
|
||||
|
||||
@ArquillianResource
|
||||
protected OAuthClient oauth;
|
||||
|
@ -75,12 +75,6 @@ public class LoginPage extends AbstractPage {
|
|||
private WebElement instruction;
|
||||
|
||||
|
||||
@FindBy(id = "kc-current-locale-link")
|
||||
private WebElement languageText;
|
||||
|
||||
@FindBy(id = "kc-locale-dropdown")
|
||||
private WebElement localeDropdown;
|
||||
|
||||
public void login(String username, String password) {
|
||||
usernameInput.clear();
|
||||
usernameInput.sendKeys(username);
|
||||
|
@ -191,14 +185,4 @@ public class LoginPage extends AbstractPage {
|
|||
assertCurrent();
|
||||
}
|
||||
|
||||
public String getLanguageDropdownText() {
|
||||
return languageText.getText();
|
||||
}
|
||||
|
||||
public void openLanguage(String language){
|
||||
WebElement langLink = localeDropdown.findElement(By.xpath("//a[text()='" +language +"']"));
|
||||
String url = langLink.getAttribute("href");
|
||||
driver.navigate().to(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.openqa.selenium.support.FindBy;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LoginPasswordUpdatePage extends AbstractPage {
|
||||
public class LoginPasswordUpdatePage extends LanguageComboboxAwarePage {
|
||||
|
||||
@FindBy(id = "password-new")
|
||||
private WebElement newPasswordInput;
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.openqa.selenium.support.FindBy;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class OAuthGrantPage extends AbstractPage {
|
||||
public class OAuthGrantPage extends LanguageComboboxAwarePage {
|
||||
|
||||
@FindBy(css = "input[name=\"accept\"]")
|
||||
private WebElement acceptButton;
|
||||
|
|
|
@ -16,19 +16,30 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.i18n;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.LanguageComboboxAwarePage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
|
||||
/**
|
||||
|
@ -37,9 +48,19 @@ import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
|||
*/
|
||||
public class LoginPageTest extends AbstractI18NTest {
|
||||
|
||||
@Page
|
||||
protected AppPage appPage;
|
||||
|
||||
@Page
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Page
|
||||
protected LoginPasswordUpdatePage changePasswordPage;
|
||||
|
||||
@Page
|
||||
protected OAuthGrantPage grantPage;
|
||||
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
|
@ -63,11 +84,7 @@ public class LoginPageTest extends AbstractI18NTest {
|
|||
loginPage.open();
|
||||
Assert.assertEquals("English", loginPage.getLanguageDropdownText());
|
||||
|
||||
loginPage.openLanguage("Deutsch");
|
||||
Assert.assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
||||
|
||||
loginPage.openLanguage("English");
|
||||
Assert.assertEquals("English", loginPage.getLanguageDropdownText());
|
||||
switchLanguageToGermanAndBack("Username or email", "Benutzername oder E-Mail", loginPage);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -109,6 +126,8 @@ public class LoginPageTest extends AbstractI18NTest {
|
|||
|
||||
response = client.target(driver.getCurrentUrl()).request().acceptLanguage("en").get();
|
||||
Assert.assertTrue(response.readEntity(String.class).contains("Log in to test"));
|
||||
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -119,4 +138,73 @@ public class LoginPageTest extends AbstractI18NTest {
|
|||
Assert.assertEquals("MyOIDC", loginPage.findSocialButton("myoidc").getText());
|
||||
|
||||
}
|
||||
|
||||
|
||||
// KEYCLOAK-3887
|
||||
@Test
|
||||
public void languageChangeRequiredActions() {
|
||||
UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost");
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setRequiredActions(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
|
||||
user.update(userRep);
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
changePasswordPage.assertCurrent();
|
||||
Assert.assertEquals("English", changePasswordPage.getLanguageDropdownText());
|
||||
|
||||
// Switch language
|
||||
switchLanguageToGermanAndBack("Update password", "Passwort aktualisieren", changePasswordPage);
|
||||
|
||||
// Update password
|
||||
changePasswordPage.changePassword("password", "password");
|
||||
|
||||
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
}
|
||||
|
||||
|
||||
// KEYCLOAK-3887
|
||||
@Test
|
||||
public void languageChangeConsentScreen() {
|
||||
// Set client, which requires consent
|
||||
oauth.clientId("third-party");
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
grantPage.assertCurrent();
|
||||
Assert.assertEquals("English", grantPage.getLanguageDropdownText());
|
||||
|
||||
// Switch language
|
||||
switchLanguageToGermanAndBack("Do you grant these access privileges?", "Wollen Sie diese Zugriffsrechte", changePasswordPage);
|
||||
|
||||
// Confirm grant
|
||||
grantPage.accept();
|
||||
|
||||
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
// Revert client
|
||||
oauth.clientId("test-app");
|
||||
}
|
||||
|
||||
|
||||
private void switchLanguageToGermanAndBack(String expectedEnglishMessage, String expectedGermanMessage, LanguageComboboxAwarePage page) {
|
||||
// Switch language to Deutsch
|
||||
page.openLanguage("Deutsch");
|
||||
Assert.assertEquals("Deutsch", page.getLanguageDropdownText());
|
||||
String pageSource = driver.getPageSource();
|
||||
Assert.assertFalse(pageSource.contains(expectedEnglishMessage));
|
||||
Assert.assertTrue(pageSource.contains(expectedGermanMessage));
|
||||
|
||||
// Revert language
|
||||
page.openLanguage("English");
|
||||
Assert.assertEquals("English", page.getLanguageDropdownText());
|
||||
pageSource = driver.getPageSource();
|
||||
Assert.assertTrue(pageSource.contains(expectedEnglishMessage));
|
||||
Assert.assertFalse(pageSource.contains(expectedGermanMessage));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue