Remove legacy Account Console tests

Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jon Koops 2024-03-26 17:56:52 +01:00 committed by Hynek Mlnařík
parent 0826a12ca4
commit 0327787645
57 changed files with 26 additions and 4511 deletions

1
.github/CODEOWNERS vendored
View file

@ -47,4 +47,3 @@
/js/ @keycloak/ui-maintainers
/adapters/oidc/js/ @keycloak/ui-maintainers
/rest/admin-ui-ext/ @keycloak/ui-maintainers
/testsuite/integration-arquillian/tests/other/base-ui/ @keycloak/ui-maintainers

View file

@ -352,17 +352,6 @@ mvn -f testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml \
[-Pspringboot27]
```
## Base UI tests
Similarly to Admin Console tests, these tests are focused on UI, specifically on the parts of the server that are accessed by an end user (like Login page, or Account Console).
They are designed to work with mobile browsers (alongside the standard desktop browsers). For details on the supported browsers and their configuration please refer to [Different Browsers chapter](#different-browsers).
#### Execution example
```
mvn -f testsuite/integration-arquillian/tests/other/base-ui/pom.xml \
clean test \
-Pandroid \
-Dappium.avd=Nexus_5X_API_27
```
## Disabling features
Some features in Keycloak can be disabled. To run the testsuite with a specific feature disabled use the `auth.server.feature` system property. For example to run the tests with authorization disabled run:
```
@ -454,23 +443,17 @@ You can use many different real-world browsers to run the integration tests.
Although technically they can be run with almost every test in the testsuite, they can fail with some of them as the tests often require specific optimizations for given browser. Therefore, only some of the test modules have support to be run with specific browsers.
#### Mozilla Firefox
* **Supported test modules:** `console`, `base-ui`
* **Supported test modules:** `console`
* **Supported version:** latest stable
* **Driver download required:** [GeckoDriver](https://github.com/mozilla/geckodriver/releases)
* **Run with:** `-Dbrowser=firefox -Dwebdriver.gecko.driver=path/to/geckodriver`; optionally you can specify `-Dfirefox_binary=path/to/firefox/binary`
#### Google Chrome
* **Supported test modules:** `console`, `base-ui`
* **Supported test modules:** `console`
* **Supported version:** latest stable
* **Driver download required:** [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/) that corresponds with your version of the browser
* **Run with:** `-Dbrowser=chrome -Dwebdriver.chrome.driver=path/to/chromedriver`
#### Apple Safari
* **Supported test modules:** `base-ui`
* **Supported version:** latest stable
* **Driver download required:** no (the driver is bundled with macOS)
* **Run with:** `-Dbrowser=safari`
#### [DEPRECATED] Mozilla Firefox with legacy driver
* **Supported test modules:** `console`
* **Supported version:** [52 ESR](http://ftp.mozilla.org/pub/firefox/releases/52.9.0esr/) ([Extended Support Release](https://www.mozilla.org/en-US/firefox/organizations/))
@ -490,25 +473,11 @@ The tests will try to start the Appium server automatically but you can do it ma
To use a mobile browser you need to create a virtual device. The most convenient way to do so is to install the desired platform's IDE - either [Android Studio](https://developer.android.com/studio/) (for Android devices) or [Xcode](https://developer.apple.com/xcode/) (for iOS devices) - then you can create a device (smartphone/tablet) there. For details please refer to documentation of those IDEs.
#### Google Chrome on Android
* **Supported test modules:** `base-ui`
* **Supported host OS:** Windows, Linux, macOS
* **Supported browser version:** latest stable
* **Supported mobile OS version:** Android 7.x, 8.x
* **Run with:** `mvn clean test -Pandroid -Dappium.avd=name_of_the_AVD` where AVD is the name of your Android Virtual Device (e.g. `Nexus_5X_API_27`)
**Tips & tricks:**
* If the AVD name contains any spaces, you need to replace them with underscores when specifying the `-Dappium.avd=...`.
* It's probable that a freshly created device will contain an outdated Chrome version. To update to the latest version (without using the Play Store) you need to download an `.apk` for Chrome and install it with `adb install -r path/to/chrome.apk`.
* Chrome on Android uses ChromeDriver similarly to regular desktop Chrome. The ChromeDriver is bundled with the Appium server. To use a newer ChromeDriver please follow the [Appium documentation](http://appium.io/docs/en/writing-running-appium/web/chromedriver/).
#### Apple Safari on iOS
* **Supported test modules:** `base-ui`
* **Supported host OS:** macOS
* **Supported browser version:** _depends on the mobile OS version_
* **Supported mobile OS version:** iOS 11.x
* **Run with:** `mvn clean test -Pios -Dappium.deviceName=device_name` where the device name is your device identification (e.g. `iPhone X`)
## Disabling TLS (SSL) in the tests
All tests are executed with TLS by default. In order to disable it, you need to switch the `auth.server.ssl.required` property off.

View file

@ -110,15 +110,6 @@ UI testing is sometimes very tricky due to different demands and behaviours of d
The base testsuite contains custom Arquillian extensions and most functional tests.
The other test modules depend on this module.
### Base UI Testsuite
Contains most of the UI-focused tests that don't cover Admin Console, i.e. all the parts of the server that are intended to be accessed by an end user.
The tests placed here are exclusively covering the UI functionality of the server, i.e. checking if all the page elements are visible, links clickable etc., and are focused on simplicity and stability.
This differs them from other integration tests and Admin Console UI tests.
They are designed to work with most of the desktop browsers (HtmlUnit included) as well as mobile browsers (Chrome on Android and Safari on iOS). Please see [HOW-TO-RUN.md](HOW-TO-RUN.md) for details on supported browsers.
The tests are place in a separate module (`tests/other/base-ui`) and are disabled by default.
#### Types of adapter tests
1. Using *custom test servlets*

View file

@ -1,184 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2018 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>integration-arquillian-tests-other</artifactId>
<groupId>org.keycloak.testsuite</groupId>
<version>999.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>integration-arquillian-tests-base-ui</artifactId>
<name>Base UI TestSuite</name>
<properties>
<droneInstantiationTimeoutInSeconds>600</droneInstantiationTimeoutInSeconds>
<keycloak.theme.dir>${auth.server.home}/themes</keycloak.theme.dir>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>${testsuite.constants}</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-theme-files</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${keycloak.theme.dir}</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/themes</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<keycloak.theme.dir>${keycloak.theme.dir}</keycloak.theme.dir>
</systemProperties>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-appium-extension</artifactId>
<version>${arquillian-drone.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>android</id>
<properties>
<browser>appium</browser>
<appium.platformName>android</appium.platformName>
<appium.browserName>chrome</appium.browserName>
<appium.deviceName>doesn't matter</appium.deviceName> <!-- ignored but required by AndroidDriver -->
<appium.automationName>Appium</appium.automationName>
<auth.server.browserHost>10.0.2.2</auth.server.browserHost>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireProperty>
<property>appium.avd</property>
<regex>\S+.*</regex>
</requireProperty>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>ios</id>
<properties>
<browser>appium</browser>
<appium.platformName>ios</appium.platformName>
<appium.browserName>safari</appium.browserName>
<appium.automationName>XCUITest</appium.automationName>
<appium.noReset>true</appium.noReset>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireProperty>
<property>appium.deviceName</property>
<regex>\S+.*</regex>
</requireProperty>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View file

@ -1,107 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.ArrayList;
import java.util.List;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ApplicationsPage extends AbstractLoggedInPage {
@FindBy(xpath = "//li[starts-with(@id,'application-client-id')]")
private List<WebElement> applications;
@Override
public String getPageId() {
return "applications";
}
public void toggleApplicationDetails(String clientId) {
By selector = By.xpath("//button[@id='application-toggle-" + clientId + "']");
waitUntilElement(selector).is().clickable();
driver.findElement(selector).click();
}
public List<ClientRepresentation> getApplications() {
ArrayList<ClientRepresentation> apps = new ArrayList<>();
for(WebElement app : applications) {
String clientId = app.getAttribute("id").replace("application-client-id-", "");
apps.add(toRepresentation(app, clientId));
}
return apps;
}
private ClientRepresentation toRepresentation(WebElement app, String clientId) {
String clientName = UIUtils.getTextFromElement(app.findElement(By.xpath("//div[@id='application-name-" + clientId + "']")));
boolean userConsentRequired = !UIUtils.getTextFromElement(app.findElement(By.xpath("//div[@id='application-internal-" + clientId + "']"))).equals("Internal");
boolean inUse = UIUtils.getTextFromElement(app.findElement(By.xpath("//div[@id='application-status-" + clientId + "']"))).equals("In use");
boolean applicationDetailsVisible = app.findElement(By.xpath("//section[@id='application-expandable-" + clientId + "']")).isDisplayed();
String effectiveURL = UIUtils.getTextFromElement(app.findElement(By.id("application-effectiveurl-" + clientId)));
return new ClientRepresentation(clientId, clientName, userConsentRequired, inUse, effectiveURL, applicationDetailsVisible);
}
public class ClientRepresentation {
private final String clientId;
private final String clientName;
private final boolean userConsentRequired;
private final boolean inUse;
private final String effectiveUrl;
private final boolean applicationDetailsVisible;
public ClientRepresentation(String clientId, String clientName, boolean userConsentRequired, boolean inUse, String effectiveUrl, boolean applicationDetailsVisible) {
this.clientId = clientId;
this.clientName = clientName;
this.userConsentRequired = userConsentRequired;
this.inUse = inUse;
this.effectiveUrl = effectiveUrl;
this.applicationDetailsVisible = applicationDetailsVisible;
}
public String getClientId() {
return clientId;
}
public String getClientName() {
return clientName;
}
public boolean isUserConsentRequired() {
return userConsentRequired;
}
public boolean isInUse() {
return inUse;
}
public String getEffectiveUrl() {
return effectiveUrl;
}
public boolean isApplicationDetailsVisible() {
return applicationDetailsVisible;
}
}
}

View file

@ -1,215 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class DeviceActivityPage extends AbstractLoggedInPage {
@FindBy(id = "sign-out-all")
private WebElement signOutAllBtn;
@FindBy(className = "signed-in-device-grid")
private List<WebElement> sessions;
@Override
public String getPageId() {
return "device-activity";
}
@Override
public String getParentPageId() {
return ACCOUNT_SECURITY_ID;
}
public boolean isSignOutAllDisplayed() {
return isElementVisible(signOutAllBtn);
}
public void clickSignOutAll() {
clickLink(signOutAllBtn);
}
public int getSessionsCount() {
return sessions.size();
}
public Optional<Session> getSessionByIndex(int index) {
try {
return Optional.of(new Session(sessions.get(index)));
} catch (Exception e) {
log.warn(e.getMessage());
return Optional.empty();
}
}
public Optional<Session> getSession(String sessionId) {
try {
return Optional.of(new Session(getSessionElement(sessionId)));
} catch (Exception e) {
log.warn(e.getMessage());
return Optional.empty();
}
}
private WebElement getSessionElement(String sessionId) {
return sessions.stream()
.filter(f -> getTrimmedSessionId(sessionId).equals(getSessionId(f)))
.findFirst()
.orElse(null);
}
private static String getSessionId(WebElement sessionElement) {
if (sessionElement == null) return null;
return sessionElement.getAttribute("id").split("-")[1]; // the id looks like session-71891504-item
}
public static String getTrimmedSessionId(String fullSessionId) {
return fullSessionId.substring(0, 7);
}
// We cannot use standard Page Fragment as there's no root element. Even though the sessions are placed in rows,
// there's no element that would encapsulate it. Hence we cannot simply use e.g. @FindBy annotations.
public class Session {
private static final String SESSION = "session";
private static final String DEVICE_ICON = "device-icon";
private static final String IP = "ip";
private static final String SIGN_OUT = "sign-out";
private final WebElement element;
private final String sessionId;
// we don't want Session to be instantiated outside DeviceActivityPage
private Session(WebElement element) {
this.element = element;
this.sessionId = DeviceActivityPage.getSessionId(element);
}
public String getSessionId() {
return sessionId;
}
public boolean isPresent() {
return isItemDisplayed(IP); // no root element hence this workaround
}
public String getIcon() {
final WebElement icon = (WebElement) Optional.ofNullable(element.findElement(By.className(DEVICE_ICON)))
.map(f -> (WebElement) f)
.map(f -> f.findElement(By.tagName("svg")))
.orElse(null);
if (icon == null) return "";
return icon.getAttribute("id").split("-")[3]; // the id looks like session-71891504-icon-desktop
}
public String getIp() {
return getTextFromItem(IP);
}
public boolean hasCurrentBadge() {
return isItemDisplayed("current-badge");
}
public boolean isBrowserDisplayed() {
return !"".equals(getBrowser());
}
public String getTitle() {
return getTextFromElement(element.findElement(By.className("session-title")));
}
public String getBrowser() {
try {
return getTitle().split("/", 2)[1].trim();
} catch (Exception e) {
return "";
}
}
public String getLastAccess() {
return getTextFromItem("last-access");
}
public String getClients() {
return getTextFromItem("clients");
}
public String getStarted() {
return getTextFromItem("started");
}
public String getExpires() {
return getTextFromItem("expires");
}
public boolean isSignOutDisplayed() {
return getSignOutButton() != null;
}
public void clickSignOut() {
WebElement signOutButton = getSignOutButton();
if (signOutButton != null) {
clickLink(signOutButton);
} else {
log.warn("Cannot click sign out button; not present");
}
}
private WebElement getSignOutButton() {
try {
return driver.findElement(By.xpath(String.format("//button[@id='%s']", getFullItemId(SIGN_OUT))));
} catch (NoSuchElementException e) {
return null;
}
}
private String getFullItemId(String itemId) {
return String.format("%s-%s-%s", SESSION, sessionId, itemId);
}
private WebElement getItemElement(String itemId) {
return element.findElement(By.id(getFullItemId(itemId)));
}
private String getTextFromItem(String itemId) {
return getTextFromElement(getItemElement(itemId).findElement(By.tagName("div")));
}
private boolean isItemDisplayed(String itemId) {
try {
return getItemElement(itemId).isDisplayed();
} catch (NoSuchElementException e) {
return false;
}
}
}
}

View file

@ -1,39 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ForbiddenPage extends AbstractLoggedInPage {
@FindBy(tagName = "main")
private WebElement mainTag;
@Override
public String getPageId() {
return "forbidden";
}
@Override
public boolean isCurrent() {
return mainTag.getText().contains("You do not have access rights to this request.");
}
}

View file

@ -1,142 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page;
import org.jboss.arquillian.graphene.Graphene;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
import static org.openqa.selenium.By.id;
import static org.openqa.selenium.By.xpath;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class LinkedAccountsPage extends AbstractLoggedInPage {
public static final String LINKED_ACCOUNTS_ID = "linked-accounts";
public static final String LINKED_IDPS_LIST_ID = "linked-idps";
public static final String UNLINKED_IDPS_LIST_ID = "unlinked-idps";
@FindBy(id = LINKED_IDPS_LIST_ID)
private List<WebElement> linkedIdPsList;
@FindBy(id = UNLINKED_IDPS_LIST_ID)
private List<WebElement> unlinkedIdPsList;
@Override
public String getPageId() {
return LINKED_ACCOUNTS_ID;
}
@Override
public String getParentPageId() {
return ACCOUNT_SECURITY_ID;
}
public IdentityProvider getProvider(String providerAlias) {
WebElement root = driver.findElement(id(providerAlias + "-idp"));
return Graphene.createPageFragment(IdentityProvider.class, root);
}
public int getLinkedProvidersCount() {
return linkedIdPsList.size();
}
public int getUnlinkedProvidersCount() {
return unlinkedIdPsList.size();
}
public class IdentityProvider {
@Root
private WebElement root;
@FindBy(xpath = ".//*[contains(@id,'idp-name')]")
private WebElement nameElement;
@FindBy(xpath = ".//*[contains(@id,'idp-icon')]")
private WebElement iconElement;
@FindBy(xpath = ".//*[contains(@id,'idp-label')]")
private WebElement badgeElement;
@FindBy(xpath = ".//*[contains(@id,'idp-username')]")
private WebElement usernameElement;
@FindBy(xpath = ".//*[contains(@id,'idp-link')]")
private WebElement linkBtn;
@FindBy(xpath = ".//*[contains(@id,'idp-unlink')]")
private WebElement unlinkBtn;
public boolean isLinked() {
String parentListId = root.findElement(xpath("ancestor::ul")).getAttribute("id");
if (parentListId.equals(LINKED_IDPS_LIST_ID)) {
return true;
}
else if (parentListId.equals(UNLINKED_IDPS_LIST_ID)) {
return false;
}
else {
throw new IllegalStateException("Unexpected parent list ID: " + parentListId);
}
}
public boolean hasSocialLoginBadge() {
return getTextFromElement(badgeElement).equals("Social login");
}
public boolean hasSystemDefinedBadge() {
return getTextFromElement(badgeElement).equals("System defined");
}
public boolean hasSocialIcon() {
return iconElement.getAttribute("id").contains("social");
}
public boolean hasDefaultIcon() {
return iconElement.getAttribute("id").contains("default");
}
public String getUsername() {
return getTextFromElement(usernameElement);
}
public boolean isLinkBtnVisible() {
return isElementVisible(linkBtn);
}
public boolean isUnlinkBtnVisible() {
return isElementVisible(unlinkBtn);
}
public void clickLinkBtn() {
clickLink(linkBtn);
}
public void clickUnlinkBtn() {
clickLink(unlinkBtn);
}
}
}

View file

@ -1,182 +0,0 @@
package org.keycloak.testsuite.ui.account3.page;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.jboss.arquillian.graphene.Graphene.waitGui;
import static org.junit.Assert.assertEquals;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
public class MyResourcesPage extends AbstractLoggedInPage {
@FindBy(xpath = "//ul[@id='resourcesList']/li")
private List<WebElement> resourcesList;
@FindBy(id = "refresh-page")
private WebElement refreshButton;
@Override
public String getPageId() {
return "resources";
}
public int getResourcesListCount() {
return resourcesList.size();
}
public void clickRefreshButton() {
clickLink(refreshButton);
}
public void clickExpandButton(int row) {
driver.findElement(By.id("resourceToggle-" + row)).click();
waitGui().until().element(By.id("ex-expand" + row)).is().visible();
}
public void clickCollapseButton(int row) {
driver.findElement(By.id("resourceToggle-" + row)).click();
waitGui().until().element(By.id("ex-expand" + row)).is().not().visible();
}
public String getCellText(String cell, int row) {
return getCell(cell, row).getText();
}
public String getCellHref(String cell, int row) {
return getCell(cell, row).findElement(By.tagName("a")).getAttribute("href");
}
private WebElement getCell(String cell, int row) {
final String name = Character.toUpperCase(cell.charAt(0)) + cell.substring(1);
return driver.findElement(By.id(String.format("resource%s-%d", name, row)));
}
public String getSharedWith(int row) {
final WebElement element = driver.findElement(By.id("shared-with-user-message-" + row));
return element.getText();
}
public void clickShareButton(int row) {
driver.findElement(By.id("share-" + row)).click();
waitForModalFadeIn();
}
public void clickEditButton(int row) {
final WebElement webElement = driver.findElement(By.id("action-menu-" + row));
webElement.click();
webElement.findElement(By.id("edit-" + row)).click();
waitForModalFadeIn();
}
public String getEditDialogUsername(int row) {
return driver.findElement(By.id("username-" + row)).getAttribute("value");
}
public void clickRemoveButton(int row) {
final WebElement webElement = driver.findElement(By.id("action-menu-" + row));
webElement.click();
webElement.findElement(By.id("remove-" + row)).click();
}
public String getPendingRequestRequestor(int row) {
return driver.findElement(By.id("requestor" + row)).getText();
}
public String getPendingRequestPermissions(int row) {
return driver.findElement(By.id("permissions" + row)).getText();
}
private WebElement getPendingRequest(String resourceName) {
return driver.findElement(By.id("shareRequest-" + resourceNameToId(resourceName)));
}
public String getPendingRequestText(String resourceName) {
return getPendingRequest(resourceName).getText();
}
public void clickPendingRequest(String resourceName) {
getPendingRequest(resourceName).click();
}
public void acceptRequest(String resourceName, int row) {
clickApproveDenyButton(resourceName, row, true);
}
public void denyRequest(String resourceName, int row) {
clickApproveDenyButton(resourceName, row, false);
}
private void clickApproveDenyButton(String resourceName, int row, boolean approve) {
final By id = By.id(String.format("%s-%d-shareRequest-%s", approve ? "accept" : "deny", row, resourceNameToId(resourceName)));
driver.findElement(id).click();
waitForModalFadeOut();
}
private String resourceNameToId(String resourceName) {
return resourceName.replace(" ", "-");
}
public void clickSignOut() {
driver.findElement(By.id("signOutButton")).click();
}
public void clickNextPage() {
final WebElement webElement = driver.findElements(By.className("pf-m-primary")).get(1);
assertEquals("Next>", webElement.getText());
webElement.click();
}
public void clickSharedWithMeTab() {
final WebElement sharedWithMe = driver.findElement(By.id("pf-tab-1-sharedwithMe"));
sharedWithMe.click();
final WebElement tab = sharedWithMe.findElement(By.xpath("./.."));
//test to see that the tab is really clicked
assertEquals("pf-v5-c-tabs__item pf-m-current", tab.getAttribute("class"));
}
public boolean containsResource(String resourceName) {
return driver.findElement(By.id("sharedResourcesList")).getText().contains(resourceName);
}
public void createShare(String userName) {
driver.findElement(By.id("username")).sendKeys(userName);
driver.findElement(By.id("add")).click();
driver.findElement(By.className("pf-v5-c-select__toggle-typeahead")).click();
driver.findElement(By.xpath("//button[@class='pf-v5-c-select__menu-item' and text()='Scope A']")).click();
driver.findElement(By.id("done")).click();
waitForModalFadeOut();
}
public void removeAllPermissions() {
assertThat(getScopesTexts(), containsInAnyOrder("Scope A", "Scope B"));
driver.findElement(By.className("pf-v5-c-select__toggle-clear")).click();
driver.findElement(By.id("save-0")).click();
driver.findElement(By.id("done")).click();
waitForModalFadeOut();
}
private List<String> getScopesTexts() {
return driver.findElements(By.xpath("//span[contains(@id,'pf-random-id-')]"))
.stream()
.filter(Objects::nonNull)
.map(UIUtils::getTextFromElement)
.collect(Collectors.toList());
}
private void waitForModalFadeIn() {
waitGui().until().element(By.className("pf-v5-c-modal-box")).is().present();
}
private void waitForModalFadeOut() {
waitGui().until().element(By.className("pf-v5-c-backdrop")).is().not().present();
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class PageNotFound extends AbstractLoggedInPage {
@Override
public String getPageId() {
return "some-nonexisting-id";
}
@Override
public boolean isCurrent() {
return driver.getPageSource().contains("Page not found");
}
}

View file

@ -1,195 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.keycloak.testsuite.util.UIAssert.assertElementDisabled;
import static org.keycloak.testsuite.util.UIAssert.assertInputElementValid;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextInputValue;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
import org.keycloak.representations.idm.UserRepresentation;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class PersonalInfoPage extends AbstractLoggedInPage {
@FindBy(id = "user-name")
private WebElement username;
@FindBy(id = "email-address")
private WebElement email;
@FindBy(id = "first-name")
private WebElement firstName;
@FindBy(id = "last-name")
private WebElement lastName;
@FindBy(id = "locale-select")
private Select localeSelector;
@FindBy(id = "save-btn")
private WebElement saveBtn;
@FindBy(id = "cancel-btn")
private WebElement cancelBtn;
@FindBy(id = "delete-account")
private WebElement deleteAccountSection;
@FindBy(id = "update-email-btn")
private WebElement updateEmailLink;
@Override
public String getPageId() {
return "/";
}
public void assertUsernameDisabled(boolean expected) {
assertEquals(isUsernameDisabled(), expected);
}
public boolean isUsernameDisabled() {
return isElementDisabled(username);
}
public boolean isEmailDisabled() {
return isElementDisabled(email);
}
private boolean isElementDisabled(WebElement element) {
return element.getAttribute("readonly") != null || element.getAttribute("disabled") != null;
}
public String getUsername() {
return getTextInputValue(username);
}
public void setUsername(String value) {
setTextInputValue(username, value);
}
public void assertUsernameValid(boolean expected) {
assertInputElementValid(expected, username);
}
public String getEmail() {
return getTextInputValue(email);
}
public void setEmail(String value) {
setTextInputValue(email, value);
}
public void assertEmailValid(boolean expected) {
assertInputElementValid(expected, email);
}
public void assertUpdateEmailLinkVisible(boolean expected){
if (updateEmailLink == null) {
assertFalse(expected);
return;
}
assertEquals(expected, isElementVisible(updateEmailLink));
}
public void clickUpdateEmailLink(){
clickLink(updateEmailLink);
}
public String getFirstName() {
return getTextInputValue(firstName);
}
public void setFirstName(String value) {
setTextInputValue(firstName, value);
}
public void assertFirstNameValid(boolean expected) {
assertInputElementValid(expected, firstName);
}
public String getLastName() {
return getTextInputValue(lastName);
}
public void setLastName(String value) {
setTextInputValue(lastName, value);
}
public void assertLastNameValid(boolean expected) {
assertInputElementValid(expected, lastName);
}
public void assertSaveDisabled(boolean expected) {
assertElementDisabled(expected, saveBtn);
}
public void assertDeleteAccountSectionVisible(boolean expected) {
if (deleteAccountSection == null) {
assertFalse(expected);
return;
}
assertEquals(expected, isElementVisible(deleteAccountSection));
}
public void clickSave() {
clickSave(true);
}
public void clickSave(boolean assertAlert) {
saveBtn.click();
if (assertAlert) {
alert().assertIsDisplayed();
}
}
public void clickCancel() {
cancelBtn.click();
}
public void clickOpenDeleteExapandable() {
clickLink(driver.findElement(By.cssSelector(".pf-c-expandable-section__toggle")));
}
public void clickDeleteAccountButton() {
clickLink(driver.findElement(By.id("delete-account-btn")));
}
public void setValues(UserRepresentation user, boolean includeUsername) {
if (includeUsername) {
setUsername(user.getUsername());
}
if (!isEmailDisabled()) {
setEmail(user.getEmail());
}
setFirstName(user.getFirstName());
setLastName(user.getLastName());
}
public boolean valuesEqual(UserRepresentation user) {
return user.getUsername().equals(getUsername())
&& user.getEmail().equals(getEmail())
&& user.getFirstName().equals(getFirstName())
&& user.getLastName().equals(getLastName());
}
public void selectLocale(String customLocale) {
localeSelector.selectByValue(customLocale);
}
}

View file

@ -1,141 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page;
import org.keycloak.testsuite.ui.account3.page.fragment.WelcomeScreenHeader;
import org.keycloak.testsuite.util.URLUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import jakarta.ws.rs.core.UriBuilder;
import static org.keycloak.testsuite.util.UIAssert.assertElementVisible;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
import static org.keycloak.testsuite.util.UIUtils.isElementVisible;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class WelcomeScreen extends AbstractAccountPage {
public static final String ROOT_ELEMENT_ID = "welcomeScreen";
@FindBy(id = ROOT_ELEMENT_ID)
private WebElement welcomeScreenRoot;
@FindBy(xpath = "//*[@id='" + ROOT_ELEMENT_ID + "']//header")
private WelcomeScreenHeader header;
@FindBy(xpath = "//a[@id='landing-personal-info']")
private WebElement personalInfoLink;
@FindBy(xpath = "//*[@id='landingChangePasswordLink']/a")
private WebElement changePasswordLink;
@FindBy(xpath = "//a[@id='landing-authenticator']")
private WebElement authenticatorLink;
@FindBy(xpath = "//*[@id='landing-device-activity']/a")
private WebElement deviceActivityLink;
@FindBy(xpath = "//*[@id='landing-linked-accounts']/a")
private WebElement linkedAccountsLink;
@FindBy(xpath = "//a[@id='landing-applications']")
private WebElement applicationsLink;
@FindBy(id = "landing-resources")
private WebElement myResourcesCard;
@FindBy(xpath = "//a[@id='landing-resources']")
private WebElement myResourcesLink;
@FindBy(id = "landingLogo")
private WebElement logoLink;
@FindBy(id = "landingWelcomeMessage")
private WebElement welcomeMessage; // used only for i18n testing
private String referrer;
private String referrerUri;
@Override
public boolean isCurrent() {
return URLUtils.currentUrlEquals(toString() + "#/") && isElementVisible(welcomeScreenRoot); // the hash will be eventually added after the page is loaded
}
@Override
public UriBuilder getUriBuilder() {
UriBuilder uriBuilder = super.getUriBuilder();
if (referrer != null) {
uriBuilder.queryParam("referrer", referrer);
}
if (referrerUri != null) {
uriBuilder.queryParam("referrer_uri", referrerUri);
}
return uriBuilder;
}
public WelcomeScreenHeader header() {
return header;
}
public void clickLogoImage() {
clickLink(logoLink);
}
public void clickPersonalInfoLink() {
clickLink(personalInfoLink);
}
public void clickChangePasswordLink() {
clickLink(changePasswordLink);
}
public void clickAuthenticatorLink() {
clickLink(authenticatorLink);
}
public void clickDeviceActivityLink() {
clickLink(deviceActivityLink);
}
public void assertLinkedAccountsLinkVisible(boolean expected) {
assertElementVisible(expected, linkedAccountsLink);
}
public void clickLinkedAccountsLink() {
clickLink(linkedAccountsLink);
}
public void clickApplicationsLink() {
clickLink(applicationsLink);
}
public void assertMyResourcesCardVisible(boolean expected) {
assertElementVisible(expected, myResourcesCard);
}
public void clickMyResourcesLink() {
clickLink(myResourcesLink);
}
public String getWelcomeMessage() {
return getTextFromElement(welcomeMessage);
}
public void navigateTo(String referrer, String referrerUri) {
this.referrer = referrer;
this.referrerUri = referrerUri;
navigateTo();
this.referrer = null;
this.referrerUri = null;
}
}

View file

@ -1,87 +0,0 @@
/*
* Copyright 2019 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.ui.account3.page.fragment;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class WelcomeScreenHeader extends AbstractHeader {
@FindBy(id = "landingSignOutButton")
private WebElement logoutBtn;
@FindBy(id = "landingSignOutLink")
private WebElement logoutBtnMobile;
@FindBy(id = "landingSignInButton")
private WebElement loginBtn;
@FindBy(id = "landingSignInLink")
private WebElement loginBtnMobile;
@FindBy(xpath = "//*[@id='landing-locale-dropdown']/button")
private WebElement localeBtn;
@FindBy(id = "landing-mobile-local-toggle")
private WebElement localeBtnMobile;
@FindBy(id = "landingReferrerLink")
private WebElement referrerLink;
@FindBy(id = "landingMobileReferrerLink")
private WebElement referrerLinkMobile;
@FindBy(id = "landingMobileDropdown")
private WebElement mobileKebab;
@FindBy(id = "landingLoggedInUser")
private WebElement toolbarLoggedInUser;
@Override
public void clickOptions() {
clickLink(mobileKebab);
}
public void clickLoginBtn() {
clickToolsBtn(isMobileLayout() ? loginBtnMobile : loginBtn);
}
public void assertLoginBtnVisible(boolean expected) {
assertToolsBtnVisible(expected, isMobileLayout() ? loginBtnMobile : loginBtn);
}
@Override
protected WebElement getLogoutBtn() {
return isMobileLayout() ? logoutBtnMobile : logoutBtn;
}
@Override
protected WebElement getReferrerLink() {
return isMobileLayout() ? referrerLinkMobile : referrerLink;
}
@Override
protected String getLocaleElementIdPrefix() {
return "landing-" + super.getLocaleElementIdPrefix();
}
public String getToolbarLoggedInUser() {
return getTextFromElement(toolbarLoggedInUser);
}
}

View file

@ -1,2 +0,0 @@
locale_test=Přísný jazyk
client_localized-client=Přespříliš lokalizovaný klient

View file

@ -1,3 +0,0 @@
locale_test=Přísný jazyk
accountManagementWelcomeMessage=Vítejte v Keycloaku
personalInfoHtmlTitle=Osobní údaje

View file

@ -1,2 +0,0 @@
parent=${theme-default-name}.v2
locales=en,de,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -1,2 +0,0 @@
locale_test=Přísný jazyk
client_localized-client=Přespříliš lokalizovaný klient

View file

@ -1,3 +0,0 @@
locale_test=Přísný jazyk
accountManagementWelcomeMessage=Vítejte v Keycloaku
personalInfoHtmlTitle=Osobní údaje

View file

@ -1,2 +0,0 @@
parent=${theme-default-name}
locales=en,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -1,2 +0,0 @@
parent=${theme-default-name}
locales=en,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -1,11 +0,0 @@
locale_test=Přísný jazyk
termsText=[TEST LOCALE] souhlas s podmínkami
notMatchPasswordMessage=[TEST LOCALE] hesla se neshodují
firstName=[TEST LOCALE] křestní jméno
updateProfileMessage=[TEST LOCALE] aktualizovat profil
verifyEmailMessage=[TEST LOCALE] je třeba ověřit emailovou adresu
invalidTotpMessage=[TEST LOCALE] vložen chybný kód
oauthGrantTitle=[TEST LOCALE] Udělit přístup {0}
rememberMe=[TEST LOCALE] Zapamatuj si mě
invalidUserMessage=[TEST LOCALE] Chybné jméno nebo heslo
emailForgotTitle=[TEST LOCALE] Zapomenuté heslo

View file

@ -1,2 +0,0 @@
parent=${theme-default-name}
locales=en,de,lang01,lang02,lang03,lang04,lang05,test,lang06,lang07,lang08,lang09,lang10

View file

@ -1,84 +0,0 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui;
import org.junit.Before;
import org.junit.BeforeClass;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractAuthTest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.stream.Collectors;
import static org.junit.Assume.assumeFalse;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractUiTest extends AbstractAuthTest {
public static final String LOCALIZED_THEME = "localized-theme";
public static final String LOCALIZED_THEME_PREVIEW = "localized-theme-preview";
public static final String CUSTOM_LOCALE = "test";
public static final String CUSTOM_LOCALE_NAME = "Přísný jazyk";
public static final String DEFAULT_LOCALE="en";
public static final String DEFAULT_LOCALE_NAME = "English";
public static final String LOCALE_CLIENT_NAME = "${client_localized-client}";
public static final String LOCALE_CLIENT_NAME_LOCALIZED = "Přespříliš lokalizovaný klient";
@BeforeClass
public static void assumeSupportedBrowser() {
assumeFalse("Browser must not be htmlunit", System.getProperty("browser").equals("htmlUnit"));
assumeFalse("Browser must not be PhantomJS", System.getProperty("browser").equals("phantomjs"));
}
@Before
public void addTestUser() {
createTestUserWithAdminClient(false);
}
protected boolean isAccountPreviewTheme() {
return false;
}
protected void configureInternationalizationForRealm(RealmRepresentation realm) {
final String localizedTheme = isAccountPreviewTheme() ? LOCALIZED_THEME_PREVIEW : LOCALIZED_THEME;
// fetch the supported locales for the special test theme that includes some fake test locales
Set<String> supportedLocales = adminClient.serverInfo().getInfo().getThemes().get("login").stream()
.filter(x -> x.getName().equals(LOCALIZED_THEME))
.flatMap(x -> Arrays.stream(x.getLocales()))
.collect(Collectors.toSet());
realm.setInternationalizationEnabled(true);
realm.setSupportedLocales(supportedLocales);
realm.setLoginTheme(LOCALIZED_THEME);
realm.setAccountTheme(localizedTheme);
realm.setEmailTheme(LOCALIZED_THEME);
}
protected IdentityProviderRepresentation createIdentityProviderRepresentation(String alias, String providerId) {
IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation();
idpRep.setProviderId(providerId);
idpRep.setAlias(alias);
idpRep.setConfig(new HashMap<>());
return idpRep;
}
}

View file

@ -1,73 +0,0 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.keycloak.common.Profile;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.ui.AbstractUiTest;
import org.keycloak.testsuite.ui.account3.page.PageNotFound;
import org.keycloak.testsuite.ui.account3.page.WelcomeScreen;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class AbstractAccountTest extends AbstractUiTest {
public static final String ACCOUNT_THEME_NAME_KC = "keycloak.v2";
public static final DateTimeFormatter DEFAULT_TIME_FORMATTER = DateTimeFormatter.ofPattern("MMMM d, yyyy 'at' h:mm a", Locale.ENGLISH);
@Page
protected WelcomeScreen accountWelcomeScreen;
@Page
protected PageNotFound pageNotFound;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation testRealmRep = testRealms.get(0);
testRealmRep.setAccountTheme(getAccountThemeName());
}
@Before
public void navigateBeforeTest() {
accountWelcomeScreen.navigateTo();
}
@Override
protected boolean isAccountPreviewTheme() {
return true;
}
protected void loginToAccount() {
assertCurrentUrlStartsWithLoginUrlOf(accountWelcomeScreen);
loginPage.form().login(testUser);
}
protected String getAccountThemeName() {
return ACCOUNT_THEME_NAME_KC;
}
}

View file

@ -1,121 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.ApplicationsPage;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.keycloak.testsuite.util.OAuthClient.APP_ROOT;
import static org.hamcrest.Matchers.containsInAnyOrder;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ApplicationsTest extends BaseAccountPageTest {
@Page
private ApplicationsPage applicationsPage;
@Override
protected AbstractLoggedInPage getAccountPage() {
return applicationsPage;
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation realm = testRealms.get(0);
realm.setClients(Arrays.asList(
ClientBuilder
.create()
.clientId("always-display-client")
.id(KeycloakModelUtils.generateId())
.name("Always Display Client")
.baseUrl(APP_ROOT + "/always-display-client")
.directAccessGrants()
.secret("secret1")
.alwaysDisplayInConsole(true)
.build(),
ClientBuilder
.create()
.clientId("third-party-client")
.id(KeycloakModelUtils.generateId())
.name("Third Party Client")
.baseUrl(APP_ROOT + "/third-party-client")
.directAccessGrants()
.secret("secret1")
.consentRequired(true)
.build()
));
}
@Test
public void applicationListTest() throws Exception {
List<ApplicationsPage.ClientRepresentation> applications = applicationsPage.getApplications();
assertFalse(applications.isEmpty());
Map<String, ApplicationsPage.ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
assertThat(apps.keySet(), containsInAnyOrder("always-display-client", "account-console"));
assertClientRep(apps.get("account-console"), "Account Console", false, true, getAuthServerRoot() + "realms/test/account/", false);
assertClientRep(apps.get("always-display-client"), "Always Display Client", false, false, getAuthServerRoot() + "realms/master/app/always-display-client", false);
}
@Test
public void toggleApplicationDetailsTest() throws Exception {
applicationsPage.toggleApplicationDetails("account-console");
List<ApplicationsPage.ClientRepresentation> applications = applicationsPage.getApplications();
assertFalse(applications.isEmpty());
Map<String, ApplicationsPage.ClientRepresentation> apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
assertThat(apps.keySet(), containsInAnyOrder("always-display-client", "account-console"));
assertClientRep(apps.get("account-console"), "Account Console", false, true, getAuthServerRoot() + "realms/test/account/", true);
assertClientRep(apps.get("always-display-client"), "Always Display Client", false, false, getAuthServerRoot() + "realms/master/app/always-display-client", false);
applicationsPage.toggleApplicationDetails("account-console");
applications = applicationsPage.getApplications();
assertFalse(applications.isEmpty());
apps = applications.stream().collect(Collectors.toMap(x -> x.getClientId(), x -> x));
assertThat(apps.keySet(), containsInAnyOrder("always-display-client", "account-console"));
assertClientRep(apps.get("account-console"), "Account Console", false, true, getAuthServerRoot() + "realms/test/account/", false);
assertClientRep(apps.get("always-display-client"), "Always Display Client", false, false, getAuthServerRoot() + "realms/master/app/always-display-client", false);
}
private void assertClientRep(ApplicationsPage.ClientRepresentation clientRep, String name, boolean userConsentRequired, boolean inUse, String effectiveUrl, boolean applicationDetailsVisible) {
assertNotNull(clientRep);
assertEquals(name, clientRep.getClientName());
assertEquals(userConsentRequired, clientRep.isUserConsentRequired());
assertEquals(inUse, clientRep.isInUse());
assertEquals(applicationDetailsVisible, clientRep.isApplicationDetailsVisible());
if (applicationDetailsVisible) assertEquals(effectiveUrl, clientRep.getEffectiveUrl());
}
}

View file

@ -1,56 +0,0 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3;
import org.junit.Test;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.utils.SigningInPageUtils;
import static org.junit.Assert.assertTrue;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public abstract class BaseAccountPageTest extends AbstractAccountTest {
protected abstract AbstractLoggedInPage getAccountPage();
@Override
public void navigateBeforeTest() {
getAccountPage().navigateTo();
loginToAccount();
getAccountPage().assertCurrent();
}
@Test
public void navigationTest() {
pageNotFound.navigateTo();
pageNotFound.assertCurrent();
getAccountPage().navigateToUsingSidebar();
getAccountPage().assertCurrent();
if (getAccountPage().getParentPageId() != null) {
assertTrue("Nav bar subsection should be expanded after clicking nav item",
getAccountPage().sidebar().isNavSubsectionExpanded(getAccountPage().getParentPageId()));
}
}
protected void testModalDialog(Runnable triggerModal, Runnable onCancel) {
SigningInPageUtils.testModalDialog(getAccountPage(), triggerModal, onCancel);
}
}

View file

@ -1,165 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import java.util.Arrays;
import java.util.Objects;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.events.EventType;
import org.keycloak.models.AccountRoles;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.pages.PasswordPage;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.auth.page.login.DeleteAccountActionConfirmPage;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
/**
* @author Zakaria Amine <zakaria.amine88@gmail.com>
*/
public class DeleteAccountTest extends BaseAccountPageTest {
@Page
private PersonalInfoPage personalInfoPage;
@Page
private DeleteAccountActionConfirmPage deleteAccountActionConfirmPage;
@Page
private PasswordPage passwordPage;
@Rule
public AssertEvents events = new AssertEvents(this);
@Override
protected AbstractLoggedInPage getAccountPage() {
return personalInfoPage;
}
@Before
public void setup() {
enableDeleteAccountRequiredAction();
addDeleteAccountRoleToUserClientRoles();
}
@After
public void clean() {
disableDeleteAccountRequiredAction();
}
@Test
public void deleteOwnAccountSectionNotVisibleWithoutClientRole() {
removeDeleteAccountRoleFromUserClientRoles();
refreshPageAndWaitForLoad();
personalInfoPage.assertDeleteAccountSectionVisible(false);
}
@Test
public void deleteOwnAccountSectionNotVisibleWithoutDeleteAccountActionEnabled() {
disableDeleteAccountRequiredAction();
refreshPageAndWaitForLoad();
personalInfoPage.assertDeleteAccountSectionVisible(false);
}
@Test
public void deleteOwnAccountAIACancellationSucceeds() {
refreshPageAndWaitForLoad();
personalInfoPage.assertDeleteAccountSectionVisible(true);
personalInfoPage.clickOpenDeleteExapandable();
personalInfoPage.clickDeleteAccountButton();
reauthenticateUser();
Assert.assertTrue(deleteAccountActionConfirmPage.isCurrent());
deleteAccountActionConfirmPage.clickCancelAIA();
Assert.assertTrue(personalInfoPage.isCurrent());
}
@Test
public void deleteOwnAccountForbiddenWithoutClientRole() {
refreshPageAndWaitForLoad();
personalInfoPage.assertDeleteAccountSectionVisible(true);
personalInfoPage.clickOpenDeleteExapandable();
personalInfoPage.clickDeleteAccountButton();
reauthenticateUser();
Assert.assertTrue(deleteAccountActionConfirmPage.isCurrent());
removeDeleteAccountRoleFromUserClientRoles();
deleteAccountActionConfirmPage.clickConfirmAction();
Assert.assertTrue(deleteAccountActionConfirmPage.isErrorMessageDisplayed());
Assert.assertEquals(deleteAccountActionConfirmPage.getErrorMessageText(), "You do not have enough permissions to delete your own account, contact admin.");
}
@Test
public void deleteOwnAccountSucceeds() {
personalInfoPage.navigateTo();
personalInfoPage.assertDeleteAccountSectionVisible(true);
personalInfoPage.clickOpenDeleteExapandable();
personalInfoPage.clickDeleteAccountButton();
reauthenticateUser();
deleteAccountActionConfirmPage.isCurrent();
deleteAccountActionConfirmPage.clickConfirmAction();
events.expectAccount(EventType.DELETE_ACCOUNT);
Assert.assertTrue(testRealmResource().users().search(testUser.getUsername()).isEmpty());
}
private void reauthenticateUser() {
passwordPage.assertCurrent();
passwordPage.login("password");
}
private void addDeleteAccountRoleToUserClientRoles() {
ApiUtil.assignClientRoles(testRealmResource(), testUser.getId(), "account",AccountRoles.DELETE_ACCOUNT);
}
private void disableDeleteAccountRequiredAction() {
RequiredActionProviderRepresentation deleteAccount = testRealmResource().flows().getRequiredAction("delete_account");
deleteAccount.setEnabled(false);
testRealmResource().flows().updateRequiredAction("delete_account", deleteAccount);
}
private void enableDeleteAccountRequiredAction() {
RequiredActionProviderRepresentation deleteAccount = testRealmResource().flows().getRequiredAction("delete_account");
deleteAccount.setEnabled(true);
testRealmResource().flows().updateRequiredAction("delete_account", deleteAccount);
}
private void removeDeleteAccountRoleFromUserClientRoles() {
ClientRepresentation clientRepresentation = testRealmResource().clients().findByClientId("account").get(0);
String deleteRoleId = testUserResource().roles().clientLevel(clientRepresentation.getId()).listAll().stream().filter(role -> Objects.equals(role.getName(), "delete-account")).findFirst().get().getId();
RoleRepresentation deleteRole = new RoleRepresentation();
deleteRole.setName("delete-account");
deleteRole.setId(deleteRoleId);
testUserResource().roles().clientLevel(clientRepresentation.getId()).remove(Arrays.asList(deleteRole));
}
}

View file

@ -1,461 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.DeviceActivityPage;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class DeviceActivityTest extends BaseAccountPageTest {
public static final String TEST_CLIENT_ID = "test-client";
public static final String TEST_CLIENT_SECRET = "top secret stuff";
public static final String TEST_CLIENT2_ID = "test-client2";
public static final String TEST_CLIENT2_SECRET = "even more top secret stuff";
public static final String TEST_CLIENT3_ID = "test-client3";
public static final String TEST_CLIENT3_SECRET = "dunno";
public static final String TEST_CLIENT3_NAME = "Příliš žluťoučký kůň";
@Page
private DeviceActivityPage deviceActivityPage;
@Override
protected AbstractLoggedInPage getAccountPage() {
return deviceActivityPage;
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation realm = testRealms.get(0);
realm.setClients(Arrays.asList(
ClientBuilder
.create()
.clientId(TEST_CLIENT_ID) // client with no name
.secret(TEST_CLIENT_SECRET)
.directAccessGrants()
.build(),
ClientBuilder
.create().
clientId(TEST_CLIENT2_ID)
.name(LOCALE_CLIENT_NAME) // client with localized name
.secret(TEST_CLIENT2_SECRET)
.directAccessGrants().build(),
ClientBuilder
.create().
clientId(TEST_CLIENT3_ID)
.name(TEST_CLIENT3_NAME) // client without localized name
.secret(TEST_CLIENT3_SECRET)
.directAccessGrants().build()
));
realm.setAccountTheme(LOCALIZED_THEME_PREVIEW); // using localized custom theme for the client localized name
configureInternationalizationForRealm(testRealms.get(0));
}
@Before
public void beforeDeviceActivityTest() {
oauth.clientId(TEST_CLIENT3_ID);
}
@Test
public void browsersTest() {
Map<Browsers, String> browserSessions = new HashMap<>();
Arrays.stream(Browsers.values()).forEach(b -> {
browserSessions.put(b, DeviceActivityPage.getTrimmedSessionId(createSession(b)));
});
deviceActivityPage.clickRefreshPage();
browserSessions.forEach((browser, sessionId) -> {
final Optional<DeviceActivityPage.Session> session = deviceActivityPage.getSession(sessionId);
assertThat(session.isPresent(), is(true));
assertSession(browser, session.get());
});
assertEquals(Browsers.values().length + 1, deviceActivityPage.getSessionsCount()); // + 1 for the current session
}
@Test
public void currentSessionTest() {
createSession(Browsers.CHROME);
createSession(Browsers.SAFARI);
deviceActivityPage.clickRefreshPage();
assertEquals(3, deviceActivityPage.getSessionsCount());
Optional<DeviceActivityPage.Session> currentSession = deviceActivityPage.getSessionByIndex(0); // current session should be first
assertThat(currentSession.isPresent(), is(true));
assertSessionRowsAreNotEmpty(currentSession.get(), false);
assertTrue("Browser identification should be present", currentSession.get().isBrowserDisplayed());
assertTrue("Current session badge should be present", currentSession.get().hasCurrentBadge());
assertFalse("Icon should be present", currentSession.get().getIcon().isEmpty());
}
@Test
public void signOutTest() {
assertFalse("Sign out all shouldn't be displayed", deviceActivityPage.isSignOutAllDisplayed());
final String chromeSessionId = createSession(Browsers.CHROME);
deviceActivityPage.clickRefreshPage();
Optional<DeviceActivityPage.Session> chromeSessionOptional = deviceActivityPage.getSession(chromeSessionId);
assertThat(chromeSessionOptional.isPresent(), is(true));
DeviceActivityPage.Session chromeSession = chromeSessionOptional.get();
createSession(Browsers.SAFARI);
deviceActivityPage.clickRefreshPage();
assertTrue("Sign out all should be displayed", deviceActivityPage.isSignOutAllDisplayed());
assertEquals(3, testUserResource().getUserSessions().size());
assertThat(testUserResource()
.getUserSessions()
.stream()
.map(f -> f.getId())
.map(DeviceActivityPage::getTrimmedSessionId)
.collect(Collectors.toList()),
hasItem(chromeSession.getSessionId()));
// sign out one session
assertThat(chromeSession.isSignOutDisplayed(), is(true));
testModalDialog(chromeSession::clickSignOut, () -> {
assertEquals(3, testUserResource().getUserSessions().size()); // no change, all sessions still present
});
deviceActivityPage.alert().assertSuccess();
assertFalse("Chrome session should be gone", chromeSession.isPresent());
assertEquals(2, testUserResource().getUserSessions().size());
assertThat(testUserResource()
.getUserSessions()
.stream()
.map(f -> f.getId())
.map(DeviceActivityPage::getTrimmedSessionId)
.collect(Collectors.toList()),
not(hasItem(chromeSession.getSessionId())));
// sign out all sessions
testModalDialog(deviceActivityPage::clickSignOutAll, () -> {
assertEquals(2, testUserResource().getUserSessions().size()); // no change
});
accountWelcomeScreen.assertCurrent();
assertEquals(0, testUserResource().getUserSessions().size());
}
@Test
public void clientsTest() {
String sessionId = createSession(Browsers.CHROME);
// attach more clients to the session
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(TEST);
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
ClientModel client2 = session.clients().getClientByClientId(realm, TEST_CLIENT2_ID);
ClientModel client3 = session.clients().getClientByClientId(realm, TEST_CLIENT3_ID);
session.sessions().createClientSession(realm, client2, userSession);
session.sessions().createClientSession(realm, client3, userSession);
});
deviceActivityPage.clickRefreshPage();
List<String> expectedClients = Arrays.asList(TEST_CLIENT_ID, LOCALE_CLIENT_NAME_LOCALIZED, TEST_CLIENT3_NAME);
final Optional<DeviceActivityPage.Session> sessionById = deviceActivityPage.getSession(sessionId);
assertThat(sessionById.isPresent(), is(true));
String[] actualClients = sessionById.get().getClients().split(", ");
assertThat(expectedClients, containsInAnyOrder(actualClients));
final Optional<DeviceActivityPage.Session> session = deviceActivityPage.getSessionByIndex(0);
assertThat(session.isPresent(), is(true));
assertEquals("Account Console", session.get().getClients());
}
@Test
public void timesTests() {
LocalDateTime now = LocalDateTime.now();
LocalDateTime nowPlus1 = now.plusMinutes(1);
String nowStr = now.format(DEFAULT_TIME_FORMATTER);
String nowStrPlus1 = nowPlus1.format(DEFAULT_TIME_FORMATTER);
String sessionId = createSession(Browsers.CHROME);
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(TEST);
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
userSession.setLastSessionRefresh(Time.currentTime() + 120);
});
deviceActivityPage.clickRefreshPage();
final Optional<DeviceActivityPage.Session> session = deviceActivityPage.getSession(sessionId);
assertThat(session.isPresent(), is(true));
String startedAtStr = session.get().getStarted();
LocalDateTime startedAt = LocalDateTime.parse(startedAtStr, DEFAULT_TIME_FORMATTER);
LocalDateTime lastAccessed = LocalDateTime.parse(session.get().getLastAccess(), DEFAULT_TIME_FORMATTER);
LocalDateTime expiresAt = LocalDateTime.parse(session.get().getExpires(), DEFAULT_TIME_FORMATTER);
assertTrue("Last access should be after started at", lastAccessed.isAfter(startedAt));
assertTrue("Expires at should be after last access", expiresAt.isAfter(lastAccessed));
assertTrue("Last accessed should be in the future", lastAccessed.isAfter(now));
assertThat(startedAtStr, either(equalTo(nowStr)).or(equalTo(nowStrPlus1)));
int ssoLifespan = testRealmResource().toRepresentation().getSsoSessionMaxLifespan();
assertEquals(startedAt.plusSeconds(ssoLifespan), expiresAt);
}
@Test
public void timeLocaleTest() {
String sessionId = createSession(Browsers.CHROME);
UserRepresentation user = testUserResource().toRepresentation();
final Locale locale = Locale.GERMAN;
user.setAttributes(new HashMap<String, List<String>>() {{
put("locale", Collections.singletonList(locale.toLanguageTag()));
}});
testUserResource().update(user);
refreshPageAndWaitForLoad();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d. MMMM yyyy 'um' H:mm", locale);
Optional<DeviceActivityPage.Session> session = deviceActivityPage.getSession(sessionId);
assertThat(session.isPresent(), is(true));
try {
LocalDateTime.parse(session.get().getLastAccess(), formatter);
} catch (DateTimeParseException e) {
fail("Time was not formatted with the locale");
}
}
@Test
public void ipTest() {
final String ip = "146.58.69.12";
String sessionId = "abcdefg";
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(TEST);
ClientModel client = session.clients().getClientByClientId(realm, TEST_CLIENT_ID);
UserModel user = session.users().getUserByUsername(realm, "test"); // cannot use testUser.getUsername() because it throws NotSerializableException for no apparent reason (or maybe I'm just stupid :D)
UserSessionModel userSession = session.sessions().createUserSession(sessionId, realm, user, "test", ip, "form", false, null, null, null);
session.sessions().createClientSession(realm, client, userSession);
});
deviceActivityPage.clickRefreshPage();
final Optional<DeviceActivityPage.Session> session = deviceActivityPage.getSession(sessionId);
assertThat(session.isPresent(), is(true));
assertEquals(ip, session.get().getIp());
}
private String createSession(Browsers browser) {
log.info("Creating session for " + browser);
OAuthClient.AccessTokenResponse res;
try {
// using direct grant not to use current browser
res = oauth.doGrantAccessTokenRequest(
TEST, testUser.getUsername(), PASSWORD, null, TEST_CLIENT_ID, TEST_CLIENT_SECRET, browser.userAgent);
} catch (Exception e) {
throw new RuntimeException(e);
}
return res.getSessionState(); // session id
}
private void assertSession(Browsers browser, DeviceActivityPage.Session session) {
log.infof("Asserting %s (session %s)", browser, session.getSessionId());
assertTrue("Session should be present", session.isPresent());
assertTrue("Browser name should be present", session.isBrowserDisplayed());
if (browser.sessionBrowser != null) {
assertEquals(browser.sessionBrowser, session.getBrowser());
} else {
assertEquals("Other/Unknown", session.getBrowser());
}
assertEquals(browser.iconName, session.getIcon());
assertFalse("Session shouldn't have current badge", session.hasCurrentBadge()); // we don't test current session
assertSessionRowsAreNotEmpty(session, true);
}
private void assertSessionRowsAreNotEmpty(DeviceActivityPage.Session session, boolean expectSignOutPresent) {
assertFalse("IP address shouldn't be empty", session.getIp().isEmpty());
assertFalse("Last accessed shouldn't be empty", session.getLastAccess().isEmpty());
assertFalse("Started shouldn't be empty", session.getStarted().isEmpty());
assertFalse("Expires shouldn't be empty", session.getExpires().isEmpty());
assertFalse("Clients shouldn't be empty", session.getClients().isEmpty());
assertEquals("Sign out button visibility", expectSignOutPresent, session.isSignOutDisplayed());
}
public enum Browsers {
CHROME(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
"Chrome/78.0.3904",
DeviceType.DESKTOP
),
CHROMIUM(
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/11.04 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30",
"Chromium/12.0.742",
DeviceType.DESKTOP
),
FIREFOX(
"Mozilla/5.0 (X11; Fedora;Linux x86; rv:60.0) Gecko/20100101 Firefox/60.0",
"Firefox/60.0",
DeviceType.DESKTOP
),
EDGE(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763",
"Edge/18.17763",
DeviceType.DESKTOP
),
// TODO uncomment this once KEYCLOAK-12445 is resolved
// CHREDGE( // Edge based on Chromium
// "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43",
// "Edge/79.0.309 / Mac OS X 10.15.1",
// "edge"
// ),
IE(
"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
"IE/11.0",
DeviceType.DESKTOP
),
SAFARI(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15",
"Safari/13.0.3",
DeviceType.DESKTOP
),
OPERA(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 OPR/56.0.3051.52",
"Opera/56.0.3051",
DeviceType.DESKTOP
),
YANDEX(
"Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 YaBrowser/17.6.1.749 Yowser/2.5 Safari/537.36",
"Yandex Browser/17.6.1",
DeviceType.DESKTOP
),
CHROME_ANDROID(
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Mobile Safari/537.36",
"Chrome Mobile/68.0.3440",
DeviceType.MOBILE
),
SAFARI_IOS(
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1",
"Mobile Safari/13.0.1",
DeviceType.MOBILE
),
UNKNOWN_BROWSER(
"Top-secret government browser running on top-secret OS",
null,
DeviceType.UNKNOWN
),
UNKNOWN_OS(
"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
"Chrome/78.0.3904",
DeviceType.UNKNOWN
),
UNKNOWN_OS_VERSION(
"Mozilla/5.0 (Windows 256.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
"Chrome/78.0.3904",
DeviceType.UNKNOWN
);
// not sure what "Amazon" browser is supposed to be (it's specified in DeviceActivityPage.tsx)
private final String userAgent;
private final String sessionBrowser; // how the browser is interpreted by the sessions endpoint
private final DeviceType deviceType;
private final String iconName;
Browsers(String userAgent, String sessionBrowser, DeviceType deviceType) {
this.userAgent = userAgent;
this.sessionBrowser = sessionBrowser;
this.deviceType = deviceType;
this.iconName = deviceType.getIconName();
}
public String userAgent() {
return userAgent;
}
public String sessionBrowser() {
return sessionBrowser;
}
public String iconName() {
return iconName;
}
private enum DeviceType {
DESKTOP("desktop"),
MOBILE("mobile"),
UNKNOWN("desktop"); // Default icon
private final String iconName;
DeviceType(String iconName) {
this.iconName = iconName;
}
public String getIconName() {
return iconName;
}
}
}
}

View file

@ -1,196 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.models.UserModel;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import org.keycloak.testsuite.ui.account3.page.SigningInPage;
import org.keycloak.testsuite.ui.account3.page.WelcomeScreen;
import org.keycloak.testsuite.util.WaitUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class InternationalizationTest extends AbstractAccountTest {
@Page
private WelcomeScreen welcomeScreen;
@Page
private PersonalInfoPage personalInfoPage;
@Page
private SigningInPage signingInPage;
private SigningInPage.CredentialType passwordCredentialType;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
configureInternationalizationForRealm(testRealms.get(0));
}
@Before
public void beforeI18nTest() {
assertTestUserLocale(null);
passwordCredentialType = signingInPage.getCredentialType(PasswordCredentialModel.TYPE);
}
@Test
public void loggedInPageTest() {
personalInfoPage.navigateTo();
loginToAccount();
assertTestUserLocale(null);
personalInfoPage.selectLocale(CUSTOM_LOCALE);
personalInfoPage.clickSave(false);
WaitUtils.waitForPageToLoad();
assertTestUserLocale(CUSTOM_LOCALE);
assertCustomLocalePersonalInfo();
// check if selected locale is preserved
personalInfoPage.header().clickLogoutBtn();
welcomeScreen.assertCurrent();
assertCustomLocaleWelcomeScreen();
}
@Test
public void realmLocalizationMessagesAreApplied() {
String messageKey = "personalInfoHtmlTitle";
String localeEn = Locale.ENGLISH.toLanguageTag();
String messageEn = "personalInfoHtmlTitle EN";
testRealmResource().localization().saveRealmLocalizationText(localeEn,
messageKey, messageEn);
getCleanup().addLocalization(localeEn);
String localeDe = Locale.GERMAN.toLanguageTag();
String messageDe = "personalInfoHtmlTitle DE";
testRealmResource().localization().saveRealmLocalizationText(localeDe,
messageKey, messageDe);
getCleanup().addLocalization(localeDe);
// default locale should be "en"
personalInfoPage.navigateTo();
loginToAccount();
assertTestUserLocale(null);
assertPersonalInfo(messageEn);
// switch to locale "de"
personalInfoPage.selectLocale(localeDe);
personalInfoPage.clickSave(false);
WaitUtils.waitForPageToLoad();
assertTestUserLocale(localeDe);
assertPersonalInfo(messageDe);
}
@Test
@Ignore
public void loginFormTest() {
personalInfoPage.navigateTo();
loginPage.localeDropdown().selectAndAssert(CUSTOM_LOCALE_NAME);
loginPage.form().login(testUser); // cannot use loginToAccount() because it asserts URL which is now different
assertTestUserLocale(CUSTOM_LOCALE);
assertCustomLocalePersonalInfo();
}
@Test
@Ignore // TODO: Enable once chromedriver version 113.0.5672.92 is available in https://chromedriver.storage.googleapis.com/
public void userAttributeTest() {
testUser.setAttributes(singletonMap(UserModel.LOCALE, singletonList(CUSTOM_LOCALE)));
testUserResource().update(testUser);
welcomeScreen.navigateTo();
welcomeScreen.clickPersonalInfoLink();
assertEquals(DEFAULT_LOCALE_NAME, loginPage.localeDropdown().getSelected());
loginToAccount();
assertCustomLocalePersonalInfo();
}
@Test
@Ignore
public void shouldDisplayTimeUsingSelectedLocale() {
signingInPage.navigateTo();
loginToAccount();
SigningInPage.UserCredential passwordCred =
passwordCredentialType.getUserCredential(testUserResource().credentials().get(0).getId());
try {
LocalDateTime.parse(passwordCred.getCreatedAtStr(), DEFAULT_TIME_FORMATTER);
} catch (DateTimeParseException e) {
fail("Time was not formatted with the locale");
}
signingInPage.header().clickLogoutBtn();
signingInPage.navigateTo();
loginPage.localeDropdown().selectAndAssert("Deutsch");
loginPage.form().login(testUser);
DateTimeFormatter formatterDe = DateTimeFormatter.ofPattern("d. MMMM yyyy 'um' H:mm", Locale.GERMAN);
try {
LocalDateTime.parse(passwordCred.getCreatedAtStr(), formatterDe);
} catch (DateTimeParseException e) {
fail("Time was not formatted with the locale");
}
}
private void assertCustomLocaleWelcomeScreen() {
assertEquals("Vítejte v Keycloaku", welcomeScreen.getWelcomeMessage());
}
private void assertCustomLocalePersonalInfo() {
assertPersonalInfo("Osobní údaje");
}
private void assertPersonalInfo(String expectedText) {
assertEquals(expectedText, personalInfoPage.getPageTitle());
}
private void assertTestUserLocale(String expectedLocale) {
String actualLocale = null;
List <String> userLocales;
Map<String, List<String>> userAttributes = testUserResource().toRepresentation().getAttributes();
if (userAttributes != null) {
userLocales = userAttributes.get(UserModel.LOCALE);
if (userLocales != null) {
actualLocale = userLocales.get(0);
}
}
assertEquals(expectedLocale, actualLocale);
}
}

View file

@ -1,163 +0,0 @@
/*
* Copyright 2020 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.ui.account3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.ldap.LDAPStorageProvider;
import org.keycloak.storage.ldap.idm.model.LDAPObject;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.federation.ldap.LDAPTestContext;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import org.keycloak.testsuite.ui.account3.page.SigningInPage;
import org.keycloak.testsuite.util.LDAPRule;
import org.keycloak.testsuite.util.LDAPTestUtils;
/**
* @author Alfredo Moises Boullosa <aboullos@redhat.com>
*/
public class LDAPAccountTest extends AbstractAccountTest {
@Page
private SigningInPage signingInPage;
@Page
private PersonalInfoPage personalInfoPage;
private SigningInPage.CredentialType passwordCredentialType;
@ClassRule
public static LDAPRule ldapRule = new LDAPRule();
@Before
public void beforeSigningInTest() {
passwordCredentialType = signingInPage.getCredentialType(PasswordCredentialModel.TYPE);
testingClient.testing().ldap(TEST).createLDAPProvider(ldapRule.getConfig(), true);
log.infof("LDAP Provider created");
String userName = "johnkeycloak";
String firstName = "Jonh";
String lastName = "Doe";
String email = "john@email.org";
testingClient.server().run(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel appRealm = ctx.getRealm();
// Delete all LDAP users and add some new for testing
LDAPTestUtils.removeAllLDAPUsers(ctx.getLdapProvider(), appRealm);
LDAPObject john = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, userName, firstName, lastName, email, null, "1234");
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john, PASSWORD);
});
testRealmLoginPage.setAuthRealm(testRealmPage);
testUser = createUserRepresentation(userName, email, firstName, lastName, true);
setPasswordFor(testUser, PASSWORD);
resetTestRealmSession();
}
@Test
public void createdNotVisibleTest() {
signingInPage.navigateTo();
loginPage.form().login(testUser);
SigningInPage.UserCredential userCredential = passwordCredentialType.getUserCredential("password");
assertTrue("ROW is not present", userCredential.isPresent());
assertFalse("Created at is present", userCredential.hasCreatedAt());
}
// KEYCLOAK-15634
@Test
public void updateProfileWithAttributePresent() {
RealmResource testRealm = adminClient.realm("test");
assertEquals(getAccountThemeName(), testRealm.toRepresentation().getAccountTheme());
UserRepresentation userRepBefore = ApiUtil.findUserByUsername(testRealm,"keycloak-15634");
assertNull("User should not exist", userRepBefore);
testingClient.server().run(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel appRealm = ctx.getRealm();
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ctx.getLdapModel());
ldapFedProvider.getModel().put(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.UNSYNCED.toString());
appRealm.updateComponent(ldapFedProvider.getModel());
LDAPObject testUser = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(),
appRealm, "keycloak-15634",
"firstName",
"lastName",
"keycloak-15634@test.local",
null,
"1234");
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), testUser, PASSWORD);
});
// Check our test user is ok before updating profile
userRepBefore = ApiUtil.findUserByUsername(testRealm,"keycloak-15634");
assertEquals("Test user should have an email address set", "keycloak-15634@test.local", userRepBefore.getEmail());
assertTrue("Test user should have the LDAP_ID attribute set", userRepBefore.getAttributes().containsKey("LDAP_ID"));
assertFalse("Test user should not have locale attribute set", userRepBefore.getAttributes().containsKey("locale"));
personalInfoPage.navigateTo();
loginPage.assertCurrent();
loginPage.form().login("keycloak-15634","password");
personalInfoPage.assertCurrent();
assertEquals("keycloak-15634@test.local", personalInfoPage.getEmail());
// Trigger the JS involved in KEYCLOAK-15634
personalInfoPage.setEmail("keycloak-15634@domain.local");
personalInfoPage.clickSave();
// Check if updateProfile went well and if user is still there
UserRepresentation userRepAfter = ApiUtil.findUserByUsername(testRealm,"keycloak-15634");
assertNotNull("Test user should still be there", userRepAfter);
assertEquals("Email should have been updated","keycloak-15634@domain.local", userRepAfter.getEmail());
assertTrue("LDAP_ID attribute should still be there", userRepAfter.getAttributes().containsKey("LDAP_ID"));
// Clean up
ApiUtil.removeUserByUsername(testRealm, "keycloak-15634");
testingClient.server().run(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel appRealm = ctx.getRealm();
LDAPTestUtils.removeAllLDAPUsers(ctx.getLdapProvider(), appRealm);
});
}
}

View file

@ -1,209 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.social.google.GoogleIdentityProviderFactory;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.LinkedAccountsPage;
import org.keycloak.testsuite.util.ClientBuilder;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class LinkedAccountsTest extends BaseAccountPageTest {
public static final String SOCIAL_IDP_ALIAS = "fake-google-account";
public static final String SYSTEM_IDP_ALIAS = "kc-to-kc-account";
public static final String REALM2_NAME = "test-realm2";
public static final String CLIENT_ID = "cross-realm-client";
public static final String CLIENT_SECRET = "top secret";
private UserRepresentation homerUser;
private LinkedAccountsPage.IdentityProvider socialIdp;
private LinkedAccountsPage.IdentityProvider systemIdp;
@Page
private LinkedAccountsPage linkedAccountsPage;
@Page
private LoginPage loginPageWithSocialBtns;
public LinkedAccountsTest() {
// needs to be done here (setting fields in addTestRealms acts really weird resulting in Homer being null)
homerUser = createUserRepresentation("hsimpson", "hsimpson@keycloak.org",
"Homer", "Simpson", true, "Mmm donuts");
}
@Override
protected AbstractLoggedInPage getAccountPage() {
return linkedAccountsPage;
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation realm1 = testRealms.get(0);
realm1.addIdentityProvider(createIdentityProviderRepresentation(SOCIAL_IDP_ALIAS,
GoogleIdentityProviderFactory.PROVIDER_ID));
String oidcRoot = getAuthServerRoot() + "realms/" + REALM2_NAME + "/protocol/openid-connect/";
IdentityProviderRepresentation systemIdp = createIdentityProviderRepresentation(SYSTEM_IDP_ALIAS,
OIDCIdentityProviderFactory.PROVIDER_ID);
systemIdp.getConfig().put("clientId", CLIENT_ID);
systemIdp.getConfig().put("clientSecret", CLIENT_SECRET);
systemIdp.getConfig().put("clientAuthMethod", OIDCLoginProtocol.CLIENT_SECRET_POST);
systemIdp.getConfig().put("authorizationUrl", oidcRoot + "auth");
systemIdp.getConfig().put("tokenUrl", oidcRoot + "token");
realm1.addIdentityProvider(systemIdp);
ClientRepresentation client = ClientBuilder.create()
.clientId(CLIENT_ID)
.secret(CLIENT_SECRET)
.redirectUris(getAuthServerRoot() + "realms/" + TEST + "/broker/" + SYSTEM_IDP_ALIAS + "/endpoint")
.build();
// using REALM2 as an identity provider
RealmRepresentation realm2 = new RealmRepresentation();
realm2.setId(REALM2_NAME);
realm2.setRealm(REALM2_NAME);
realm2.setEnabled(true);
realm2.setClients(Collections.singletonList(client));
realm2.setUsers(Collections.singletonList(homerUser));
testRealms.add(realm2);
}
@Before
public void beforeLinkedAccountsTest() {
socialIdp = linkedAccountsPage.getProvider(SOCIAL_IDP_ALIAS);
systemIdp = linkedAccountsPage.getProvider(SYSTEM_IDP_ALIAS);
assertProvidersCount();
}
@After
public void afterLinkedAccountsTest() {
assertProvidersCount();
}
@Test
public void linkAccountTest() {
assertEquals(0, testUserResource().getFederatedIdentity().size());
assertProvider(socialIdp, false, true, "");
assertProvider(systemIdp, false, false, "");
systemIdp.clickLinkBtn();
loginPage.form().login(homerUser);
linkedAccountsPage.assertCurrent();
assertProvider(systemIdp, true, false, homerUser.getUsername());
assertProvider(socialIdp, false, true, "");
// check through admin REST endpoints
List<FederatedIdentityRepresentation> fids = testUserResource().getFederatedIdentity();
assertEquals(1, fids.size());
FederatedIdentityRepresentation fid = fids.get(0);
assertEquals(SYSTEM_IDP_ALIAS, fid.getIdentityProvider());
assertEquals(homerUser.getUsername(), fid.getUserName());
// logout user and try to login using IdP
testUserResource().logout();
linkedAccountsPage.navigateTo();
loginPageWithSocialBtns.clickSocial(SYSTEM_IDP_ALIAS);
linkedAccountsPage.assertCurrent(); // no need for re-login to REALM2
}
@Test
public void unlinkAccountTest() {
FederatedIdentityRepresentation fid = new FederatedIdentityRepresentation();
fid.setIdentityProvider(SOCIAL_IDP_ALIAS);
fid.setUserId("Homer lost his ID at Moe's last night");
fid.setUserName(homerUser.getUsername());
testUserResource().addFederatedIdentity(SOCIAL_IDP_ALIAS, fid);
assertEquals(1, testUserResource().getFederatedIdentity().size());
linkedAccountsPage.navigateTo();
assertProvider(systemIdp, false, false, "");
assertProvider(socialIdp, true, true, homerUser.getUsername());
socialIdp.clickUnlinkBtn();
linkedAccountsPage.assertCurrent();
assertProvider(systemIdp, false, false, "");
assertProvider(socialIdp, false, true, "");
assertEquals(0, testUserResource().getFederatedIdentity().size());
}
private void assertProvider(
LinkedAccountsPage.IdentityProvider provider,
boolean expectLinked,
boolean expectSocial,
String expectedUsername
) {
if (expectLinked) {
assertTrue("Account should be in the \"Linked\" list", provider.isLinked());
assertTrue("Unlink button should be visible", provider.isUnlinkBtnVisible());
assertFalse("Link button shouldn't be visible", provider.isLinkBtnVisible());
}
else {
assertFalse("Account should be in the \"Unlinked\" list", provider.isLinked());
assertFalse("Unlink button shouldn't be visible", provider.isUnlinkBtnVisible());
assertTrue("Link button should be visible", provider.isLinkBtnVisible());
}
if (expectSocial) {
assertTrue("Social badge should be visible", provider.hasSocialLoginBadge());
assertTrue("Social icon should be visible", provider.hasSocialIcon());
assertFalse("Default icon shouldn't be visible", provider.hasDefaultIcon());
}
else {
assertFalse("Social badge shouldn't be visible", provider.hasSocialLoginBadge());
assertFalse("Social icon shouldn't be visible", provider.hasSocialIcon());
assertTrue("Default icon should be visible", provider.hasDefaultIcon());
}
assertEquals(expectedUsername, provider.getUsername());
}
private void assertProvidersCount() {
assertEquals(2,
linkedAccountsPage.getLinkedProvidersCount() + linkedAccountsPage.getUnlinkedProvidersCount());
}
}

View file

@ -1,316 +0,0 @@
package org.keycloak.testsuite.ui.account3;
import com.google.common.collect.Lists;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.AccountRoles;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.MyResourcesPage;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.util.JsonSerialization;
import org.openqa.selenium.NoSuchElementException;
import jakarta.ws.rs.core.Response;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.Collections.singletonList;
import static org.hamcrest.core.StringEndsWith.endsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MyResourcesTest extends BaseAccountPageTest {
private static final String[] userNames = new String[]{"alice", "jdoe"};
@Page
private MyResourcesPage myResourcesPage;
private RealmRepresentation testRealm;
private CloseableHttpClient httpClient;
@Override
protected AbstractLoggedInPage getAccountPage() {
return myResourcesPage;
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
testRealm = testRealms.get(0);
testRealm.setUserManagedAccessAllowed(true);
testRealm.setUsers(Lists.asList("admin", userNames).stream().map(this::createUser).collect(Collectors.toList()));
ClientRepresentation client = ClientBuilder.create()
.clientId("my-resource-server")
.authorizationServicesEnabled(true)
.serviceAccountsEnabled(true)
.secret("secret")
.name("My Resource Server")
.baseUrl("http://resourceserver.com")
.directAccessGrants().build();
testRealm.setClients(singletonList(client));
}
private UserRepresentation createUser(String userName) {
return UserBuilder.create()
.username(userName)
.enabled(true)
.password(PASSWORD)
.role("account", AccountRoles.MANAGE_ACCOUNT)
.build();
}
@After
public void after() {
if (httpClient != null) {
try {
httpClient.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void afterAbstractKeycloakTestRealmImport() {
ClientResource resourceServer = getResourceServer();
AuthzClient authzClient = createAuthzClient(resourceServer.toRepresentation());
AuthorizationResource authorization = resourceServer.authorization();
ResourceRepresentation resource13 = null;
for (int i = 1; i < 15; i++) {
ResourceRepresentation resource = createResource(authzClient, authorization, i);
if (i == 13) {
resource13 = resource;
}
for (String scope : Arrays.asList("Scope A", "Scope B")) {
createTicket(authzClient, i, resource, scope, userNames[i % userNames.length]);
}
}
createTicket(authzClient, 13, resource13, "Scope A", "admin");
}
private void createTicket(AuthzClient authzClient, int i, ResourceRepresentation resource, String scope, String userName) {
PermissionTicketRepresentation ticket = new PermissionTicketRepresentation();
ticket.setGranted(!(i == 12 || i == 13));
ticket.setOwner(resource.getOwner().getId());
ticket.setRequesterName(userName);
ticket.setResource(resource.getId());
ticket.setScopeName(scope);
authzClient.protection("jdoe", PASSWORD).permission().create(ticket);
}
private ResourceRepresentation createResource(AuthzClient authzClient, AuthorizationResource authorization, int i) {
ResourceRepresentation resource = new ResourceRepresentation();
resource.setOwnerManagedAccess(true);
try {
final byte[] content = new JWSInput(authzClient.obtainAccessToken("jdoe", PASSWORD).getToken()).getContent();
final AccessToken accessToken = JsonSerialization.readValue(content, AccessToken.class);
resource.setOwner(accessToken.getSubject());
}
catch (Exception e) {
throw new RuntimeException(e);
}
resource.setName("Resource " + i);
resource.setDisplayName("Display Name " + i);
resource.setIconUri("Icon Uri " + i);
resource.addScope("Scope A", "Scope B", "Scope C", "Scope D");
resource.setUri("http://resourceServer.com/resources/" + i);
try (Response response1 = authorization.resources().create(resource)) {
resource.setId(response1.readEntity(ResourceRepresentation.class).getId());
}
return resource;
}
@Override
public void addTestUser() {
testUser = createUser("jdoe");
}
@Test
public void shouldDisplayTheResources() {
assertEquals(6, myResourcesPage.getResourcesListCount());
assertEquals("Resource 0", myResourcesPage.getCellText("name", 0));
assertEquals("Resource 1", myResourcesPage.getCellText("name", 1));
assertEquals("My Resource Server", myResourcesPage.getCellText("client", 0));
assertEquals("http://resourceserver.com/", myResourcesPage.getCellHref("client", 0));
}
@Test
public void shouldShowMyResourcesAndUpdatePermissions() {
final int row = 1;
myResourcesPage.clickExpandButton(row);
myResourcesPage.clickCollapseButton(row);
myResourcesPage.clickExpandButton(row);
assertEquals("Resource is shared with jdoe.", myResourcesPage.getSharedWith(row));
myResourcesPage.clickEditButton(row);
assertEquals("jdoe", myResourcesPage.getEditDialogUsername(0));
myResourcesPage.removeAllPermissions();
myResourcesPage.alert().assertSuccess();
assertEquals("This resource is not shared.", myResourcesPage.getSharedWith(row));
}
@Test
public void shouldShowMyResourcesAndRemoveShares() {
final int row = 2;
myResourcesPage.clickExpandButton(row);
testModalDialog(() -> myResourcesPage.clickRemoveButton(row), () ->
assertEquals("Resource is shared with alice.", myResourcesPage.getSharedWith(row)));
myResourcesPage.alert().assertSuccess();
assertEquals("This resource is not shared.", myResourcesPage.getSharedWith(row));
}
@Test
public void shouldShowMyResourcesAndShare() {
final int row = 3;
myResourcesPage.clickExpandButton(row);
assertEquals("Resource is shared with jdoe.", myResourcesPage.getSharedWith(row));
myResourcesPage.clickShareButton(row);
myResourcesPage.createShare("alice");
myResourcesPage.alert().assertSuccess();
assertThat(myResourcesPage.getSharedWith(row), endsWith("and 1 other users."));
}
@Test
public void firstShouldRefreshOnRefreshButtonClick() {
ClientResource resourceServer = getResourceServer();
AuthzClient authzClient = createAuthzClient(resourceServer.toRepresentation());
AuthorizationResource authorization = resourceServer.authorization();
createResource(authzClient, authorization, 0);
assertEquals("Resource 1", myResourcesPage.getCellText("name", 0));
myResourcesPage.clickRefreshButton();
assertEquals("Resource 0", myResourcesPage.getCellText("name", 0));
}
@Test
public void shouldAllowRequestToShare() {
final String resourceName = "Resource 12";
assertEquals("1", myResourcesPage.getPendingRequestText(resourceName));
switchUserSharedWithMeTab();
assertFalse(myResourcesPage.containsResource(resourceName));
login("jdoe");
myResourcesPage.clickPendingRequest(resourceName);
assertEquals("alice", myResourcesPage.getPendingRequestRequestor(0));
final String permissions = myResourcesPage.getPendingRequestPermissions(0);
assertTrue(permissions.contains("Scope A"));
assertTrue(permissions.contains("Scope B"));
myResourcesPage.acceptRequest(resourceName, 0);
assertNoPendingRequest(resourceName);
switchUserSharedWithMeTab();
assertTrue(myResourcesPage.containsResource(resourceName));
}
@Test
public void shouldDenyRequestToShare() {
final String resourceName = "Resource 13";
switchUserSharedWithMeTab();
assertFalse(myResourcesPage.containsResource(resourceName));
login("jdoe");
myResourcesPage.clickNextPage();
assertEquals("2", myResourcesPage.getPendingRequestText(resourceName));
myResourcesPage.clickPendingRequest(resourceName);
myResourcesPage.denyRequest(resourceName, 0);
assertEquals("1", myResourcesPage.getPendingRequestText(resourceName));
switchUserSharedWithMeTab();
assertFalse(myResourcesPage.containsResource(resourceName));
}
@Test
public void shouldNotHaveRequestToShareButton() {
assertNoPendingRequest("Resource 0");
assertNoPendingRequest("Resource 1");
}
private void assertNoPendingRequest(String resourceName) {
try {
myResourcesPage.getPendingRequestText(resourceName);
fail("should not have a pending request button");
} catch (NoSuchElementException e) {
// success
}
}
private void switchUserSharedWithMeTab() {
login("alice");
myResourcesPage.clickSharedWithMeTab();
}
private void login(String user) {
myResourcesPage.clickSignOut();
myResourcesPage.navigateTo();
loginPage.form().login(createUser(user));
}
private AuthzClient createAuthzClient(ClientRepresentation client) {
Map<String, Object> credentials = new HashMap<>();
credentials.put("secret", "secret");
httpClient = HttpClientBuilder.create().build();
return AuthzClient
.create(new Configuration(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth",
testRealm.getRealm(), client.getClientId(),
credentials, httpClient));
}
private ClientResource getResourceServer() {
ClientsResource clients = adminClient.realm(TEST).clients();
return clients.get(clients.findByClientId("my-resource-server").get(0).getId());
}
}

View file

@ -1,79 +0,0 @@
/*
* Copyright 2020 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.admin.client.resource.RoleScopeResource;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.testsuite.ui.account3.page.ForbiddenPage;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import org.keycloak.testsuite.ui.account3.page.SigningInPage;
import org.keycloak.testsuite.ui.account3.page.WelcomeScreen;
import java.util.List;
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class PermissionsTest extends AbstractAccountTest {
@Page
private WelcomeScreen welcomeScreen;
@Page
private PersonalInfoPage personalInfoPage;
@Page
private SigningInPage signingInPage;
@Page
private ForbiddenPage forbiddenPage;
private static final String DEFAULT_ROLE_NAME = "default-roles-" + TEST;
@Test
public void manageAccountRoleRequired() throws Exception {
// remove realm level roles (no "default-roles-test") and any roles in the account client
testUserResource().roles().realmLevel().remove(testUserResource().roles().realmLevel().listAll());
String accountClientId = testRealmResource().clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0).getId();
RoleScopeResource roleScopes = testUserResource().roles().clientLevel(accountClientId);
List<RoleRepresentation> roles = roleScopes.listAll();
if (!roles.isEmpty()) {
roleScopes.remove(roles);
}
welcomeScreen.header().clickLoginBtn();
loginToAccount();
welcomeScreen.assertCurrent(); // no forbidden at welcome screen yet
welcomeScreen.clickPersonalInfoLink();
forbiddenPage.assertCurrent();
signingInPage.navigateToUsingSidebar();
forbiddenPage.assertCurrent();
// still possible to sign out
forbiddenPage.header().clickLogoutBtn();
welcomeScreen.assertCurrent();
welcomeScreen.header().assertLoginBtnVisible(true);
welcomeScreen.header().assertLogoutBtnVisible(false);
}
}

View file

@ -1,277 +0,0 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.forms.VerifyProfileTest;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import org.keycloak.testsuite.util.UserBuilder;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class PersonalInfoTest extends BaseAccountPageTest {
@Page
private PersonalInfoPage personalInfoPage;
private UserRepresentation testUser2;
@Before
public void setTestUser() {
testUser2 = new UserRepresentation();
testUser2.setUsername("vmuzikar");
testUser2.setEmail("vmuzikar@redhat.com");
testUser2.setFirstName("Václav");
testUser2.setLastName("Muzikář");
ApiUtil.removeUserByUsername(testRealmResource(), testUser2.getUsername());
VerifyProfileTest.enableUnmanagedAttributes(testRealmResource().users().userProfile());
}
@Override
protected AbstractLoggedInPage getAccountPage() {
return personalInfoPage;
}
@Test
public void updateUserInfo() {
setEditUsernameAllowed(true);
assertTrue(personalInfoPage.valuesEqual(testUser));
personalInfoPage.assertUsernameDisabled(false);
personalInfoPage.assertSaveDisabled(false);
personalInfoPage.setValues(testUser2, true);
//assertEquals("test user", personalInfoPage.header().getToolbarLoggedInUser());
assertTrue(personalInfoPage.valuesEqual(testUser2));
personalInfoPage.assertSaveDisabled(false);
personalInfoPage.clickSave();
personalInfoPage.alert().assertSuccess();
personalInfoPage.assertSaveDisabled(false);
personalInfoPage.navigateTo();
personalInfoPage.valuesEqual(testUser2);
//assertEquals("Václav Muzikář", personalInfoPage.header().getToolbarLoggedInUser());
// change just first and last name
testUser2.setFirstName("Another");
testUser2.setLastName("Name");
personalInfoPage.setValues(testUser2, true);
personalInfoPage.clickSave();
personalInfoPage.alert().assertSuccess();
personalInfoPage.navigateTo();
personalInfoPage.valuesEqual(testUser2);
//assertEquals("Another Name", personalInfoPage.header().getToolbarLoggedInUser());
}
@Test
public void formValidationTest() {
setEditUsernameAllowed(true);
// clear username
personalInfoPage.setUsername("");
personalInfoPage.assertSaveDisabled(true);
personalInfoPage.assertUsernameValid(false);
personalInfoPage.setUsername("hsimpson");
personalInfoPage.assertUsernameValid(true);
// clear email
personalInfoPage.setEmail("edewit@");
personalInfoPage.assertEmailValid(false);
personalInfoPage.setEmail("");
personalInfoPage.assertEmailValid(false);
personalInfoPage.setEmail("hsimpson@springfield.com");
personalInfoPage.assertEmailValid(true);
// clear first name
personalInfoPage.setFirstName("");
personalInfoPage.assertFirstNameValid(false);
personalInfoPage.setFirstName("Homer");
personalInfoPage.assertFirstNameValid(true);
// clear last name
personalInfoPage.setLastName("");
personalInfoPage.assertLastNameValid(false);
personalInfoPage.setLastName("Simpson");
personalInfoPage.assertLastNameValid(true);
// duplicity tests
ApiUtil.createUserWithAdminClient(testRealmResource(), testUser2);
// duplicate username
personalInfoPage.setUsername(testUser2.getUsername());
personalInfoPage.clickSave();
personalInfoPage.alert().assertDanger();
// TODO assert actual error message and that the field is marked as invalid (KEYCLOAK-12102)
personalInfoPage.setUsername(testUser.getUsername());
// duplicate email
personalInfoPage.setEmail(testUser2.getEmail());
personalInfoPage.clickSave();
personalInfoPage.alert().assertDanger();
// TODO assert actual error message and that the field is marked as invalid (KEYCLOAK-12102)
// check no changes were saved
personalInfoPage.navigateTo();
personalInfoPage.valuesEqual(testUser);
}
@Test
public void cancelForm() {
setEditUsernameAllowed(false);
personalInfoPage.setValues(testUser2, false);
personalInfoPage.setEmail("hsimpson@springfield.com");
personalInfoPage.clickCancel();
personalInfoPage.valuesEqual(testUser2);
}
@Test
public void disabledEditUsername() {
setEditUsernameAllowed(false);
personalInfoPage.assertUsernameDisabled(true);
personalInfoPage.setValues(testUser2, false);
personalInfoPage.clickSave();
personalInfoPage.alert().assertSuccess();
testUser2.setUsername(testUser.getUsername()); // the username should remain the same
personalInfoPage.navigateTo();
personalInfoPage.valuesEqual(testUser2);
}
@Test
public void clickLogoTest() {
personalInfoPage.clickBrandLink();
accountWelcomeScreen.assertCurrent();
}
@Ignore("Username is not included in the account console anymore, but it should be there.")
@Test
public void testNameInToolbar() {
assertEquals("test user", personalInfoPage.header().getToolbarLoggedInUser());
UserRepresentation user = new UserRepresentation();
user.setUsername("edewit");
user.setEnabled(true);
setPasswordFor(user, "password");
try {
ApiUtil.removeUserByUsername(testRealmResource(), testUser.getUsername());
personalInfoPage.navigateTo();
ApiUtil.createUserWithAdminClient(testRealmResource(), user);
loginPage.form().login(user);
assertEquals("edewit", personalInfoPage.header().getToolbarLoggedInUser());
} finally {
ApiUtil.removeUserByUsername(testRealmResource(), user.getUsername());
}
}
private void setEditUsernameAllowed(boolean value) {
RealmRepresentation realm = testRealmResource().toRepresentation();
realm.setEditUsernameAllowed(value);
testRealmResource().update(realm);
refreshPageAndWaitForLoad();
}
private void addUser(String username, String email) {
UserRepresentation user = UserBuilder.create()
.username(username)
.enabled(true)
.email(email)
.firstName("first")
.lastName("last")
.build();
RealmResource testRealm = adminClient.realm("test");
ApiUtil.createUserAndResetPasswordWithAdminClient(testRealm, user, "password");
}
// KEYCLOAK-15634
@Test
public void updateProfileWithAttributePresent() {
RealmResource testRealm = adminClient.realm("test");
assertEquals(getAccountThemeName(), testRealm.toRepresentation().getAccountTheme());
// Add a user and set a test attribute
addUser("keycloak-15634","keycloak-15634@test.local");
UserRepresentation userRepBefore = ApiUtil.findUserByUsername(testRealm,"keycloak-15634");
Map<String, List<String>> userAttributes = new HashMap<>();
userAttributes.put("testAttribute", Collections.singletonList("testValue"));
userRepBefore.setAttributes(userAttributes);
testRealm.users().get(userRepBefore.getId()).update(userRepBefore);
// Check our test user is ok before updating profile with account v2
UserRepresentation updatedUserRep = ApiUtil.findUserByUsername(testRealm,"keycloak-15634");
assertEquals("keycloak-15634@test.local", updatedUserRep.getEmail());
assertEquals("testAttribute should be set", "testValue", updatedUserRep.getAttributes().get("testAttribute").get(0));
assertFalse("locale attribute should not be set", updatedUserRep.getAttributes().containsKey("locale"));
personalInfoPage.assertCurrent();
personalInfoPage.header().clickLogoutBtn();
personalInfoPage.navigateTo();
loginPage.assertCurrent();
loginPage.form().login("keycloak-15634","password");
personalInfoPage.assertCurrent();
// Trigger the JS involved in KEYCLOAK-15634
assertEquals("keycloak-15634@test.local", personalInfoPage.getEmail());
personalInfoPage.setEmail("keycloak-15634@domain.local");
personalInfoPage.clickSave();
// Check if updateProfile went well and if testAttribute is still there
UserRepresentation userRepAfter = ApiUtil.findUserByUsername(testRealm,"keycloak-15634");
assertEquals("keycloak-15634@domain.local", userRepAfter.getEmail());
assertEquals("testAttribute should still be there","testValue", userRepAfter.getAttributes().get("testAttribute").get(0));
ApiUtil.removeUserByUsername(testRealm, "keycloak-15634");
}
@Test
// https://issues.redhat.com/browse/KEYCLOAK-16890
// Stored personal info triggers attack via the display of user name in header.
// If user name is left unsanitized, this test will fail with
// org.openqa.selenium.UnhandledAlertException: unexpected alert open: {Alert text : XSS}
public void storedXSSAttack() {
personalInfoPage.navigateTo();
testUser.setFirstName("<img src=x onerror=\"alert('XSS');\">");
personalInfoPage.setValues(testUser, false);
personalInfoPage.clickSave();
personalInfoPage.header().clickLogoutBtn();
accountWelcomeScreen.header().clickLoginBtn();
loginPage.form().login(testUser);
personalInfoPage.navigateTo();
}
}

View file

@ -1,192 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import org.keycloak.testsuite.ui.account3.page.WelcomeScreen;
import org.keycloak.testsuite.ui.account3.page.fragment.AbstractHeader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ReferrerTest extends AbstractAccountTest {
public static final String FAKE_CLIENT_ID = "fake-client-name";
public static final String REFERRER_LINK_TEXT = "Back to " + LOCALE_CLIENT_NAME_LOCALIZED;
public static final String FAKE_CLIENT_URL_CONTEXT = "auth/non-existing-page/";
public static final String FAKE_CLIENT_URL_FRAGMENT = "?foo=bar&bar=foo#anchor";
@Page
private WelcomeScreen welcomeScreen;
@Page
private PersonalInfoPage personalInfoPage;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation testRealm = testRealms.get(0);
ClientRepresentation testClient = new ClientRepresentation();
testClient.setClientId(FAKE_CLIENT_ID);
testClient.setName(LOCALE_CLIENT_NAME);
// Redirect URIs are no longer allowed to contain a fragment, so we
// need the wildcard in order to use fragments in tests
testClient.setRedirectUris(Collections.singletonList(getFakeClientUrl("*")));
testClient.setEnabled(true);
ClientRepresentation rootUrlClient = new ClientRepresentation();
rootUrlClient.setClientId("test-client-with-root-url");
rootUrlClient.setRootUrl(getFakeClientUrl("foo"));
rootUrlClient.setBaseUrl("/bar");
testClient.setRedirectUris(Collections.singletonList(getFakeClientUrl("*")));
rootUrlClient.setEnabled(true);
testRealm.setClients(Arrays.asList(testClient, rootUrlClient));
testRealm.setAccountTheme(LOCALIZED_THEME_PREVIEW); // using localized custom theme for the fake client localized name
}
@Test
// https://issues.redhat.com/browse/KEYCLOAK-17033
// If the referrer is unescaped, this test will throw an exception.
// org.openqa.selenium.UnhandledAlertException: unexpected alert open: {Alert text : XSS}
public void reflectedXSSTest() {
String attackUrl = getFakeClientUrl("'+alert('XSS')+'");
welcomeScreen.navigateTo(FAKE_CLIENT_ID, attackUrl);
welcomeScreen.header().clickLoginBtn();
loginToAccount();
welcomeScreen.clickPersonalInfoLink();
}
@Test
public void loggedInWelcomeScreenTest() {
welcomeScreen.header().clickLoginBtn();
loginToAccount();
welcomeScreen.navigateTo(FAKE_CLIENT_ID, getFakeClientUrl(FAKE_CLIENT_URL_FRAGMENT));
welcomeScreen.header().assertLoginBtnVisible(false);
welcomeScreen.header().assertLogoutBtnVisible(true);
testReferrer(welcomeScreen.header(), true);
}
@Test
public void loggedOutWelcomeScreenTest() {
welcomeScreen.navigateTo(FAKE_CLIENT_ID, getFakeClientUrl(FAKE_CLIENT_URL_FRAGMENT));
welcomeScreen.header().assertLoginBtnVisible(true);
welcomeScreen.header().assertLogoutBtnVisible(false);
testReferrer(welcomeScreen.header(), true);
}
@Test
public void loggedInPageTest() {
welcomeScreen.header().clickLoginBtn();
loginToAccount();
welcomeScreen.navigateTo(FAKE_CLIENT_ID, getFakeClientUrl(FAKE_CLIENT_URL_FRAGMENT));
welcomeScreen.clickPersonalInfoLink();
testReferrer(personalInfoPage.header(), true);
}
@Test
public void loggedOutPageTest() {
welcomeScreen.navigateTo(FAKE_CLIENT_ID, getFakeClientUrl(FAKE_CLIENT_URL_FRAGMENT));
welcomeScreen.clickPersonalInfoLink();
loginToAccount();
testReferrer(personalInfoPage.header(), true);
}
@Test
public void badClientNameTest() {
welcomeScreen.navigateTo(FAKE_CLIENT_ID + "-bad", getFakeClientUrl(FAKE_CLIENT_URL_FRAGMENT));
testReferrer(welcomeScreen.header(), false);
welcomeScreen.navigateTo(FAKE_CLIENT_ID + "-bad", getFakeClientUrl(FAKE_CLIENT_URL_FRAGMENT));
welcomeScreen.clickPersonalInfoLink();
loginToAccount();
testReferrer(personalInfoPage.header(), false);
}
@Test
public void badClientUriTest() {
welcomeScreen.navigateTo(FAKE_CLIENT_ID, getFakeClientUrlWithBadContext());
testReferrer(welcomeScreen.header(), false);
welcomeScreen.navigateTo(FAKE_CLIENT_ID, getFakeClientUrlWithBadContext());
welcomeScreen.clickPersonalInfoLink();
loginToAccount();
testReferrer(personalInfoPage.header(), false);
}
// Issue 16484
@Test
public void loggedInWithoutProvidedReferrerUrl() {
welcomeScreen.header().clickLoginBtn();
loginToAccount();
welcomeScreen.navigateTo("test-client-with-root-url", null);
welcomeScreen.header().assertLoginBtnVisible(false);
welcomeScreen.header().assertLogoutBtnVisible(true);
// referrer_uri parameter was not provided. So it should be set by Keycloak based on the client rootUrl and baseUrl
assertEquals("Back to test-client-with-root-url", welcomeScreen.header().getReferrerLinkText());
welcomeScreen.header().clickReferrerLink();
assertCurrentUrlEquals(getFakeClientUrl("foo/bar"));
}
private void testReferrer(AbstractHeader header, boolean expectReferrerVisible) {
if (expectReferrerVisible) {
assertEquals(REFERRER_LINK_TEXT, header.getReferrerLinkText());
header.clickReferrerLink();
assertCurrentUrlEquals(getFakeClientUrl(FAKE_CLIENT_URL_FRAGMENT));
}
else {
header.assertReferrerLinkVisible(false);
}
}
private String getFakeClientUrl(String suffix) {
// we need to use some page which host exists Firefox is throwing exceptions like crazy if we try to load
// a page on a non-existing host, like e.g. http://non-existing-server/
// also we need to do this here as getAuthServerRoot is not ready when firing this class' constructor
return getAuthServerRoot() + FAKE_CLIENT_URL_CONTEXT + suffix;
}
private String getFakeClientUrlWithBadContext() {
// we need to use some page which host exists Firefox is throwing exceptions like crazy if we try to load
// a page on a non-existing host, like e.g. http://non-existing-server/
// also we need to do this here as getAuthServerRoot is not ready when firing this class' constructor
return getAuthServerRoot() + "bad/" + FAKE_CLIENT_URL_CONTEXT;
}
}

View file

@ -1,110 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.ui.account3.page.DeviceActivityPage;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import java.util.List;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
import static org.keycloak.testsuite.util.WaitUtils.pause;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class SessionTest extends AbstractAccountTest {
public static final int SSO_SESSION_IDLE_TIMEOUT = 10;
public static final int ACCESS_TOKEN_LIFESPAN = 10;
@Page
private PersonalInfoPage personalInfoPage;
@Page
private DeviceActivityPage deviceActivityPage;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation realm = testRealms.get(0);
// in seconds
realm.setSsoSessionIdleTimeout(SSO_SESSION_IDLE_TIMEOUT);
realm.setAccessTokenLifespan(ACCESS_TOKEN_LIFESPAN);
}
@Before
public void beforeSessionTest() {
personalInfoPage.navigateTo();
loginToAccount();
}
@Test
public void reactPageSsoTimeoutTest() {
deviceActivityPage.navigateToUsingSidebar();
deviceActivityPage.assertCurrent();
personalInfoPage.navigateToUsingSidebar();
personalInfoPage.assertCurrent();
waitForSessionToExpire();
deviceActivityPage.navigateToUsingSidebar();
assertCurrentUrlStartsWithLoginUrlOf(accountWelcomeScreen);
}
@Test
public void reactPageAsyncLogoutTest() {
testRealmResource().logoutAll();
deviceActivityPage.navigateToUsingSidebar();
assertCurrentUrlStartsWithLoginUrlOf(accountWelcomeScreen);
}
@Test
public void welcomeScreenSsoTimeoutTest() {
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.header().assertLoginBtnVisible(false);
accountWelcomeScreen.header().assertLogoutBtnVisible(true);
waitForSessionToExpire();
refreshPageAndWaitForLoad();
accountWelcomeScreen.header().assertLoginBtnVisible(true);
accountWelcomeScreen.header().assertLogoutBtnVisible(false);
}
@Test
public void welcomeScreenAsyncLogoutTest() {
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.header().assertLoginBtnVisible(false);
accountWelcomeScreen.header().assertLogoutBtnVisible(true);
testRealmResource().logoutAll();
refreshPageAndWaitForLoad();
accountWelcomeScreen.header().assertLoginBtnVisible(true);
accountWelcomeScreen.header().assertLogoutBtnVisible(false);
}
private void waitForSessionToExpire() {
// +3 to add some toleration
log.info("Waiting for SSO session to expire");
pause((SSO_SESSION_IDLE_TIMEOUT + 3) * 1000);
}
}

View file

@ -1,219 +0,0 @@
/*
* Copyright 2020 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.models.credential.OTPCredentialModel;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.models.utils.Base32;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.testsuite.admin.Users;
import org.keycloak.testsuite.auth.page.login.OTPSetup;
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.SigningInPage;
import org.keycloak.testsuite.ui.account3.page.utils.SigningInPageUtils;
import java.time.LocalDateTime;
import static java.util.Collections.emptyList;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.keycloak.models.UserModel.RequiredAction.CONFIGURE_TOTP;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.ui.account3.page.utils.SigningInPageUtils.assertUserCredential;
import static org.keycloak.testsuite.ui.account3.page.utils.SigningInPageUtils.testSetUpLink;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
import static org.keycloak.testsuite.util.WaitUtils.pause;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class SigningInTest extends BaseAccountPageTest {
public static final String PASSWORD_LABEL = "My password";
@Page
private SigningInPage signingInPage;
@Page
private UpdatePassword updatePasswordPage;
@Page
private OTPSetup otpSetupPage;
private SigningInPage.CredentialType passwordCredentialType;
private SigningInPage.CredentialType otpCredentialType;
private TimeBasedOTP otpGenerator;
@Override
protected AbstractLoggedInPage getAccountPage() {
return signingInPage;
}
@Override
public void setDefaultPageUriParameters() {
super.setDefaultPageUriParameters();
updatePasswordPage.setAuthRealm(TEST);
otpSetupPage.setAuthRealm(TEST);
}
@Before
public void beforeSigningInTest() {
passwordCredentialType = signingInPage.getCredentialType(PasswordCredentialModel.TYPE);
otpCredentialType = signingInPage.getCredentialType(OTPCredentialModel.TYPE);
RealmRepresentation realm = testRealmResource().toRepresentation();
otpGenerator = new TimeBasedOTP(realm.getOtpPolicyAlgorithm(), realm.getOtpPolicyDigits(), realm.getOtpPolicyPeriod(), 0);
}
@Test
public void categoriesTest() {
testContext.setTestRealmReps(emptyList()); // reimport realm after this test
assertThat(signingInPage.getCategoriesCount(), is(2));
assertThat(signingInPage.getCategoryTitle("basic-authentication"), is("Basic authentication"));
assertThat(signingInPage.getCategoryTitle("two-factor"), is("Two-factor authentication"));
}
@Test
public void updatePasswordTest() {
SigningInPage.UserCredential passwordCred = passwordCredentialType.getUserCredential(
testUserResource()
.credentials()
.get(0)
.getId()
);
assertThat(passwordCredentialType.isSetUpLinkVisible(), is(false));
assertThat(passwordCredentialType.isSetUp(), is(true));
assertUserCredential(PASSWORD_LABEL, false, passwordCred);
LocalDateTime previousCreatedAt = passwordCred.getCreatedAt();
log.info("Waiting one minute to ensure createdAt will change");
pause(60000);
final String newPwd = "Keycloak is the best!";
passwordCred.clickUpdateBtn();
updatePasswordPage.assertCurrent();
updatePasswordPage.updatePasswords(newPwd, newPwd);
signingInPage.assertCurrent();
assertUserCredential(PASSWORD_LABEL, false, passwordCred);
assertThat(passwordCred.getCreatedAt(), is(not(previousCreatedAt)));
}
@Test
public void updatePasswordTestForUserWithoutPassword() {
// Remove password from the user through admin REST API
String passwordId = testUserResource().credentials().get(0).getId();
testUserResource().removeCredential(passwordId);
// Refresh the page
refreshPageAndWaitForLoad();
// Test user doesn't have password set
assertThat(passwordCredentialType.isSetUpLinkVisible(), is(true));
assertThat(passwordCredentialType.isSetUp(), is(false));
// Set password
passwordCredentialType.clickSetUpLink();
updatePasswordPage.assertCurrent();
String originalPassword = Users.getPasswordOf(testUser);
updatePasswordPage.updatePasswords(originalPassword, originalPassword);
signingInPage.assertCurrent();
// Credential set-up now
assertThat(passwordCredentialType.isSetUpLinkVisible(), is(false));
assertThat(passwordCredentialType.isSetUp(), is(true));
SigningInPage.UserCredential passwordCred =
passwordCredentialType.getUserCredential(testUserResource().credentials().get(0).getId());
assertUserCredential(PASSWORD_LABEL, false, passwordCred);
}
@Test
public void otpTest() {
testContext.setTestRealmReps(emptyList());
assertThat(otpCredentialType.isSetUp(), is(false));
otpCredentialType.clickSetUpLink();
otpSetupPage.cancel();
signingInPage.assertCurrent();
assertThat(otpCredentialType.isSetUp(), is(false));
assertThat(otpCredentialType.getTitle(), is("authenticator application"));
final String label1 = "OTP is secure";
final String label2 = "OTP is inconvenient";
SigningInPage.UserCredential otp1 = addOtpCredential(label1);
assertThat(otpCredentialType.isSetUp(), is(true));
assertThat(otpCredentialType.getUserCredentialsCount(), is(1));
assertUserCredential(label1, true, otp1);
SigningInPage.UserCredential otp2 = addOtpCredential(label2);
assertThat(otpCredentialType.getUserCredentialsCount(), is(2));
assertUserCredential(label2, true, otp2);
assertThat("Set up link is not visible", otpCredentialType.isSetUpLinkVisible(), is(true));
RequiredActionProviderRepresentation requiredAction = new RequiredActionProviderRepresentation();
requiredAction.setEnabled(false);
testRealmResource().flows().updateRequiredAction(CONFIGURE_TOTP.name(), requiredAction);
refreshPageAndWaitForLoad();
assertThat("Set up link for \"otp\" is visible", otpCredentialType.isSetUpLinkVisible(), is(false));
assertThat("Not set up link for \"otp\" is visible", otpCredentialType.isNotSetUpLabelVisible(), is(false));
assertThat("Title for \"otp\" is not visible", otpCredentialType.isTitleVisible(), is(true));
assertThat(otpCredentialType.getUserCredentialsCount(), is(2));
testRemoveCredential(otp1);
}
@Test
public void setUpLinksTest() {
testSetUpLink(testRealmResource(), otpCredentialType, CONFIGURE_TOTP.name());
}
private SigningInPage.UserCredential addOtpCredential(String label) {
otpCredentialType.clickSetUpLink();
otpSetupPage.assertCurrent();
otpSetupPage.clickManualMode();
String secret = new String(Base32.decode(otpSetupPage.getSecretKey()));
String code = otpGenerator.generateTOTP(secret);
otpSetupPage.setTotp(code);
otpSetupPage.setUserLabel(label);
otpSetupPage.submit();
signingInPage.assertCurrent();
return getNewestUserCredential(otpCredentialType);
}
private SigningInPage.UserCredential getNewestUserCredential(SigningInPage.CredentialType credentialType) {
return SigningInPageUtils.getNewestUserCredential(testUserResource(), credentialType);
}
private void testRemoveCredential(SigningInPage.UserCredential userCredential) {
SigningInPageUtils.testRemoveCredential(getAccountPage(), userCredential);
}
}

View file

@ -1,46 +0,0 @@
/*
* Copyright 2019 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.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.testsuite.ui.account3.page.DeviceActivityPage;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
/**
* Basic sanity check for Account Console
*
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class SmokeTest extends AbstractAccountTest {
@Page
private PersonalInfoPage personalInfoPage;
@Page
private DeviceActivityPage deviceActivityPage;
@Test
public void baseFunctionalityTest() {
accountWelcomeScreen.assertCurrent();
accountWelcomeScreen.clickPersonalInfoLink();
loginToAccount();
personalInfoPage.assertCurrent();
deviceActivityPage.navigateToUsingSidebar();
deviceActivityPage.assertCurrent();
}
}

View file

@ -1,164 +0,0 @@
/*
* Copyright 2021 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.ui.account3;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.auth.page.login.UpdateEmailPage;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
@EnableFeature(Profile.Feature.UPDATE_EMAIL)
public class UpdateEmailTest extends BaseAccountPageTest {
@Page
private PersonalInfoPage personalInfoPage;
@Page
private UpdateEmailPage updateEmailPage;
@Rule
public AssertEvents events = new AssertEvents(this);
@Override
protected AbstractLoggedInPage getAccountPage() {
return personalInfoPage;
}
@Before
public void setup() {
enableUpdateEmailRequiredAction();
}
@After
public void clean() {
disableUpdateEmailRequiredAction();
disableRegistration();
}
@Test
public void updateEmailLinkVisibleWithUpdateEmailActionEnabled() {
refreshPageAndWaitForLoad();
personalInfoPage.assertUpdateEmailLinkVisible(true);
}
@Test
public void updateEmailLinkNotVisibleWithoutUpdateEmailActionEnabled() {
disableUpdateEmailRequiredAction();
refreshPageAndWaitForLoad();
personalInfoPage.assertUpdateEmailLinkVisible(false);
}
@Test
public void updateEmailLinkVisibleWithUpdateEmailActionEnabledAndRegistrationEmailAsUsernameAndEditUsernameNotAllowed() {
enableRegistration(true, false);
refreshPageAndWaitForLoad();
personalInfoPage.assertUpdateEmailLinkVisible(false);
}
@Test
public void updateUserInfoWithRegistrationEnabled() {
enableRegistration(false, true);
refreshPageAndWaitForLoad();
assertTrue(personalInfoPage.valuesEqual(testUser));
personalInfoPage.assertSaveDisabled(false);
UserRepresentation newInfo = new UserRepresentation();
newInfo.setUsername(testUser.getUsername());
newInfo.setEmail(testUser.getEmail());
newInfo.setFirstName("New First");
newInfo.setLastName("New Last");
personalInfoPage.setValues(newInfo, true);
assertTrue(personalInfoPage.valuesEqual(newInfo));
personalInfoPage.assertSaveDisabled(false);
personalInfoPage.clickSave();
personalInfoPage.alert().assertSuccess();
personalInfoPage.assertSaveDisabled(false);
personalInfoPage.navigateTo();
personalInfoPage.valuesEqual(newInfo);
}
@Test
public void aiaCancellationSucceeds() {
refreshPageAndWaitForLoad();
personalInfoPage.assertUpdateEmailLinkVisible(true);
personalInfoPage.clickUpdateEmailLink();
Assert.assertTrue(updateEmailPage.isCurrent());
updateEmailPage.clickCancelAIA();
Assert.assertTrue(personalInfoPage.isCurrent());
}
@Test
public void updateEmailSucceeds() {
personalInfoPage.navigateTo();
personalInfoPage.assertUpdateEmailLinkVisible(true);
personalInfoPage.clickUpdateEmailLink();
Assert.assertTrue(updateEmailPage.isCurrent());
updateEmailPage.changeEmail("new-email@example.org");
events.expectAccount(EventType.UPDATE_EMAIL).detail(Details.UPDATED_EMAIL, "new-email@example.org");
Assert.assertEquals("new-email@example.org", testRealmResource().users().get(testUser.getId()).toRepresentation().getEmail());
}
private void disableUpdateEmailRequiredAction() {
RequiredActionProviderRepresentation updateEmail = testRealmResource().flows().getRequiredAction(UserModel.RequiredAction.UPDATE_EMAIL.name());
updateEmail.setEnabled(false);
testRealmResource().flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_EMAIL.name(), updateEmail);
}
private void enableUpdateEmailRequiredAction() {
RequiredActionProviderRepresentation updateEmail = testRealmResource().flows().getRequiredAction(UserModel.RequiredAction.UPDATE_EMAIL.name());
updateEmail.setEnabled(true);
testRealmResource().flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_EMAIL.name(), updateEmail);
}
private void enableRegistration(boolean emailAsUsername, boolean usernameEditionAllowed) {
RealmRepresentation realmRepresentation = testRealmResource().toRepresentation();
realmRepresentation.setRegistrationAllowed(true);
realmRepresentation.setRegistrationEmailAsUsername(emailAsUsername);
realmRepresentation.setEditUsernameAllowed(usernameEditionAllowed);
testRealmResource().update(realmRepresentation);
}
private void disableRegistration() {
RealmRepresentation realmRepresentation = testRealmResource().toRepresentation();
realmRepresentation.setRegistrationAllowed(false);
realmRepresentation.setRegistrationEmailAsUsername(false);
realmRepresentation.setEditUsernameAllowed(false);
testRealmResource().update(realmRepresentation);
}
}

View file

@ -1,150 +0,0 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.ui.account3.page.ApplicationsPage;
import org.keycloak.testsuite.ui.account3.page.DeviceActivityPage;
import org.keycloak.testsuite.ui.account3.page.LinkedAccountsPage;
import org.keycloak.testsuite.ui.account3.page.MyResourcesPage;
import org.keycloak.testsuite.ui.account3.page.PersonalInfoPage;
import static org.junit.Assert.assertEquals;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class WelcomeScreenTest extends AbstractAccountTest {
@Page
private PersonalInfoPage personalInfoPage;
@Page
private DeviceActivityPage deviceActivityPage;
@Page
private LinkedAccountsPage linkedAccountsPage;
@Page
private ApplicationsPage applicationsPage;
@Page
private MyResourcesPage myResourcesPage;
@Test
public void loginLogoutTest() {
accountWelcomeScreen.assertCurrent();
accountWelcomeScreen.header().assertLogoutBtnVisible(false);
assertEquals("", accountWelcomeScreen.header().getToolbarLoggedInUser());
// login
accountWelcomeScreen.header().clickLoginBtn();
loginToAccount();
accountWelcomeScreen.assertCurrent();
accountWelcomeScreen.header().assertLoginBtnVisible(false);
assertEquals("test user", accountWelcomeScreen.header().getToolbarLoggedInUser());
// try if we're really logged in
personalInfoPage.navigateTo();
personalInfoPage.assertCurrent();
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.assertCurrent();
// logout
accountWelcomeScreen.header().assertLoginBtnVisible(false);
accountWelcomeScreen.header().clickLogoutBtn();
accountWelcomeScreen.assertCurrent();
accountWelcomeScreen.header().assertLogoutBtnVisible(false);
accountWelcomeScreen.header().assertLoginBtnVisible(true);
assertEquals("", accountWelcomeScreen.header().getToolbarLoggedInUser());
personalInfoPage.navigateTo();
assertCurrentUrlStartsWithLoginUrlOf(personalInfoPage);
}
@Test
public void personalInfoTest() {
accountWelcomeScreen.clickPersonalInfoLink();
loginToAccount();
personalInfoPage.assertCurrent();
}
@Test
public void clickLogoTest() {
accountWelcomeScreen.clickLogoImage();
accountWelcomeScreen.assertCurrent();
}
@Test
public void accountSecurityTest() {
// TODO rewrite this! (KEYCLOAK-12105)
// // change password link
// accountWelcomeScreen.accountSecurityCard().clickChangePassword();
// loginToAccount();
// changePasswordPage.assertCurrent();
//
// // authenticator link
// accountWelcomeScreen.navigateTo();
// accountWelcomeScreen.accountSecurityCard().clickAuthenticator();
// authenticatorPage.assertCurrent();
assertEquals("", accountWelcomeScreen.header().getToolbarLoggedInUser());
// device activity link
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.clickDeviceActivityLink();
loginToAccount();
deviceActivityPage.assertCurrent();
// linked accounts nav item (this doesn't test welcome page directly but the sidebar after login)
personalInfoPage.navigateTo();
personalInfoPage.sidebar().assertNavNotPresent(LinkedAccountsPage.LINKED_ACCOUNTS_ID);
// linked accounts link
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.assertLinkedAccountsLinkVisible(false);
// add simple IdP
testRealmResource().identityProviders().create(createIdentityProviderRepresentation("test-idp", "google"));
// test link appeared
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.clickLinkedAccountsLink();
linkedAccountsPage.assertCurrent();
// no need to remove the IdP
}
@Test
public void applicationsTest() {
accountWelcomeScreen.clickApplicationsLink();
loginToAccount();
applicationsPage.assertCurrent();
}
@Test
public void resourcesTest() {
accountWelcomeScreen.assertMyResourcesCardVisible(false);
// set user managed access
RealmRepresentation testRealm = testRealmResource().toRepresentation();
testRealm.setUserManagedAccessAllowed(true);
testRealmResource().update(testRealm);
// test my resources appeared
accountWelcomeScreen.navigateTo();
accountWelcomeScreen.assertMyResourcesCardVisible(true);
accountWelcomeScreen.clickMyResourcesLink();
loginToAccount();
myResourcesPage.assertCurrent();
// no need to disable user managed access
}
}

View file

@ -151,7 +151,6 @@
<profile>
<id>webauthn</id>
<modules>
<module>base-ui</module>
<module>webauthn</module>
</modules>
</profile>

View file

@ -23,11 +23,6 @@
</properties>
<dependencies>
<dependency>
<groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-tests-base-ui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-bom</artifactId>

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page;
package org.keycloak.testsuite.webauthn.pages;
import org.keycloak.testsuite.auth.page.AuthRealm;

View file

@ -15,13 +15,13 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page;
package org.keycloak.testsuite.webauthn.pages;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.ui.account3.page.fragment.ContentAlert;
import org.keycloak.testsuite.ui.account3.page.fragment.ContinueCancelModal;
import org.keycloak.testsuite.ui.account3.page.fragment.LoggedInPageHeader;
import org.keycloak.testsuite.ui.account3.page.fragment.Sidebar;
import org.keycloak.testsuite.webauthn.pages.fragments.ContentAlert;
import org.keycloak.testsuite.webauthn.pages.fragments.ContinueCancelModal;
import org.keycloak.testsuite.webauthn.pages.fragments.LoggedInPageHeader;
import org.keycloak.testsuite.webauthn.pages.fragments.Sidebar;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -53,7 +53,7 @@ public abstract class AbstractLoggedInPage extends AbstractAccountPage {
@FindBy(id = "refresh-page")
private WebElement refreshPageBtn;
@FindBy(id = "brandLink")
private WebElement brandLink;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page;
package org.keycloak.testsuite.webauthn.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
@ -212,8 +212,8 @@ public class SigningInPage extends AbstractLoggedInPage {
String lastCreateAtText = getTextFromItem(CREATED_AT);
return lastCreateAtText
.substring(lastCreatedAtLabel.length(), lastCreateAtText.length() - 1) // remove label, drop last dot
.trim();
.substring(lastCreatedAtLabel.length(), lastCreateAtText.length() - 1) // remove label, drop last dot
.trim();
}
public LocalDateTime getCreatedAt() {

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page.fragment;
package org.keycloak.testsuite.webauthn.pages.fragments;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.openqa.selenium.WebDriver;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page.fragment;
package org.keycloak.testsuite.webauthn.pages.fragments;
import org.openqa.selenium.WebElement;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page.fragment;
package org.keycloak.testsuite.webauthn.pages.fragments;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.By;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page.fragment;
package org.keycloak.testsuite.webauthn.pages.fragments;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.openqa.selenium.WebDriver;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page.fragment;
package org.keycloak.testsuite.webauthn.pages.fragments;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page.fragment;
package org.keycloak.testsuite.webauthn.pages.fragments;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.By;

View file

@ -15,14 +15,14 @@
* limitations under the License.
*/
package org.keycloak.testsuite.ui.account3.page.utils;
package org.keycloak.testsuite.webauthn.utils;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.testsuite.ui.account3.page.AbstractLoggedInPage;
import org.keycloak.testsuite.ui.account3.page.SigningInPage;
import org.keycloak.testsuite.webauthn.pages.AbstractLoggedInPage;
import org.keycloak.testsuite.webauthn.pages.SigningInPage;
import java.time.LocalDateTime;
import java.util.List;

View file

@ -33,8 +33,8 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.page.AbstractPatternFlyAlert;
import org.keycloak.testsuite.ui.account3.page.SigningInPage;
import org.keycloak.testsuite.ui.account3.page.utils.SigningInPageUtils;
import org.keycloak.testsuite.webauthn.pages.SigningInPage;
import org.keycloak.testsuite.webauthn.utils.SigningInPageUtils;
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
import org.keycloak.testsuite.util.FlowUtil;
import org.keycloak.testsuite.webauthn.AbstractWebAuthnVirtualTest;

View file

@ -26,7 +26,7 @@ import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory;
import org.keycloak.models.credential.WebAuthnCredentialModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.testsuite.ui.account3.page.SigningInPage;
import org.keycloak.testsuite.webauthn.pages.SigningInPage;
import org.keycloak.testsuite.webauthn.pages.WebAuthnAuthenticatorsList;
import org.keycloak.theme.DateTimeFormatterUtil;
@ -49,8 +49,8 @@ import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.keycloak.testsuite.ui.account3.page.utils.SigningInPageUtils.assertUserCredential;
import static org.keycloak.testsuite.ui.account3.page.utils.SigningInPageUtils.testSetUpLink;
import static org.keycloak.testsuite.webauthn.utils.SigningInPageUtils.assertUserCredential;
import static org.keycloak.testsuite.webauthn.utils.SigningInPageUtils.testSetUpLink;
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;