keycloak-aplcache
Changes
services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java 1(+1 -0)
services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java 20(+19 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java 44(+44 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java 18(+1 -17)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java 2(+1 -1)
Details
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 ac435e3..cb203c1 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
@@ -120,4 +120,6 @@ public interface LoginFormsProvider extends Provider {
public LoginFormsProvider setStatus(Response.Status status);
LoginFormsProvider setActionUri(URI requestUri);
+
+ LoginFormsProvider setExecution(String execution);
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 23d06e3..af7d2f7 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -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) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
index 7189b95..ca841d0 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
@@ -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);
}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
index 82c12ec..575677d 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
@@ -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);
diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java b/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java
index 1d9475a..3afb34c 100755
--- a/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java
@@ -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;
}
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 affaf20..d7eb01c 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
@@ -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));
}
}
@@ -591,6 +603,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
}
@Override
+ public LoginFormsProvider setExecution(String execution) {
+ this.execution = execution;
+ return this;
+ }
+
+ @Override
public LoginFormsProvider setResponseHeader(String headerName, String headerValue) {
this.httpResponseHeaders.put(headerName, headerValue);
return this;
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 07bd1f6..6c91759 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -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();
diff --git a/services/src/main/java/org/keycloak/services/util/AuthenticationFlowURLHelper.java b/services/src/main/java/org/keycloak/services/util/AuthenticationFlowURLHelper.java
index 3726b99..489f73f 100644
--- a/services/src/main/java/org/keycloak/services/util/AuthenticationFlowURLHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/AuthenticationFlowURLHelper.java
@@ -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);
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java
new file mode 100644
index 0000000..29d512e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java
@@ -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);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java
index 11d8fb2..b025ec7 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -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);
- }
-
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
index 93d203d..7a963e1 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
@@ -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;
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
index 1a550ec..cfb1f06 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
@@ -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;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
index 5c9ff74..2d92719 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
@@ -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;
/**
@@ -38,8 +49,18 @@ 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));
+ }
}