mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Migrate i18n package to new testsuite
Closes #44520 Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
efa881d016
commit
f6676ccd76
@ -240,6 +240,11 @@ public class RealmConfigBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RealmConfigBuilder resetPasswordAllowed(boolean allowed) {
|
||||||
|
rep.setResetPasswordAllowed(allowed);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public RealmConfigBuilder clientPolicy(ClientPolicyRepresentation clienPolicyRep) {
|
public RealmConfigBuilder clientPolicy(ClientPolicyRepresentation clienPolicyRep) {
|
||||||
ClientPoliciesRepresentation clientPolicies = rep.getParsedClientPolicies();
|
ClientPoliciesRepresentation clientPolicies = rep.getParsedClientPolicies();
|
||||||
if (clientPolicies == null) {
|
if (clientPolicies == null) {
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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.testframework.ui.page;
|
||||||
|
|
||||||
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.NoSuchElementException;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
public abstract class AbstractLoginPage extends AbstractPage {
|
||||||
|
|
||||||
|
@FindBy(xpath = "//select[@aria-label='languages']/option[@selected]")
|
||||||
|
private WebElement selectedLanguage;
|
||||||
|
|
||||||
|
@FindBy(xpath = "//select[@aria-label='languages']")
|
||||||
|
private WebElement languages;
|
||||||
|
|
||||||
|
@FindBy(id = "kc-current-locale-link")
|
||||||
|
private WebElement languageTextBase; // base theme
|
||||||
|
|
||||||
|
@FindBy(id = "kc-locale-dropdown")
|
||||||
|
private WebElement localeDropdownBase; // base theme
|
||||||
|
|
||||||
|
public AbstractLoginPage(ManagedWebDriver driver) {
|
||||||
|
super(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSelectedLanguage() {
|
||||||
|
try {
|
||||||
|
final String text = selectedLanguage.getText();
|
||||||
|
return text == null ? text : text.trim();
|
||||||
|
} catch (NoSuchElementException ex) {
|
||||||
|
// Fallback for Login v1
|
||||||
|
return languageTextBase.getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectLanguage(String language){
|
||||||
|
try {
|
||||||
|
WebElement langLink = languages.findElement(By.xpath("//option[text()[contains(.,'" + language + "')]]"));
|
||||||
|
langLink.click();
|
||||||
|
} catch (NoSuchElementException ex) {
|
||||||
|
// Fallback for Login v1
|
||||||
|
WebElement langLink = localeDropdownBase.findElement(By.xpath("//a[text()[contains(.,'" + language + "')]]"));
|
||||||
|
langLink.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -24,7 +24,7 @@ import org.openqa.selenium.support.FindBy;
|
|||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
public class ErrorPage extends AbstractPage {
|
public class ErrorPage extends AbstractLoginPage {
|
||||||
|
|
||||||
@FindBy(className = "instruction")
|
@FindBy(className = "instruction")
|
||||||
private WebElement errorMessage;
|
private WebElement errorMessage;
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import org.openqa.selenium.support.FindBy;
|
|||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
public class InfoPage extends AbstractPage {
|
public class InfoPage extends AbstractLoginPage {
|
||||||
|
|
||||||
@FindBy(className = "instruction")
|
@FindBy(className = "instruction")
|
||||||
private WebElement infoMessage;
|
private WebElement infoMessage;
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
package org.keycloak.testframework.ui.page;
|
||||||
|
|
||||||
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
public class LoginExpiredPage extends AbstractLoginPage {
|
||||||
|
|
||||||
|
public LoginExpiredPage(ManagedWebDriver driver) {
|
||||||
|
super(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FindBy(id = "loginRestartLink")
|
||||||
|
private WebElement loginRestartLink;
|
||||||
|
|
||||||
|
@FindBy(id = "loginContinueLink")
|
||||||
|
private WebElement loginContinueLink;
|
||||||
|
|
||||||
|
|
||||||
|
public void clickLoginRestartLink() {
|
||||||
|
loginRestartLink.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clickLoginContinueLink() {
|
||||||
|
loginContinueLink.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExpectedPageId() {
|
||||||
|
return "login-login-page-expired";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,10 +3,11 @@ package org.keycloak.testframework.ui.page;
|
|||||||
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.NoSuchElementException;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.support.FindBy;
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
public class LoginPage extends AbstractPage {
|
public class LoginPage extends AbstractLoginPage {
|
||||||
|
|
||||||
@FindBy(id = "username")
|
@FindBy(id = "username")
|
||||||
private WebElement usernameInput;
|
private WebElement usernameInput;
|
||||||
@ -20,12 +21,23 @@ public class LoginPage extends AbstractPage {
|
|||||||
@FindBy(id = "rememberMe")
|
@FindBy(id = "rememberMe")
|
||||||
private WebElement rememberMe;
|
private WebElement rememberMe;
|
||||||
|
|
||||||
|
@FindBy(linkText = "Forgot Password?")
|
||||||
|
private WebElement resetPasswordLink;
|
||||||
|
|
||||||
|
@FindBy(className = "pf-m-success")
|
||||||
|
private WebElement loginSuccessMessage;
|
||||||
|
|
||||||
|
@FindBy(id = "input-error-username")
|
||||||
|
private WebElement userNameInputError;
|
||||||
|
|
||||||
public LoginPage(ManagedWebDriver driver) {
|
public LoginPage(ManagedWebDriver driver) {
|
||||||
super(driver);
|
super(driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fillLogin(String username, String password) {
|
public void fillLogin(String username, String password) {
|
||||||
|
usernameInput.clear();
|
||||||
usernameInput.sendKeys(username);
|
usernameInput.sendKeys(username);
|
||||||
|
passwordInput.clear();
|
||||||
passwordInput.sendKeys(password);
|
passwordInput.sendKeys(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +66,14 @@ public class LoginPage extends AbstractPage {
|
|||||||
return rememberMe.isSelected();
|
return rememberMe.isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetPassword() {
|
||||||
|
resetPasswordLink.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSuccessMessage() {
|
||||||
|
return loginSuccessMessage != null ? loginSuccessMessage.getText() : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getExpectedPageId() {
|
public String getExpectedPageId() {
|
||||||
return "login-login";
|
return "login-login";
|
||||||
@ -66,4 +86,13 @@ public class LoginPage extends AbstractPage {
|
|||||||
public void clearUsernameInput() {
|
public void clearUsernameInput() {
|
||||||
usernameInput.clear();
|
usernameInput.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUsernameInputError() {
|
||||||
|
try {
|
||||||
|
return userNameInputError.getText();
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
package org.keycloak.testframework.ui.page;
|
||||||
|
|
||||||
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
public class LoginPasswordResetPage extends AbstractLoginPage {
|
||||||
|
|
||||||
|
@FindBy(id = "username")
|
||||||
|
private WebElement usernameInput;
|
||||||
|
|
||||||
|
@FindBy(css = "[type=\"submit\"]")
|
||||||
|
private WebElement submitButton;
|
||||||
|
|
||||||
|
@FindBy(id = "kc-reset-password-form")
|
||||||
|
private WebElement formResetPassword;
|
||||||
|
|
||||||
|
public LoginPasswordResetPage(ManagedWebDriver driver) {
|
||||||
|
super(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changePassword(String username) {
|
||||||
|
usernameInput.clear();
|
||||||
|
usernameInput.sendKeys(username);
|
||||||
|
|
||||||
|
submitButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormUrl() {
|
||||||
|
return formResetPassword.getAttribute("action");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExpectedPageId() {
|
||||||
|
return "login-login-reset-password";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -24,7 +24,7 @@ import org.openqa.selenium.support.FindBy;
|
|||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
public class LoginPasswordUpdatePage extends AbstractPage {
|
public class LoginPasswordUpdatePage extends AbstractLoginPage {
|
||||||
|
|
||||||
@FindBy(id = "password-new")
|
@FindBy(id = "password-new")
|
||||||
private WebElement newPasswordInput;
|
private WebElement newPasswordInput;
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
package org.keycloak.testframework.ui.page;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
public class OAuthGrantPage extends AbstractLoginPage {
|
||||||
|
|
||||||
|
// Locale-resolved built-in client scope consents
|
||||||
|
public static final String PROFILE_CONSENT_TEXT = "User profile";
|
||||||
|
public static final String EMAIL_CONSENT_TEXT = "Email address";
|
||||||
|
public static final String ADDRESS_CONSENT_TEXT = "Address";
|
||||||
|
public static final String PHONE_CONSENT_TEXT = "Phone number";
|
||||||
|
public static final String OFFLINE_ACCESS_CONSENT_TEXT = "Offline Access";
|
||||||
|
public static final String ROLES_CONSENT_TEXT = "User roles";
|
||||||
|
|
||||||
|
@FindBy(css = "[name=\"accept\"]")
|
||||||
|
private WebElement acceptButton;
|
||||||
|
@FindBy(css = "[name=\"cancel\"]")
|
||||||
|
private WebElement cancelButton;
|
||||||
|
|
||||||
|
public OAuthGrantPage(ManagedWebDriver driver) {
|
||||||
|
super(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void accept(){
|
||||||
|
acceptButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel(){
|
||||||
|
cancelButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDisplayedGrants() {
|
||||||
|
List<String> table = new ArrayList<>();
|
||||||
|
WebElement divKcOauth = driver.findElement(By.id("kc-oauth"));
|
||||||
|
for (WebElement li : divKcOauth.findElements(By.tagName("li"))) {
|
||||||
|
WebElement span = li.findElement(By.tagName("span"));
|
||||||
|
table.add(span.getText());
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertGrants(String... expectedGrants) {
|
||||||
|
List<String> displayed = getDisplayedGrants();
|
||||||
|
List<String> expected = Arrays.asList(expectedGrants);
|
||||||
|
Assertions.assertTrue(displayed.containsAll(expected) && expected.containsAll(displayed),
|
||||||
|
"Not matched grants. Displayed grants: " + displayed + ", expected grants: " + expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExpectedPageId() {
|
||||||
|
return "login-login-oauth-grant";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package org.keycloak.testframework.ui.page;
|
||||||
|
|
||||||
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
public class TermsAndConditionsPage extends AbstractLoginPage {
|
||||||
|
|
||||||
|
@FindBy(id = "kc-accept")
|
||||||
|
private WebElement submitButton;
|
||||||
|
|
||||||
|
@FindBy(id = "kc-decline")
|
||||||
|
private WebElement cancelButton;
|
||||||
|
|
||||||
|
public TermsAndConditionsPage(ManagedWebDriver driver) {
|
||||||
|
super(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void acceptTerms() {
|
||||||
|
submitButton.click();
|
||||||
|
}
|
||||||
|
public void declineTerms() {
|
||||||
|
cancelButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExpectedPageId() {
|
||||||
|
return "login-terms";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package org.keycloak.testframework.ui.webdriver;
|
package org.keycloak.testframework.ui.webdriver;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class AssertionUtils {
|
public class AssertionUtils {
|
||||||
|
|
||||||
@ -11,7 +12,8 @@ public class AssertionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void assertTitle(String title) {
|
public void assertTitle(String title) {
|
||||||
Assertions.assertEquals(title, managed.page().getTitle());
|
String kcPageTitle = managed.findElement(By.id("kc-page-title")).getText();
|
||||||
|
Assertions.assertEquals(title, kcPageTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
package org.keycloak.testframework.ui.webdriver;
|
package org.keycloak.testframework.ui.webdriver;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.keycloak.cookie.CookieType;
|
||||||
|
|
||||||
import org.openqa.selenium.Cookie;
|
import org.openqa.selenium.Cookie;
|
||||||
|
|
||||||
public class CookieUtils {
|
public class CookieUtils {
|
||||||
@ -14,6 +18,18 @@ public class CookieUtils {
|
|||||||
managed.driver().manage().addCookie(cookie);
|
managed.driver().manage().addCookie(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cookie get(CookieType cookieType) {
|
||||||
|
return managed.driver().manage().getCookieNamed(cookieType.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Cookie> getAll() {
|
||||||
|
return managed.driver().manage().getCookies();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cookie get(String name) {
|
||||||
|
return managed.driver().manage().getCookieNamed(name);
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteAll() {
|
public void deleteAll() {
|
||||||
managed.driver().manage().deleteAllCookies();
|
managed.driver().manage().deleteAllCookies();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ public class ManagedWebDriver {
|
|||||||
private AssertionUtils assertionUtils = new AssertionUtils(this);
|
private AssertionUtils assertionUtils = new AssertionUtils(this);
|
||||||
private CookieUtils cookieUtils = new CookieUtils(this);
|
private CookieUtils cookieUtils = new CookieUtils(this);
|
||||||
private PageUtils pageUtils = new PageUtils(this);
|
private PageUtils pageUtils = new PageUtils(this);
|
||||||
|
private NavigateUtils navigateUtils = new NavigateUtils(this);
|
||||||
private WaitUtils waitUtils = new WaitUtils(this);
|
private WaitUtils waitUtils = new WaitUtils(this);
|
||||||
|
|
||||||
public ManagedWebDriver(WebDriver driver) {
|
public ManagedWebDriver(WebDriver driver) {
|
||||||
@ -65,6 +66,10 @@ public class ManagedWebDriver {
|
|||||||
return pageUtils;
|
return pageUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NavigateUtils navigate() {
|
||||||
|
return navigateUtils;
|
||||||
|
}
|
||||||
|
|
||||||
public WaitUtils waiting() {
|
public WaitUtils waiting() {
|
||||||
return waitUtils;
|
return waitUtils;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
package org.keycloak.testframework.ui.webdriver;
|
||||||
|
|
||||||
|
import org.keycloak.testframework.ui.page.AbstractPage;
|
||||||
|
|
||||||
|
public class NavigateUtils {
|
||||||
|
|
||||||
|
private final ManagedWebDriver driver;
|
||||||
|
|
||||||
|
NavigateUtils(ManagedWebDriver driver) {
|
||||||
|
this.driver = driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
driver.driver().navigate().refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void backWithRefresh(AbstractPage expectedPage) {
|
||||||
|
driver.driver().navigate().back();
|
||||||
|
|
||||||
|
String currentPageId = driver.page().getCurrentPageId();
|
||||||
|
if (!expectedPage.getExpectedPageId().equals(currentPageId) && driver.getBrowserType().equals(BrowserType.CHROME)) {
|
||||||
|
driver.driver().navigate().refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedPage.assertCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.i18n;
|
package org.keycloak.tests.i18n;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -29,20 +29,34 @@ import jakarta.mail.MessagingException;
|
|||||||
import jakarta.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
|
import org.keycloak.admin.client.Keycloak;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.http.simple.SimpleHttp;
|
||||||
import org.keycloak.http.simple.SimpleHttpResponse;
|
import org.keycloak.http.simple.SimpleHttpResponse;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testframework.annotations.InjectAdminClient;
|
||||||
import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
|
import org.keycloak.testframework.annotations.InjectKeycloakUrls;
|
||||||
import org.keycloak.testsuite.pages.InfoPage;
|
import org.keycloak.testframework.annotations.InjectRealm;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testframework.annotations.InjectSimpleHttp;
|
||||||
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
|
import org.keycloak.testframework.annotations.InjectUser;
|
||||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||||
import org.keycloak.testsuite.util.DroneUtils;
|
import org.keycloak.testframework.mail.MailServer;
|
||||||
import org.keycloak.testsuite.util.GreenMailRule;
|
import org.keycloak.testframework.mail.annotations.InjectMailServer;
|
||||||
import org.keycloak.testsuite.util.MailUtils;
|
import org.keycloak.testframework.oauth.OAuthClient;
|
||||||
import org.keycloak.testsuite.util.WaitUtils;
|
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||||
|
import org.keycloak.testframework.realm.ManagedRealm;
|
||||||
|
import org.keycloak.testframework.realm.ManagedUser;
|
||||||
|
import org.keycloak.testframework.server.KeycloakUrls;
|
||||||
|
import org.keycloak.testframework.ui.annotations.InjectPage;
|
||||||
|
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
|
||||||
|
import org.keycloak.testframework.ui.page.InfoPage;
|
||||||
|
import org.keycloak.testframework.ui.page.LoginPage;
|
||||||
|
import org.keycloak.testframework.ui.page.LoginPasswordResetPage;
|
||||||
|
import org.keycloak.testframework.ui.page.LoginPasswordUpdatePage;
|
||||||
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
import org.keycloak.tests.common.BasicUserConfig;
|
||||||
|
import org.keycloak.tests.utils.MailUtils;
|
||||||
|
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
@ -51,10 +65,9 @@ import org.apache.http.client.methods.HttpPost;
|
|||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.hamcrest.MatcherAssert;
|
||||||
import org.junit.Assert;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.Rule;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.Test;
|
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.Cookie;
|
import org.openqa.selenium.Cookie;
|
||||||
|
|
||||||
@ -63,33 +76,55 @@ import static org.hamcrest.Matchers.containsString;
|
|||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public class EmailTest extends AbstractI18NTest {
|
@KeycloakIntegrationTest
|
||||||
|
public class EmailTest {
|
||||||
|
|
||||||
@Rule
|
@InjectRealm(config = RealmWithInternationalization.class)
|
||||||
public GreenMailRule greenMail = new GreenMailRule();
|
ManagedRealm realm;
|
||||||
|
|
||||||
@Page
|
@InjectUser(config = BasicUserConfig.class)
|
||||||
protected LoginPage loginPage;
|
ManagedUser user;
|
||||||
|
|
||||||
@Page
|
@InjectMailServer
|
||||||
protected LoginPasswordResetPage resetPasswordPage;
|
MailServer mailServer;
|
||||||
|
|
||||||
@Page
|
@InjectAdminClient
|
||||||
private InfoPage infoPage;
|
Keycloak adminClient;
|
||||||
|
|
||||||
@Page
|
@InjectPage
|
||||||
private LoginPasswordUpdatePage loginPasswordUpdatePage;
|
LoginPage loginPage;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
LoginPasswordResetPage resetPasswordPage;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
InfoPage infoPage;
|
||||||
|
|
||||||
|
@InjectSimpleHttp
|
||||||
|
SimpleHttp simpleHttp;
|
||||||
|
|
||||||
|
@InjectWebDriver
|
||||||
|
ManagedWebDriver driver;
|
||||||
|
|
||||||
|
@InjectOAuthClient
|
||||||
|
OAuthClient oauth;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
LoginPasswordUpdatePage loginPasswordUpdatePage;
|
||||||
|
|
||||||
|
@InjectKeycloakUrls
|
||||||
|
KeycloakUrls keycloakUrls;
|
||||||
|
|
||||||
private void changeUserLocale(String locale) {
|
private void changeUserLocale(String locale) {
|
||||||
UserRepresentation user = findUser("login-test");
|
UserRepresentation userRep = user.admin().toRepresentation();
|
||||||
user.singleAttribute(UserModel.LOCALE, locale);
|
userRep.singleAttribute(UserModel.LOCALE, locale);
|
||||||
ApiUtil.findUserByUsernameId(testRealm(), "login-test").update(user);
|
user.admin().update(userRep);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -112,16 +147,16 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
String subjectEn = "Subject EN";
|
String subjectEn = "Subject EN";
|
||||||
String expectedBodyContentEn = "Body EN";
|
String expectedBodyContentEn = "Body EN";
|
||||||
String bodyMessageEn = expectedBodyContentEn + placeholders;
|
String bodyMessageEn = expectedBodyContentEn + placeholders;
|
||||||
testRealm().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), subjectMessageKey, subjectEn);
|
realm.cleanup().add(r -> r.localization().deleteRealmLocalizationTexts(Locale.ENGLISH.toLanguageTag()));
|
||||||
testRealm().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), bodyMessageKey, bodyMessageEn);
|
realm.admin().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), subjectMessageKey, subjectEn);
|
||||||
getCleanup().addLocalization(Locale.ENGLISH.toLanguageTag());
|
realm.admin().localization().saveRealmLocalizationText(Locale.ENGLISH.toLanguageTag(), bodyMessageKey, bodyMessageEn);
|
||||||
|
|
||||||
String subjectDe = "Subject DE";
|
String subjectDe = "Subject DE";
|
||||||
String expectedBodyContentDe = "Body DE";
|
String expectedBodyContentDe = "Body DE";
|
||||||
String bodyMessageDe = expectedBodyContentDe + placeholders;
|
String bodyMessageDe = expectedBodyContentDe + placeholders;
|
||||||
testRealm().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), subjectMessageKey, subjectDe);
|
realm.cleanup().add(r -> r.localization().deleteRealmLocalizationTexts(Locale.GERMAN.toLanguageTag()));
|
||||||
testRealm().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), bodyMessageKey, bodyMessageDe);
|
realm.admin().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), subjectMessageKey, subjectDe);
|
||||||
getCleanup().addLocalization(Locale.GERMAN.toLanguageTag());
|
realm.admin().localization().saveRealmLocalizationText(Locale.GERMAN.toLanguageTag(), bodyMessageKey, bodyMessageDe);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sendResetPasswordEmail();
|
sendResetPasswordEmail();
|
||||||
@ -150,12 +185,11 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updatePasswordFromAdmin() throws MessagingException, IOException {
|
public void updatePasswordFromAdmin() {
|
||||||
changeUserLocale(null);
|
changeUserLocale(null);
|
||||||
try {
|
try {
|
||||||
UserResource testUser = ApiUtil.findUserByUsernameId(testRealm(), "login-test");
|
UserResource testUser = user.admin();
|
||||||
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
|
SimpleHttpResponse responseGet = simpleHttp.doPut(keycloakUrls.getAdmin() + "/realms/" + realm.getName() + "/users/" + testUser.toRepresentation().getId() + "/execute-actions-email")
|
||||||
SimpleHttpResponse responseGet = SimpleHttpDefault.doPut(getAuthServerRoot() + "admin/realms/test/users/" + testUser.toRepresentation().getId() + "/execute-actions-email", httpClient)
|
|
||||||
.auth(adminClient.tokenManager().getAccessTokenString())
|
.auth(adminClient.tokenManager().getAccessTokenString())
|
||||||
.header("Accept-Language", "de")
|
.header("Accept-Language", "de")
|
||||||
.json(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()))
|
.json(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()))
|
||||||
@ -163,13 +197,13 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
|
|
||||||
assertEquals(responseGet.getStatus(), 204);
|
assertEquals(responseGet.getStatus(), 204);
|
||||||
|
|
||||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
MimeMessage message = mailServer.getReceivedMessages()[0];
|
||||||
String textBody = MailUtils.getBody(message).getText();
|
String textBody = MailUtils.getBody(message).getText();
|
||||||
|
|
||||||
Assert.assertThat(textBody, containsString("Your administrator has just requested"));
|
MatcherAssert.assertThat(textBody, containsString("Your administrator has just requested"));
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Assert.fail(e.getMessage());
|
Assertions.fail(e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
// Revert
|
// Revert
|
||||||
changeUserLocale("en");
|
changeUserLocale("en");
|
||||||
@ -181,12 +215,12 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
changeUserLocale(null);
|
changeUserLocale(null);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
loginPage.open();
|
oauth.openLoginForm();
|
||||||
loginPage.resetPassword();
|
loginPage.resetPassword();
|
||||||
|
|
||||||
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
|
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
|
||||||
|
|
||||||
Set<Cookie> cookies = oauth.getDriver().manage().getCookies();
|
Set<Cookie> cookies = driver.cookies().getAll();
|
||||||
String cookieHeader = cookies.stream()
|
String cookieHeader = cookies.stream()
|
||||||
.map(cookie -> cookie.getName() + "=" + cookie.getValue())
|
.map(cookie -> cookie.getName() + "=" + cookie.getValue())
|
||||||
.collect(Collectors.joining("; "));
|
.collect(Collectors.joining("; "));
|
||||||
@ -197,7 +231,7 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "de");
|
post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "de");
|
||||||
|
|
||||||
List<NameValuePair> parameters = new LinkedList<>();
|
List<NameValuePair> parameters = new LinkedList<>();
|
||||||
parameters.add(new BasicNameValuePair("username", "login-test"));
|
parameters.add(new BasicNameValuePair("username", "basic-user"));
|
||||||
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8);
|
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8);
|
||||||
post.setEntity(formEntity);
|
post.setEntity(formEntity);
|
||||||
|
|
||||||
@ -213,16 +247,18 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
|
|
||||||
|
|
||||||
private void sendResetPasswordEmail() {
|
private void sendResetPasswordEmail() {
|
||||||
loginPage.open();
|
oauth.openLoginForm();
|
||||||
loginPage.resetPassword();
|
loginPage.resetPassword();
|
||||||
resetPasswordPage.changePassword("login-test");
|
resetPasswordPage.assertCurrent();
|
||||||
|
resetPasswordPage.changePassword("basic-user");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyResetPassword(String expectedSubject, String expectedTextBodyContent, String expectedHtmlBodyContent, int expectedMsgCount)
|
private void verifyResetPassword(String expectedSubject, String expectedTextBodyContent, String expectedHtmlBodyContent, int expectedMsgCount)
|
||||||
throws MessagingException, IOException {
|
throws MessagingException, IOException {
|
||||||
assertEquals(expectedMsgCount, greenMail.getReceivedMessages().length);
|
mailServer.waitForIncomingEmail(expectedMsgCount);
|
||||||
|
assertEquals(expectedMsgCount, mailServer.getReceivedMessages().length);
|
||||||
|
|
||||||
MimeMessage message = greenMail.getReceivedMessages()[expectedMsgCount - 1];
|
MimeMessage message = mailServer.getReceivedMessages()[expectedMsgCount - 1];
|
||||||
|
|
||||||
assertEquals(expectedSubject, message.getSubject());
|
assertEquals(expectedSubject, message.getSubject());
|
||||||
|
|
||||||
@ -242,44 +278,41 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
// Issue 13922
|
// Issue 13922
|
||||||
@Test
|
@Test
|
||||||
public void changeLocaleOnInfoPage() throws InterruptedException, IOException {
|
public void changeLocaleOnInfoPage() throws InterruptedException, IOException {
|
||||||
UserResource testUser = ApiUtil.findUserByUsernameId(testRealm(), "login-test");
|
UserResource testUser = user.admin();
|
||||||
testUser.executeActionsEmail(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
|
testUser.executeActionsEmail(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
|
||||||
|
|
||||||
if (!greenMail.waitForIncomingEmail(1)) {
|
if (!mailServer.waitForIncomingEmail(1)) {
|
||||||
Assert.fail("Error when receiving email");
|
Assertions.fail("Error when receiving email");
|
||||||
}
|
}
|
||||||
|
|
||||||
String link = MailUtils.getPasswordResetEmailLink(greenMail.getLastReceivedMessage());
|
String link = MailUtils.getPasswordResetEmailLink(mailServer.getLastReceivedMessage());
|
||||||
|
|
||||||
// Make sure kc_locale added to link doesn't set locale
|
// Make sure kc_locale added to link doesn't set locale
|
||||||
link += "&kc_locale=de";
|
link += "&kc_locale=de";
|
||||||
|
|
||||||
DroneUtils.getCurrentDriver().navigate().to(link);
|
driver.open(link);
|
||||||
WaitUtils.waitForPageToLoad();
|
|
||||||
|
|
||||||
Assert.assertTrue("Expected to be on InfoPage, but it was on " + DroneUtils.getCurrentDriver().getTitle(), infoPage.isCurrent());
|
infoPage.assertCurrent();
|
||||||
assertThat(infoPage.getLanguageDropdownText(), is(equalTo("English")));
|
assertThat(infoPage.getSelectedLanguage(), is(equalTo("English")));
|
||||||
|
|
||||||
infoPage.openLanguage("Deutsch");
|
infoPage.selectLanguage("Deutsch");
|
||||||
|
|
||||||
assertThat(DroneUtils.getCurrentDriver().getPageSource(), containsString("Passwort aktualisieren"));
|
assertThat(driver.page().getPageSource(), containsString("Passwort aktualisieren"));
|
||||||
|
|
||||||
infoPage.clickToContinueDe();
|
driver.findElement(By.linkText("» Klicken Sie hier um fortzufahren")).click();
|
||||||
|
|
||||||
loginPasswordUpdatePage.openLanguage("English");
|
loginPasswordUpdatePage.selectLanguage("English");
|
||||||
loginPasswordUpdatePage.changePassword("pass", "pass");
|
loginPasswordUpdatePage.changePassword("pass", "pass");
|
||||||
WaitUtils.waitForPageToLoad();
|
|
||||||
|
|
||||||
Assert.assertTrue("Expected to be on InfoPage, but it was on " + DroneUtils.getCurrentDriver().getTitle(), infoPage.isCurrent());
|
|
||||||
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
|
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
|
||||||
|
|
||||||
// Change language again when on final info page with the message about updated account (authSession removed already at this point)
|
// Change language again when on final info page with the message about updated account (authSession removed already at this point)
|
||||||
infoPage.openLanguage("Deutsch");
|
infoPage.selectLanguage("Deutsch");
|
||||||
assertEquals("Deutsch", infoPage.getLanguageDropdownText());
|
assertEquals("Deutsch", infoPage.getSelectedLanguage());
|
||||||
assertThat(infoPage.getInfo(), containsString("Ihr Benutzerkonto wurde aktualisiert."));
|
assertThat(infoPage.getInfo(), containsString("Ihr Benutzerkonto wurde aktualisiert."));
|
||||||
|
|
||||||
infoPage.openLanguage("English");
|
infoPage.selectLanguage("English");
|
||||||
assertEquals("English", infoPage.getLanguageDropdownText());
|
assertEquals("English", infoPage.getSelectedLanguage());
|
||||||
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
|
assertThat(infoPage.getInfo(), containsString("Your account has been updated."));
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -289,16 +322,16 @@ public class EmailTest extends AbstractI18NTest {
|
|||||||
public void resetPasswordOriginalUiLocalePreservedAfterForgetPassword() throws MessagingException, IOException {
|
public void resetPasswordOriginalUiLocalePreservedAfterForgetPassword() throws MessagingException, IOException {
|
||||||
// Assert login page is in german
|
// Assert login page is in german
|
||||||
oauth.loginForm().uiLocales("de").open();
|
oauth.loginForm().uiLocales("de").open();
|
||||||
assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
assertEquals("Deutsch", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
// Click "Forget password"
|
// Click "Forget password"
|
||||||
driver.findElement(By.linkText("Passwort vergessen?")).click();
|
driver.findElement(By.linkText("Passwort vergessen?")).click();
|
||||||
assertEquals("Deutsch", resetPasswordPage.getLanguageDropdownText());
|
assertEquals("Deutsch", resetPasswordPage.getSelectedLanguage());
|
||||||
resetPasswordPage.changePassword("login-test");
|
resetPasswordPage.changePassword("basic-user");
|
||||||
|
|
||||||
// Ensure that page is still in german (after authenticationSession was forked on server). The emailSentMessage should be also displayed in german
|
// Ensure that page is still in german (after authenticationSession was forked on server). The emailSentMessage should be also displayed in german
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
assertEquals("Deutsch", loginPage.getSelectedLanguage());
|
||||||
assertEquals("Sie sollten in Kürze eine E-Mail mit weiteren Instruktionen erhalten.", loginPage.getSuccessMessage());
|
assertEquals("Sie sollten in Kürze eine E-Mail mit weiteren Instruktionen erhalten.", loginPage.getSuccessMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -0,0 +1,507 @@
|
|||||||
|
/*
|
||||||
|
* 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.tests.i18n;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||||
|
import org.keycloak.cookie.CookieType;
|
||||||
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.forms.login.freemarker.DetachedInfoStateChecker;
|
||||||
|
import org.keycloak.http.simple.SimpleHttp;
|
||||||
|
import org.keycloak.locale.LocaleSelectorProvider;
|
||||||
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.testframework.annotations.InjectEvents;
|
||||||
|
import org.keycloak.testframework.annotations.InjectRealm;
|
||||||
|
import org.keycloak.testframework.annotations.InjectSimpleHttp;
|
||||||
|
import org.keycloak.testframework.annotations.InjectUser;
|
||||||
|
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||||
|
import org.keycloak.testframework.events.EventAssertion;
|
||||||
|
import org.keycloak.testframework.events.Events;
|
||||||
|
import org.keycloak.testframework.injection.LifeCycle;
|
||||||
|
import org.keycloak.testframework.oauth.OAuthClient;
|
||||||
|
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||||
|
import org.keycloak.testframework.realm.ClientConfigBuilder;
|
||||||
|
import org.keycloak.testframework.realm.ManagedRealm;
|
||||||
|
import org.keycloak.testframework.realm.ManagedUser;
|
||||||
|
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||||
|
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
|
||||||
|
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
|
||||||
|
import org.keycloak.testframework.server.KeycloakServerConfig;
|
||||||
|
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
|
||||||
|
import org.keycloak.testframework.ui.annotations.InjectPage;
|
||||||
|
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
|
||||||
|
import org.keycloak.testframework.ui.page.AbstractLoginPage;
|
||||||
|
import org.keycloak.testframework.ui.page.ErrorPage;
|
||||||
|
import org.keycloak.testframework.ui.page.LoginExpiredPage;
|
||||||
|
import org.keycloak.testframework.ui.page.LoginPage;
|
||||||
|
import org.keycloak.testframework.ui.page.LoginPasswordUpdatePage;
|
||||||
|
import org.keycloak.testframework.ui.page.OAuthGrantPage;
|
||||||
|
import org.keycloak.testframework.ui.page.TermsAndConditionsPage;
|
||||||
|
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
|
||||||
|
import org.keycloak.tests.common.BasicUserConfig;
|
||||||
|
import org.keycloak.testsuite.forms.ClickThroughAuthenticator;
|
||||||
|
import org.keycloak.testsuite.util.FlowUtil;
|
||||||
|
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.Cookie;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
||||||
|
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||||
|
*/
|
||||||
|
@KeycloakIntegrationTest(config = LoginPageTest.ServerConfig.class)
|
||||||
|
public class LoginPageTest {
|
||||||
|
|
||||||
|
@InjectRealm(config = LoginPageRealmConfig.class)
|
||||||
|
ManagedRealm realm;
|
||||||
|
|
||||||
|
@InjectUser(config = BasicUserConfig.class, lifecycle = LifeCycle.METHOD)
|
||||||
|
ManagedUser user;
|
||||||
|
|
||||||
|
@InjectOAuthClient
|
||||||
|
protected OAuthClient oauth;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
protected LoginPage loginPage;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
protected ErrorPage errorPage;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
protected LoginPasswordUpdatePage changePasswordPage;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
protected OAuthGrantPage grantPage;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
protected LoginExpiredPage loginExpiredPage;
|
||||||
|
|
||||||
|
@InjectPage
|
||||||
|
protected TermsAndConditionsPage termsPage;
|
||||||
|
|
||||||
|
@InjectWebDriver
|
||||||
|
protected ManagedWebDriver driver;
|
||||||
|
|
||||||
|
@InjectSimpleHttp
|
||||||
|
protected SimpleHttp simpleHttp;
|
||||||
|
|
||||||
|
@InjectEvents
|
||||||
|
protected Events events;
|
||||||
|
|
||||||
|
@InjectRunOnServer
|
||||||
|
protected RunOnServerClient runOnServer;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void languageDropdown() {
|
||||||
|
oauth.openLoginForm();
|
||||||
|
assertEquals("English", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
switchLanguageToGermanAndBack("Username or email", "Benutzername oder E-Mail", loginPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void uiLocalesParameter() {
|
||||||
|
oauth.loginForm().open();
|
||||||
|
assertEquals("English", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
//test if cookie works
|
||||||
|
oauth.loginForm().uiLocales("de").open();
|
||||||
|
assertEquals("Deutsch", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
driver.cookies().deleteAll();
|
||||||
|
oauth.loginForm().uiLocales("de").open();
|
||||||
|
assertEquals("Deutsch", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
driver.cookies().deleteAll();
|
||||||
|
oauth.loginForm().uiLocales("en de").open();
|
||||||
|
assertEquals("English", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
driver.cookies().deleteAll();
|
||||||
|
oauth.loginForm().uiLocales("fr de").open();
|
||||||
|
assertEquals("Deutsch", loginPage.getSelectedLanguage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void htmlLangAttributeWithInternationalizationEnabled() {
|
||||||
|
oauth.openLoginForm();
|
||||||
|
assertEquals("en", getHtmlLanguage());
|
||||||
|
|
||||||
|
oauth.loginForm().uiLocales("de").open();
|
||||||
|
assertEquals("de", getHtmlLanguage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void htmlLangAttributeWithInternationalizationDisabled() {
|
||||||
|
realm.updateWithCleanup(r -> r.internationalizationEnabled(false));
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
assertEquals("en", getHtmlLanguage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptLanguageHeader() throws IOException {
|
||||||
|
String responseDe = simpleHttp.doGet(oauth.loginForm().build()).header("Accept-Language", "de").header("Accept", "text/html").asString();
|
||||||
|
Assertions.assertTrue(responseDe.contains("Bei Ihrem Konto anmelden"));
|
||||||
|
|
||||||
|
String responseEn = simpleHttp.doGet(oauth.loginForm().build()).header("Accept-Language", "en").header("Accept", "text/html").asString();
|
||||||
|
Assertions.assertTrue(responseEn.contains("Sign in to your account"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIdentityProviderCapitalization(){
|
||||||
|
oauth.openLoginForm();
|
||||||
|
// contains even name of sub-item - svg element in this case
|
||||||
|
assertThat(loginPage.findSocialButton("github").getText(), is("GitHub"));
|
||||||
|
assertThat(loginPage.findSocialButton("mysaml").getText(), is("mysaml"));
|
||||||
|
assertThat(loginPage.findSocialButton("myoidc").getText(), is("MyOIDC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// KEYCLOAK-3887
|
||||||
|
@Test
|
||||||
|
public void languageChangeRequiredActions() {
|
||||||
|
UserResource user = this.user.admin();
|
||||||
|
UserRepresentation userRep = user.toRepresentation();
|
||||||
|
userRep.setRequiredActions(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
|
||||||
|
user.update(userRep);
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
oauth.fillLoginForm("basic-user", "password");
|
||||||
|
|
||||||
|
changePasswordPage.assertCurrent();
|
||||||
|
assertEquals("English", changePasswordPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
// Switch language
|
||||||
|
switchLanguageToGermanAndBack("Update password", "Passwort aktualisieren", changePasswordPage);
|
||||||
|
|
||||||
|
// Update password
|
||||||
|
changePasswordPage.changePassword("password", "password");
|
||||||
|
|
||||||
|
Assertions.assertNotNull(oauth.parseLoginResponse().getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// KEYCLOAK-3887
|
||||||
|
@Test
|
||||||
|
public void languageChangeConsentScreen() {
|
||||||
|
// Set client, which requires consent
|
||||||
|
oauth.client("third-party", "password");
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
oauth.fillLoginForm("basic-user", "password");
|
||||||
|
|
||||||
|
grantPage.assertCurrent();
|
||||||
|
assertEquals("English", grantPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
// Switch language
|
||||||
|
switchLanguageToGermanAndBack("Do you grant these access privileges?", "Wollen Sie diese Zugriffsrechte", changePasswordPage);
|
||||||
|
|
||||||
|
// Confirm grant
|
||||||
|
grantPage.accept();
|
||||||
|
|
||||||
|
Assertions.assertNotNull(oauth.parseLoginResponse().getCode());
|
||||||
|
|
||||||
|
// Revert client
|
||||||
|
oauth.client("test-app", "password");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void languageUserUpdates() throws InterruptedException {
|
||||||
|
oauth.openLoginForm();
|
||||||
|
loginPage.selectLanguage("Deutsch");
|
||||||
|
|
||||||
|
assertEquals("Deutsch", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
Cookie localeCookie = driver.cookies().get(CookieType.LOCALE);
|
||||||
|
assertEquals("de", localeCookie.getValue());
|
||||||
|
|
||||||
|
loginPage.fillLogin("basic-user", "password");
|
||||||
|
loginPage.submit();
|
||||||
|
|
||||||
|
Assertions.assertNotNull(oauth.parseLoginResponse().getCode());
|
||||||
|
|
||||||
|
EventAssertion.assertSuccess(events.poll()).type(EventType.UPDATE_PROFILE).userId(user.getId()).details(Details.PREF_UPDATED + UserModel.LOCALE, "de");
|
||||||
|
EventAssertion.assertSuccess(events.poll()).type(EventType.LOGIN).userId(user.getId());
|
||||||
|
|
||||||
|
UserRepresentation userRep = user.admin().toRepresentation();
|
||||||
|
assertEquals("de", userRep.getAttributes().get("locale").get(0));
|
||||||
|
|
||||||
|
String code = oauth.parseLoginResponse().getCode();
|
||||||
|
String idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
|
||||||
|
oauth.logoutRequest().idTokenHint(idTokenHint).send();
|
||||||
|
oauth.openLoginForm();
|
||||||
|
|
||||||
|
assertEquals("Deutsch", loginPage.getSelectedLanguage());
|
||||||
|
|
||||||
|
userRep.getAttributes().remove("locale");
|
||||||
|
user.admin().update(userRep);
|
||||||
|
|
||||||
|
oauth.doLogin("basic-user", "password");
|
||||||
|
|
||||||
|
// User locale should not be updated due to previous cookie
|
||||||
|
userRep = user.admin().toRepresentation();
|
||||||
|
Assertions.assertNull(userRep.getAttributes());
|
||||||
|
|
||||||
|
code = oauth.parseLoginResponse().getCode();
|
||||||
|
idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
|
||||||
|
oauth.logoutRequest().idTokenHint(idTokenHint).send();
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
|
||||||
|
// Cookie should be removed as last user to login didn't have a locale
|
||||||
|
localeCookie = driver.cookies().get(CookieType.LOCALE);
|
||||||
|
Assertions.assertNull(localeCookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test for user updating locale on the error page (when authenticationSession is not available)
|
||||||
|
@Test
|
||||||
|
public void languageUserUpdatesOnErrorPage() {
|
||||||
|
// Login page with invalid redirect_uri
|
||||||
|
String redirectUri = oauth.getRedirectUri();
|
||||||
|
oauth.redirectUri("http://invalid");
|
||||||
|
oauth.openLoginForm();
|
||||||
|
|
||||||
|
errorPage.assertCurrent();
|
||||||
|
Assertions.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
|
||||||
|
|
||||||
|
// Change language should be OK
|
||||||
|
errorPage.selectLanguage("Deutsch");
|
||||||
|
assertEquals("Deutsch", errorPage.getSelectedLanguage());
|
||||||
|
Assertions.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
|
||||||
|
|
||||||
|
// Refresh browser button should keep german language
|
||||||
|
driver.navigate().refresh();
|
||||||
|
assertEquals("Deutsch", errorPage.getSelectedLanguage());
|
||||||
|
Assertions.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
|
||||||
|
|
||||||
|
// Changing to english should work
|
||||||
|
errorPage.selectLanguage("English");
|
||||||
|
assertEquals("English", errorPage.getSelectedLanguage());
|
||||||
|
Assertions.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
|
||||||
|
|
||||||
|
oauth.redirectUri(redirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void languageUserUpdatesOnErrorPageStateCheckerTest() throws URISyntaxException {
|
||||||
|
String redirectUri = oauth.getRedirectUri();
|
||||||
|
|
||||||
|
// Login page with invalid redirect_uri
|
||||||
|
oauth.redirectUri("http://invalid");
|
||||||
|
oauth.openLoginForm();
|
||||||
|
|
||||||
|
errorPage.assertCurrent();
|
||||||
|
Assertions.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
|
||||||
|
|
||||||
|
errorPage.selectLanguage("Deutsch");
|
||||||
|
Assertions.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
|
||||||
|
|
||||||
|
// Add incorrect state checker parameter. Error page should be shown about expired action. Language won't be changed
|
||||||
|
String currentUrl = driver.getCurrentUrl();
|
||||||
|
String newUrl = KeycloakUriBuilder.fromUri(new URI(currentUrl))
|
||||||
|
.replaceQueryParam(LocaleSelectorProvider.KC_LOCALE_PARAM, "en")
|
||||||
|
.replaceQueryParam(DetachedInfoStateChecker.STATE_CHECKER_PARAM, "invalid").buildAsString();
|
||||||
|
driver.open(newUrl);
|
||||||
|
|
||||||
|
Assertions.assertEquals("Die Aktion ist nicht mehr gültig.", errorPage.getError()); // Action expired.
|
||||||
|
|
||||||
|
oauth.redirectUri(redirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void languageUserUpdatesOnExpiredPage() throws Exception {
|
||||||
|
UserRepresentation userRep = user.admin().toRepresentation();
|
||||||
|
userRep.setRequiredActions(Collections.singletonList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
|
||||||
|
user.admin().update(userRep);
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
oauth.fillLoginForm("basic-user", "invalid-password");
|
||||||
|
loginPage.assertCurrent();
|
||||||
|
|
||||||
|
assertThat(loginPage.getUsernameInputError(), is("Invalid username or password."));
|
||||||
|
loginPage.fillLogin("basic-user", "password");
|
||||||
|
loginPage.submit();
|
||||||
|
|
||||||
|
changePasswordPage.assertCurrent();
|
||||||
|
|
||||||
|
// navigate back to the login expired page and change language to german
|
||||||
|
driver.navigate().backWithRefresh(loginExpiredPage);
|
||||||
|
errorPage.selectLanguage("Deutsch");
|
||||||
|
assertEquals("Deutsch", errorPage.getSelectedLanguage());
|
||||||
|
driver.assertions().assertTitle("Diese Seite ist nicht mehr gültig.");
|
||||||
|
|
||||||
|
// continue should show password update in german
|
||||||
|
loginExpiredPage.clickLoginContinueLink();
|
||||||
|
assertEquals("Deutsch", changePasswordPage.getSelectedLanguage());
|
||||||
|
driver.assertions().assertTitle("Passwort aktualisieren");
|
||||||
|
}
|
||||||
|
|
||||||
|
// GH issue 41292
|
||||||
|
@Test
|
||||||
|
public void languageUserUpdatesOnCustomAuthenticatorPage() {
|
||||||
|
configureBrowserFlowWithClickThroughAuthenticator();
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
termsPage.assertCurrent();
|
||||||
|
|
||||||
|
// Change language on the custom page
|
||||||
|
switchLanguageToGermanAndBack("Terms and Conditions", "Bedingungen und Konditionen", termsPage);
|
||||||
|
|
||||||
|
// Revert dummy flow
|
||||||
|
RealmRepresentation rep = realm.admin().toRepresentation();
|
||||||
|
rep.setBrowserFlow("browser");
|
||||||
|
realm.admin().update(rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void realmLocalizationMessagesAreApplied() {
|
||||||
|
String realmLocalizationMessageKey = "loginAccountTitle";
|
||||||
|
|
||||||
|
String realmLocalizationMessageValueEn = "Localization Test EN";
|
||||||
|
saveLocalizationText(Locale.ENGLISH.toLanguageTag(), realmLocalizationMessageKey,
|
||||||
|
realmLocalizationMessageValueEn);
|
||||||
|
String realmLocalizationMessageValueDe = "Localization Test DE";
|
||||||
|
saveLocalizationText(Locale.GERMAN.toLanguageTag(), realmLocalizationMessageKey,
|
||||||
|
realmLocalizationMessageValueDe);
|
||||||
|
|
||||||
|
oauth.openLoginForm();
|
||||||
|
switchLanguageToGermanAndBack(realmLocalizationMessageValueEn, realmLocalizationMessageValueDe, loginPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// KEYCLOAK-18590
|
||||||
|
@Test
|
||||||
|
public void realmLocalizationMessagesAreNotCachedWithinTheTheme() {
|
||||||
|
final String locale = Locale.ENGLISH.toLanguageTag();
|
||||||
|
|
||||||
|
final String realmLocalizationMessageKey = "loginAccountTitle";
|
||||||
|
final String realmLocalizationMessageValue = "Localization Test";
|
||||||
|
|
||||||
|
saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
|
||||||
|
oauth.openLoginForm();
|
||||||
|
assertThat(driver.page().getPageSource(), containsString(realmLocalizationMessageValue));
|
||||||
|
|
||||||
|
realm.admin().localization().deleteRealmLocalizationText(locale, realmLocalizationMessageKey);
|
||||||
|
oauth.openLoginForm();
|
||||||
|
assertThat(driver.page().getPageSource(), not(containsString(realmLocalizationMessageValue)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void realmLocalizationMessagesUsedDuringErrorHandling() {
|
||||||
|
final String locale = Locale.ENGLISH.toLanguageTag();
|
||||||
|
|
||||||
|
final String realmLocalizationMessageKey = "errorTitle";
|
||||||
|
final String realmLocalizationMessageValue = "We are really sorry...";
|
||||||
|
|
||||||
|
saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
|
||||||
|
String nonExistingUrl = oauth.loginForm().build().split("protocol")[0] + "incorrect-path";
|
||||||
|
driver.open(nonExistingUrl);
|
||||||
|
|
||||||
|
assertThat(driver.page().getPageSource(), containsString(realmLocalizationMessageValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getHtmlLanguage() {
|
||||||
|
return driver.findElement(By.xpath("//html")).getAttribute("lang");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveLocalizationText(String locale, String key, String value) {
|
||||||
|
realm.admin().localization().saveRealmLocalizationText(locale, key, value);
|
||||||
|
realm.cleanup().add(r -> r.localization().deleteRealmLocalizationTexts(locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchLanguageToGermanAndBack(String expectedEnglishMessage, String expectedGermanMessage, AbstractLoginPage page) {
|
||||||
|
// Switch language to Deutsch
|
||||||
|
page.selectLanguage("Deutsch");
|
||||||
|
assertEquals("Deutsch", page.getSelectedLanguage());
|
||||||
|
String pageSource = driver.page().getPageSource();
|
||||||
|
assertThat(pageSource, not(containsString(expectedEnglishMessage)));
|
||||||
|
assertThat(pageSource, containsString(expectedGermanMessage));
|
||||||
|
|
||||||
|
// Revert language
|
||||||
|
page.selectLanguage("English");
|
||||||
|
assertEquals("English", page.getSelectedLanguage());
|
||||||
|
pageSource = driver.page().getPageSource();
|
||||||
|
assertThat(pageSource, containsString(expectedEnglishMessage));
|
||||||
|
assertThat(pageSource, not(containsString(expectedGermanMessage)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureBrowserFlowWithClickThroughAuthenticator() {
|
||||||
|
final String newFlowAlias = "browser - rule";
|
||||||
|
runOnServer.run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
|
||||||
|
runOnServer.run(session -> FlowUtil.inCurrentRealm(session)
|
||||||
|
.selectFlow(newFlowAlias)
|
||||||
|
.inForms(forms -> forms
|
||||||
|
.clear()
|
||||||
|
// Update the browser forms with a UsernamePasswordForm
|
||||||
|
.addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.REQUIRED, ClickThroughAuthenticator.PROVIDER_ID)
|
||||||
|
)
|
||||||
|
.defineAsBrowserFlow()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LoginPageRealmConfig extends RealmWithInternationalization {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RealmConfigBuilder configure(RealmConfigBuilder realm) {
|
||||||
|
realm = super.configure(realm);
|
||||||
|
realm.identityProvider(IdentityProviderBuilder.create()
|
||||||
|
.providerId("github")
|
||||||
|
.alias("github")
|
||||||
|
.build());
|
||||||
|
realm.identityProvider(IdentityProviderBuilder.create()
|
||||||
|
.providerId("saml")
|
||||||
|
.alias("mysaml")
|
||||||
|
.build());
|
||||||
|
realm.identityProvider(IdentityProviderBuilder.create()
|
||||||
|
.providerId("oidc")
|
||||||
|
.alias("myoidc")
|
||||||
|
.displayName("MyOIDC")
|
||||||
|
.build());
|
||||||
|
realm.client(ClientConfigBuilder.create().clientId("third-party").secret("password").consentRequired(true).redirectUris("*").build());
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class ServerConfig implements KeycloakServerConfig {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) {
|
||||||
|
return config.dependency("org.keycloak.tests", "keycloak-tests-custom-providers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,15 +1,22 @@
|
|||||||
package org.keycloak.testsuite.i18n;
|
package org.keycloak.tests.i18n;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.keycloak.admin.client.resource.RealmLocalizationResource;
|
import org.keycloak.admin.client.resource.RealmLocalizationResource;
|
||||||
|
import org.keycloak.testframework.annotations.InjectRealm;
|
||||||
|
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||||
|
import org.keycloak.testframework.realm.ManagedRealm;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.hasEntry;
|
import static org.hamcrest.Matchers.hasEntry;
|
||||||
|
|
||||||
public class RealmLocalizationTest extends AbstractI18NTest {
|
@KeycloakIntegrationTest
|
||||||
|
public class RealmLocalizationTest {
|
||||||
|
|
||||||
|
@InjectRealm(config = RealmWithInternationalization.class)
|
||||||
|
ManagedRealm managedRealm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make sure that realm localization texts support unicode ().
|
* Make sure that realm localization texts support unicode ().
|
||||||
@ -19,7 +26,7 @@ public class RealmLocalizationTest extends AbstractI18NTest {
|
|||||||
String locale = "en";
|
String locale = "en";
|
||||||
String key = "Äǜṳǚǘǖ";
|
String key = "Äǜṳǚǘǖ";
|
||||||
String text = "Öṏṏ";
|
String text = "Öṏṏ";
|
||||||
RealmLocalizationResource localizationResource = testRealm().localization();
|
RealmLocalizationResource localizationResource = managedRealm.admin().localization();
|
||||||
localizationResource.saveRealmLocalizationText(locale, key, text);
|
localizationResource.saveRealmLocalizationText(locale, key, text);
|
||||||
|
|
||||||
Map<String, String> localizationTexts = localizationResource.getRealmLocalizationTexts(locale, false);
|
Map<String, String> localizationTexts = localizationResource.getRealmLocalizationTexts(locale, false);
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.keycloak.tests.i18n;
|
||||||
|
|
||||||
|
import org.keycloak.testframework.realm.RealmConfig;
|
||||||
|
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||||
|
|
||||||
|
public class RealmWithInternationalization implements RealmConfig {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RealmConfigBuilder configure(RealmConfigBuilder realm) {
|
||||||
|
return realm.resetPasswordAllowed(true).internationalizationEnabled(true).supportedLocales("de", "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ import org.junit.platform.suite.api.Suite;
|
|||||||
"org.keycloak.tests.cors",
|
"org.keycloak.tests.cors",
|
||||||
"org.keycloak.tests.db",
|
"org.keycloak.tests.db",
|
||||||
"org.keycloak.tests.forms",
|
"org.keycloak.tests.forms",
|
||||||
|
"org.keycloak.tests.i18n",
|
||||||
"org.keycloak.tests.infinispan",
|
"org.keycloak.tests.infinispan",
|
||||||
"org.keycloak.tests.keys",
|
"org.keycloak.tests.keys",
|
||||||
"org.keycloak.tests.oauth",
|
"org.keycloak.tests.oauth",
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
package org.keycloak.tests.suites;
|
package org.keycloak.tests.suites;
|
||||||
|
|
||||||
import org.keycloak.tests.admin.AdminHeadersTest;
|
import org.keycloak.tests.i18n.LoginPageTest;
|
||||||
|
|
||||||
import org.junit.platform.suite.api.SelectClasses;
|
import org.junit.platform.suite.api.SelectClasses;
|
||||||
import org.junit.platform.suite.api.Suite;
|
import org.junit.platform.suite.api.Suite;
|
||||||
|
|
||||||
@Suite
|
@Suite
|
||||||
// TODO: Select relevant test classes or packages once they have been migrated
|
@SelectClasses({
|
||||||
@SelectClasses({AdminHeadersTest.class})
|
LoginPageTest.class
|
||||||
|
})
|
||||||
public class LoginV1TestSuite {
|
public class LoginV1TestSuite {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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.forms;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
|
import org.keycloak.authentication.Authenticator;
|
||||||
|
import org.keycloak.authentication.AuthenticatorFactory;
|
||||||
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ClickThroughAuthenticator implements Authenticator, AuthenticatorFactory {
|
||||||
|
public static final String PROVIDER_ID = "testsuite-dummy-click-through";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
|
Response challenge = context.form().createForm("terms.ftl");
|
||||||
|
context.challenge(challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresUser() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void action(AuthenticationFlowContext context) {
|
||||||
|
if (context.getHttpRequest().getDecodedFormParameters().containsKey("cancel")) {
|
||||||
|
authenticate(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayType() {
|
||||||
|
return "Testsuite Dummy Click Thru";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReferenceCategory() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConfigurable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
|
||||||
|
return REQUIREMENT_CHOICES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUserSetupAllowed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpText() {
|
||||||
|
return "Testsuite Dummy authenticator. User needs to click through the page to continue.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authenticator create(KeycloakSession session) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
org.keycloak.testsuite.forms.ClickThroughAuthenticator
|
||||||
@ -150,12 +150,12 @@ public abstract class AbstractOAuthClient<T> {
|
|||||||
logoutForm().open();
|
logoutForm().open();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LogoutRequest logoutRequest(String refreshToken) {
|
public LogoutRequest logoutRequest() {
|
||||||
return new LogoutRequest(refreshToken, this);
|
return new LogoutRequest(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LogoutResponse doLogout(String refreshToken) {
|
public LogoutResponse doLogout(String refreshToken) {
|
||||||
return logoutRequest(refreshToken).send();
|
return logoutRequest().refreshToken(refreshToken).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BackchannelLogoutRequest backchannelLogoutRequest(String logoutToken) {
|
public BackchannelLogoutRequest backchannelLogoutRequest(String logoutToken) {
|
||||||
|
|||||||
@ -8,11 +8,21 @@ import org.apache.http.client.methods.CloseableHttpResponse;
|
|||||||
|
|
||||||
public class LogoutRequest extends AbstractHttpPostRequest<LogoutRequest, LogoutResponse> {
|
public class LogoutRequest extends AbstractHttpPostRequest<LogoutRequest, LogoutResponse> {
|
||||||
|
|
||||||
private final String refreshToken;
|
private String refreshToken;
|
||||||
|
private String idTokenHint;
|
||||||
|
|
||||||
LogoutRequest(String refreshToken, AbstractOAuthClient<?> client) {
|
LogoutRequest(AbstractOAuthClient<?> client) {
|
||||||
super(client);
|
super(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogoutRequest refreshToken(String refreshToken) {
|
||||||
this.refreshToken = refreshToken;
|
this.refreshToken = refreshToken;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogoutRequest idTokenHint(String idTokenHint) {
|
||||||
|
this.idTokenHint = idTokenHint;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -21,7 +31,12 @@ public class LogoutRequest extends AbstractHttpPostRequest<LogoutRequest, Logout
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void initRequest() {
|
protected void initRequest() {
|
||||||
parameter(OAuth2Constants.REFRESH_TOKEN, refreshToken);
|
if (refreshToken != null) {
|
||||||
|
parameter(OAuth2Constants.REFRESH_TOKEN, refreshToken);
|
||||||
|
}
|
||||||
|
if (idTokenHint != null) {
|
||||||
|
parameter(OAuth2Constants.ID_TOKEN_HINT, idTokenHint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
|
|
||||||
* as indicated by the @author tags. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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.i18n;
|
|
||||||
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
|
||||||
import org.keycloak.testsuite.util.RealmBuilder;
|
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractI18NTest extends AbstractTestRealmKeycloakTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
|
||||||
UserBuilder user = UserBuilder.create()
|
|
||||||
.username("login-test")
|
|
||||||
.enabled(true)
|
|
||||||
.email("login@test.com")
|
|
||||||
.role("account", "manage-account")
|
|
||||||
.password("password");
|
|
||||||
RealmBuilder.edit(testRealm).user(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove cookies at the end so that the next test will start out
|
|
||||||
* using the default locale.
|
|
||||||
*/
|
|
||||||
@After
|
|
||||||
public void deleteCookies() {
|
|
||||||
driver.manage().deleteAllCookies();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,488 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.i18n;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.core.Response;
|
|
||||||
|
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
|
||||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
|
||||||
import org.keycloak.cookie.CookieType;
|
|
||||||
import org.keycloak.events.Details;
|
|
||||||
import org.keycloak.events.EventType;
|
|
||||||
import org.keycloak.forms.login.freemarker.DetachedInfoStateChecker;
|
|
||||||
import org.keycloak.locale.LocaleSelectorProvider;
|
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
|
||||||
import org.keycloak.testsuite.forms.ClickThroughAuthenticator;
|
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
|
||||||
import org.keycloak.testsuite.pages.ErrorPage;
|
|
||||||
import org.keycloak.testsuite.pages.LanguageComboboxAwarePage;
|
|
||||||
import org.keycloak.testsuite.pages.LoginExpiredPage;
|
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
|
||||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
|
||||||
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
|
||||||
import org.keycloak.testsuite.pages.PageUtils;
|
|
||||||
import org.keycloak.testsuite.pages.TermsAndConditionsPage;
|
|
||||||
import org.keycloak.testsuite.updaters.UserAttributeUpdater;
|
|
||||||
import org.keycloak.testsuite.util.FlowUtil;
|
|
||||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
|
||||||
import org.keycloak.testsuite.util.UIUtils;
|
|
||||||
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
|
||||||
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
|
||||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
|
||||||
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.openqa.selenium.Cookie;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
import static org.hamcrest.Matchers.not;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
public class LoginPageTest extends AbstractI18NTest {
|
|
||||||
|
|
||||||
@Page
|
|
||||||
protected AppPage appPage;
|
|
||||||
|
|
||||||
@Page
|
|
||||||
protected LoginPage loginPage;
|
|
||||||
|
|
||||||
@Page
|
|
||||||
protected ErrorPage errorPage;
|
|
||||||
|
|
||||||
@Page
|
|
||||||
protected LoginPasswordUpdatePage changePasswordPage;
|
|
||||||
|
|
||||||
@Page
|
|
||||||
protected OAuthGrantPage grantPage;
|
|
||||||
|
|
||||||
@Page
|
|
||||||
protected LoginExpiredPage loginExpiredPage;
|
|
||||||
|
|
||||||
@Page
|
|
||||||
protected TermsAndConditionsPage termsPage;
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public AssertEvents events = new AssertEvents(this);
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
setRealmInternationalization(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
|
||||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
|
||||||
.providerId("github")
|
|
||||||
.alias("github")
|
|
||||||
.build());
|
|
||||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
|
||||||
.providerId("saml")
|
|
||||||
.alias("mysaml")
|
|
||||||
.build());
|
|
||||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
|
||||||
.providerId("oidc")
|
|
||||||
.alias("myoidc")
|
|
||||||
.displayName("MyOIDC")
|
|
||||||
.build());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void languageDropdown() {
|
|
||||||
loginPage.open();
|
|
||||||
assertEquals("English", loginPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
switchLanguageToGermanAndBack("Username or email", "Benutzername oder E-Mail", loginPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void uiLocalesParameter() {
|
|
||||||
oauth.loginForm().open();
|
|
||||||
assertEquals("English", loginPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
//test if cookie works
|
|
||||||
oauth.loginForm().uiLocales("de").open();
|
|
||||||
assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
driver.manage().deleteAllCookies();
|
|
||||||
oauth.loginForm().uiLocales("de").open();
|
|
||||||
assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
driver.manage().deleteAllCookies();
|
|
||||||
oauth.loginForm().uiLocales("en de").open();
|
|
||||||
assertEquals("English", loginPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
driver.manage().deleteAllCookies();
|
|
||||||
oauth.loginForm().uiLocales("fr de").open();
|
|
||||||
assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void htmlLangAttributeWithInternationalizationEnabled() {
|
|
||||||
loginPage.open();
|
|
||||||
assertEquals("en", loginPage.getHtmlLanguage());
|
|
||||||
|
|
||||||
oauth.loginForm().uiLocales("de").open();
|
|
||||||
assertEquals("de", loginPage.getHtmlLanguage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void htmlLangAttributeWithInternationalizationDisabled() {
|
|
||||||
setRealmInternationalization(false);
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
assertEquals("en", loginPage.getHtmlLanguage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void acceptLanguageHeader() throws IOException {
|
|
||||||
try(CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
|
|
||||||
ApacheHttpClient43Engine engine = new ApacheHttpClient43Engine(httpClient);
|
|
||||||
ResteasyClient client = ((ResteasyClientBuilder) ResteasyClientBuilder.newBuilder()).httpEngine(engine).build();
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
|
|
||||||
try(Response responseDe = client.target(driver.getCurrentUrl()).request().acceptLanguage("de").get()) {
|
|
||||||
Assert.assertTrue(responseDe.readEntity(String.class).contains("Anmeldung bei test"));
|
|
||||||
|
|
||||||
try(Response responseEn = client.target(driver.getCurrentUrl()).request().acceptLanguage("en").get()) {
|
|
||||||
Assert.assertTrue(responseEn.readEntity(String.class).contains("Sign in to test"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIdentityProviderCapitalization(){
|
|
||||||
loginPage.open();
|
|
||||||
// contains even name of sub-item - svg element in this case
|
|
||||||
assertThat(loginPage.findSocialButton("github").getText(), is("GitHub"));
|
|
||||||
assertThat(loginPage.findSocialButton("mysaml").getText(), is("mysaml"));
|
|
||||||
assertThat(loginPage.findSocialButton("myoidc").getText(), is("MyOIDC"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
assertEquals("English", changePasswordPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
// Switch language
|
|
||||||
switchLanguageToGermanAndBack("Update password", "Passwort aktualisieren", changePasswordPage);
|
|
||||||
|
|
||||||
// Update password
|
|
||||||
changePasswordPage.changePassword("password", "password");
|
|
||||||
|
|
||||||
assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
|
||||||
Assert.assertNotNull(oauth.parseLoginResponse().getCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// KEYCLOAK-3887
|
|
||||||
@Test
|
|
||||||
public void languageChangeConsentScreen() {
|
|
||||||
// Set client, which requires consent
|
|
||||||
oauth.client("third-party", "password");
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
|
|
||||||
loginPage.login("test-user@localhost", "password");
|
|
||||||
|
|
||||||
grantPage.assertCurrent();
|
|
||||||
assertEquals("English", grantPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
// Switch language
|
|
||||||
switchLanguageToGermanAndBack("Do you grant these access privileges?", "Wollen Sie diese Zugriffsrechte", changePasswordPage);
|
|
||||||
|
|
||||||
// Confirm grant
|
|
||||||
grantPage.accept();
|
|
||||||
|
|
||||||
assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
|
||||||
Assert.assertNotNull(oauth.parseLoginResponse().getCode());
|
|
||||||
|
|
||||||
// Revert client
|
|
||||||
oauth.client("test-app", "password");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void languageUserUpdates() {
|
|
||||||
loginPage.open();
|
|
||||||
loginPage.openLanguage("Deutsch");
|
|
||||||
|
|
||||||
assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
Cookie localeCookie = driver.manage().getCookieNamed(CookieType.LOCALE.getName());
|
|
||||||
assertEquals("de", localeCookie.getValue());
|
|
||||||
|
|
||||||
UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost");
|
|
||||||
String userId = user.toRepresentation().getId();
|
|
||||||
loginPage.login("test-user@localhost", "password");
|
|
||||||
|
|
||||||
events.expect(EventType.UPDATE_PROFILE)
|
|
||||||
.user(userId)
|
|
||||||
.client("test-app")
|
|
||||||
.detail(Details.PREF_UPDATED + UserModel.LOCALE, "de")
|
|
||||||
.assertEvent();
|
|
||||||
events.expectLogin()
|
|
||||||
.user(userId)
|
|
||||||
.client("test-app")
|
|
||||||
.assertEvent();
|
|
||||||
|
|
||||||
UserRepresentation userRep = user.toRepresentation();
|
|
||||||
assertEquals("de", userRep.getAttributes().get("locale").get(0));
|
|
||||||
|
|
||||||
String code = oauth.parseLoginResponse().getCode();
|
|
||||||
String idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
|
|
||||||
appPage.logout(idTokenHint);
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
|
|
||||||
assertEquals("Deutsch", loginPage.getLanguageDropdownText());
|
|
||||||
|
|
||||||
userRep.getAttributes().remove("locale");
|
|
||||||
user.update(userRep);
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
loginPage.login("test-user@localhost", "password");
|
|
||||||
|
|
||||||
// User locale should not be updated due to previous cookie
|
|
||||||
userRep = user.toRepresentation();
|
|
||||||
Assert.assertNull(userRep.getAttributes());
|
|
||||||
|
|
||||||
code = oauth.parseLoginResponse().getCode();
|
|
||||||
idTokenHint = oauth.doAccessTokenRequest(code).getIdToken();
|
|
||||||
appPage.logout(idTokenHint);
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
|
|
||||||
// Cookie should be removed as last user to login didn't have a locale
|
|
||||||
localeCookie = driver.manage().getCookieNamed(CookieType.LOCALE.getName());
|
|
||||||
Assert.assertNull(localeCookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Test for user updating locale on the error page (when authenticationSession is not available)
|
|
||||||
@Test
|
|
||||||
public void languageUserUpdatesOnErrorPage() {
|
|
||||||
// Login page with invalid redirect_uri
|
|
||||||
oauth.redirectUri("http://invalid");
|
|
||||||
loginPage.open();
|
|
||||||
|
|
||||||
errorPage.assertCurrent();
|
|
||||||
Assert.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
|
|
||||||
|
|
||||||
// Change language should be OK
|
|
||||||
errorPage.openLanguage("Deutsch");
|
|
||||||
assertEquals("Deutsch", errorPage.getLanguageDropdownText());
|
|
||||||
Assert.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
|
|
||||||
|
|
||||||
// Refresh browser button should keep german language
|
|
||||||
driver.navigate().refresh();
|
|
||||||
assertEquals("Deutsch", errorPage.getLanguageDropdownText());
|
|
||||||
Assert.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
|
|
||||||
|
|
||||||
// Changing to english should work
|
|
||||||
errorPage.openLanguage("English");
|
|
||||||
assertEquals("English", errorPage.getLanguageDropdownText());
|
|
||||||
Assert.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void languageUserUpdatesOnErrorPageStateCheckerTest() throws URISyntaxException {
|
|
||||||
// Login page with invalid redirect_uri
|
|
||||||
oauth.redirectUri("http://invalid");
|
|
||||||
loginPage.open();
|
|
||||||
|
|
||||||
errorPage.assertCurrent();
|
|
||||||
Assert.assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
|
|
||||||
|
|
||||||
errorPage.openLanguage("Deutsch");
|
|
||||||
Assert.assertEquals("Ungültiger Parameter: redirect_uri", errorPage.getError());
|
|
||||||
|
|
||||||
// Add incorrect state checker parameter. Error page should be shown about expired action. Language won't be changed
|
|
||||||
String currentUrl = driver.getCurrentUrl();
|
|
||||||
String newUrl = KeycloakUriBuilder.fromUri(new URI(currentUrl))
|
|
||||||
.replaceQueryParam(LocaleSelectorProvider.KC_LOCALE_PARAM, "en")
|
|
||||||
.replaceQueryParam(DetachedInfoStateChecker.STATE_CHECKER_PARAM, "invalid").buildAsString();
|
|
||||||
driver.navigate().to(newUrl);
|
|
||||||
|
|
||||||
Assert.assertEquals("Die Aktion ist nicht mehr gültig.", errorPage.getError()); // Action expired.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void languageUserUpdatesOnExpiredPage() throws Exception {
|
|
||||||
try (UserAttributeUpdater userUpdater = UserAttributeUpdater.forUserByUsername(testRealm(), "test-user@localhost")
|
|
||||||
.setRequiredActions(UserModel.RequiredAction.UPDATE_PASSWORD).update()) {
|
|
||||||
// login with a failure attempt
|
|
||||||
loginPage.open();
|
|
||||||
loginPage.login("test-user@localhost", "invalid-password");
|
|
||||||
loginPage.assertCurrent();
|
|
||||||
assertThat(loginPage.getUsernameInputError(), is("Invalid username or password."));
|
|
||||||
loginPage.login("test-user@localhost", "password");
|
|
||||||
changePasswordPage.assertCurrent();
|
|
||||||
|
|
||||||
// navigate back to the login expired page and change language to german
|
|
||||||
UIUtils.navigateBackWithRefresh(driver, loginExpiredPage);
|
|
||||||
errorPage.openLanguage("Deutsch");
|
|
||||||
assertEquals("Deutsch", errorPage.getLanguageDropdownText());
|
|
||||||
assertThat(PageUtils.getPageTitle(driver), is("Diese Seite ist nicht mehr gültig."));
|
|
||||||
|
|
||||||
// continue should show password update in german
|
|
||||||
loginExpiredPage.clickLoginContinueLink();
|
|
||||||
assertEquals("Deutsch", changePasswordPage.getLanguageDropdownText());
|
|
||||||
assertThat(PageUtils.getPageTitle(driver), is("Passwort aktualisieren"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GH issue 41292
|
|
||||||
@Test
|
|
||||||
public void languageUserUpdatesOnCustomAuthenticatorPage() {
|
|
||||||
configureBrowserFlowWithClickThroughAuthenticator();
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
Assert.assertTrue(termsPage.isCurrent());
|
|
||||||
|
|
||||||
// Change language on the custom page
|
|
||||||
switchLanguageToGermanAndBack("Terms and Conditions", "Bedingungen und Konditionen", termsPage);
|
|
||||||
|
|
||||||
// Revert dummy flow
|
|
||||||
RealmRepresentation rep = testRealm().toRepresentation();
|
|
||||||
rep.setBrowserFlow("browser");
|
|
||||||
testRealm().update(rep);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void realmLocalizationMessagesAreApplied() {
|
|
||||||
String realmLocalizationMessageKey = "loginAccountTitle";
|
|
||||||
|
|
||||||
String realmLocalizationMessageValueEn = "Localization Test EN";
|
|
||||||
saveLocalizationText(Locale.ENGLISH.toLanguageTag(), realmLocalizationMessageKey,
|
|
||||||
realmLocalizationMessageValueEn);
|
|
||||||
String realmLocalizationMessageValueDe = "Localization Test DE";
|
|
||||||
saveLocalizationText(Locale.GERMAN.toLanguageTag(), realmLocalizationMessageKey,
|
|
||||||
realmLocalizationMessageValueDe);
|
|
||||||
|
|
||||||
loginPage.open();
|
|
||||||
switchLanguageToGermanAndBack(realmLocalizationMessageValueEn, realmLocalizationMessageValueDe, loginPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// KEYCLOAK-18590
|
|
||||||
@Test
|
|
||||||
public void realmLocalizationMessagesAreNotCachedWithinTheTheme() {
|
|
||||||
final String locale = Locale.ENGLISH.toLanguageTag();
|
|
||||||
|
|
||||||
final String realmLocalizationMessageKey = "loginAccountTitle";
|
|
||||||
final String realmLocalizationMessageValue = "Localization Test";
|
|
||||||
|
|
||||||
saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
|
|
||||||
loginPage.open();
|
|
||||||
assertThat(driver.getPageSource(), containsString(realmLocalizationMessageValue));
|
|
||||||
|
|
||||||
testRealm().localization().deleteRealmLocalizationText(locale, realmLocalizationMessageKey);
|
|
||||||
loginPage.open();
|
|
||||||
assertThat(driver.getPageSource(), not(containsString(realmLocalizationMessageValue)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void realmLocalizationMessagesUsedDuringErrorHandling() {
|
|
||||||
final String locale = Locale.ENGLISH.toLanguageTag();
|
|
||||||
|
|
||||||
final String realmLocalizationMessageKey = "errorTitle";
|
|
||||||
final String realmLocalizationMessageValue = "We are really sorry...";
|
|
||||||
|
|
||||||
saveLocalizationText(locale, realmLocalizationMessageKey, realmLocalizationMessageValue);
|
|
||||||
String nonExistingUrl = oauth.loginForm().build().split("protocol")[0] + "incorrect-path";
|
|
||||||
driver.navigate().to(nonExistingUrl);
|
|
||||||
|
|
||||||
assertThat(driver.getPageSource(), containsString(realmLocalizationMessageValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveLocalizationText(String locale, String key, String value) {
|
|
||||||
testRealm().localization().saveRealmLocalizationText(locale, key, value);
|
|
||||||
getCleanup().addLocalization(locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void switchLanguageToGermanAndBack(String expectedEnglishMessage, String expectedGermanMessage, LanguageComboboxAwarePage page) {
|
|
||||||
// Switch language to Deutsch
|
|
||||||
page.openLanguage("Deutsch");
|
|
||||||
assertEquals("Deutsch", page.getLanguageDropdownText());
|
|
||||||
String pageSource = driver.getPageSource();
|
|
||||||
assertThat(pageSource, not(containsString(expectedEnglishMessage)));
|
|
||||||
assertThat(pageSource, containsString(expectedGermanMessage));
|
|
||||||
|
|
||||||
// Revert language
|
|
||||||
page.openLanguage("English");
|
|
||||||
assertEquals("English", page.getLanguageDropdownText());
|
|
||||||
pageSource = driver.getPageSource();
|
|
||||||
assertThat(pageSource, containsString(expectedEnglishMessage));
|
|
||||||
assertThat(pageSource, not(containsString(expectedGermanMessage)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRealmInternationalization(final boolean enabled) {
|
|
||||||
final var realmResource = testRealm();
|
|
||||||
RealmRepresentation realm = realmResource.toRepresentation();
|
|
||||||
realm.setInternationalizationEnabled(enabled);
|
|
||||||
realmResource.update(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureBrowserFlowWithClickThroughAuthenticator() {
|
|
||||||
final String newFlowAlias = "browser - rule";
|
|
||||||
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
|
|
||||||
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session)
|
|
||||||
.selectFlow(newFlowAlias)
|
|
||||||
.inForms(forms -> forms
|
|
||||||
.clear()
|
|
||||||
// Update the browser forms with a UsernamePasswordForm
|
|
||||||
.addAuthenticatorExecution(AuthenticationExecutionModel.Requirement.REQUIRED, ClickThroughAuthenticator.PROVIDER_ID)
|
|
||||||
)
|
|
||||||
.defineAsBrowserFlow()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -17,7 +17,6 @@ exportimport,4
|
|||||||
feature,4
|
feature,4
|
||||||
federation,5
|
federation,5
|
||||||
forms,5
|
forms,5
|
||||||
i18n,5
|
|
||||||
login,4
|
login,4
|
||||||
migration,4
|
migration,4
|
||||||
model,6
|
model,6
|
||||||
|
|||||||
@ -3,4 +3,3 @@ org.keycloak.testsuite.forms
|
|||||||
KcOidcBrokerLoginHintTest
|
KcOidcBrokerLoginHintTest
|
||||||
KcOidcFirstBrokerLoginTest
|
KcOidcFirstBrokerLoginTest
|
||||||
KcOidcMultipleTabsBrokerTest
|
KcOidcMultipleTabsBrokerTest
|
||||||
LoginPageTest
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user