Move doLogin to AbstractOAuthClient (#37638)

Closes #37637

Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
Stian Thorgersen 2025-02-26 12:34:03 +01:00 committed by GitHub
parent acb7abc255
commit c22f76867f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
226 changed files with 1197 additions and 3787 deletions

View File

@ -123,7 +123,7 @@
</#list>
</#if>
</head>
<body>
<body data-page-id="account">
<div id="app">
<main class="container">
<div class="keycloak__loading-container">

View File

@ -123,7 +123,7 @@
</#list>
</#if>
</head>
<body>
<body data-page-id="admin">
<div id="app">
<main class="container">
<div class="keycloak__loading-container">

View File

@ -600,6 +600,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
if (!attributes.containsKey("templateName")) {
attributes.put("templateName", templateName);
}
attributes.put("pageId", templateName.substring(0, templateName.length() - 4));
String result = freeMarker.processTemplate(attributes, templateName, theme);
Response.ResponseBuilder builder = Response.status(status == null ? Response.Status.OK : status).type(MediaType.TEXT_HTML_UTF_8_TYPE).language(locale).entity(result);
for (Map.Entry<String, String> entry : httpResponseHeaders.entrySet()) {

View File

@ -188,6 +188,7 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
final var localeBean = new LocaleBean(realm, locale, session.getContext().getUri().getRequestUriBuilder(), messagesBundle);
final var lang = realm.isInternationalizationEnabled() ? localeBean.getCurrentLanguageTag() : Locale.ENGLISH.toLanguageTag();
attributes.put("pageId", "error");
attributes.put("statusCode", responseStatus.getStatusCode());
attributes.put("realm", realm);

View File

@ -47,7 +47,7 @@
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-oauth-nimbus-poc</artifactId>
<artifactId>keycloak-test-framework-oauth</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

View File

@ -72,10 +72,6 @@
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-oauth</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-oauth-nimbus-poc</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-email-server</artifactId>

View File

@ -1,6 +1,5 @@
package org.keycloak.test.examples;
import com.nimbusds.oauth2.sdk.GeneralException;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.Assertions;
@ -11,8 +10,8 @@ import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.mail.MailServer;
import org.keycloak.testframework.mail.annotations.InjectMailServer;
import org.keycloak.testframework.oauth.nimbus.OAuthClient;
import org.keycloak.testframework.oauth.nimbus.annotations.InjectOAuthClient;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.ManagedUser;
import org.keycloak.testframework.realm.RealmConfig;
@ -20,7 +19,6 @@ import org.keycloak.testframework.realm.RealmConfigBuilder;
import org.keycloak.testframework.realm.UserConfig;
import org.keycloak.testframework.realm.UserConfigBuilder;
import java.io.IOException;
import java.util.Map;
@KeycloakIntegrationTest
@ -39,8 +37,8 @@ public class EmailTest {
OAuthClient oAuthClient;
@Test
public void testEmail() throws GeneralException, IOException, MessagingException {
oAuthClient.resourceOwnerCredentialGrant(user.getUsername(), "invalid");
public void testEmail() throws MessagingException {
oAuthClient.doPasswordGrantRequest(user.getUsername(), "invalid");
Map<String, String> smtpServer = realm.admin().toRepresentation().getSmtpServer();
Assertions.assertEquals("auto@keycloak.org", smtpServer.get("from"));

View File

@ -1,6 +1,5 @@
package org.keycloak.test.examples;
import com.nimbusds.oauth2.sdk.GeneralException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.events.EventType;
@ -9,14 +8,12 @@ import org.keycloak.testframework.annotations.InjectEvents;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.events.Events;
import org.keycloak.testframework.oauth.nimbus.OAuthClient;
import org.keycloak.testframework.oauth.nimbus.annotations.InjectOAuthClient;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.remote.timeoffset.InjectTimeOffSet;
import org.keycloak.testframework.remote.timeoffset.TimeOffSet;
import java.io.IOException;
@KeycloakIntegrationTest
public class EventsTest {
@ -34,13 +31,13 @@ public class EventsTest {
@Test
public void testFailedLogin() {
oAuthClient.resourceOwnerCredentialGrant("invalid", "invalid");
oAuthClient.doPasswordGrantRequest("invalid", "invalid");
EventRepresentation event = events.poll();
Assertions.assertEquals(EventType.LOGIN_ERROR.name(), event.getType());
Assertions.assertEquals("invalid", event.getDetails().get("username"));
oAuthClient.resourceOwnerCredentialGrant("invalid2", "invalid");
oAuthClient.doPasswordGrantRequest("invalid2", "invalid");
event = events.poll();
Assertions.assertEquals(EventType.LOGIN_ERROR.name(), event.getType());
@ -48,17 +45,17 @@ public class EventsTest {
}
@Test
public void testTimeOffset() throws GeneralException, IOException {
public void testTimeOffset() {
timeOffSet.set(60);
oAuthClient.clientCredentialGrant();
oAuthClient.doClientCredentialsGrantAccessTokenRequest();
Assertions.assertEquals(EventType.CLIENT_LOGIN.name(), events.poll().getType());
}
@Test
public void testClientLogin() throws GeneralException, IOException {
oAuthClient.clientCredentialGrant();
public void testClientLogin() {
oAuthClient.doClientCredentialsGrantAccessTokenRequest();
Assertions.assertEquals(EventType.CLIENT_LOGIN.name(), events.poll().getType());
}

View File

@ -1,103 +0,0 @@
package org.keycloak.test.examples;
import com.nimbusds.oauth2.sdk.AuthorizationResponse;
import com.nimbusds.oauth2.sdk.TokenIntrospectionResponse;
import com.nimbusds.oauth2.sdk.TokenResponse;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import jakarta.ws.rs.core.Response;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.testframework.oauth.nimbus.annotations.InjectOAuthClient;
import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.oauth.nimbus.OAuthClient;
import org.keycloak.testframework.realm.ManagedUser;
import org.keycloak.testframework.realm.UserConfig;
import org.keycloak.testframework.realm.UserConfigBuilder;
import org.keycloak.testframework.ui.annotations.InjectPage;
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
import org.keycloak.testframework.ui.page.LoginPage;
import org.openqa.selenium.WebDriver;
import java.net.URI;
import java.net.URL;
@KeycloakIntegrationTest
public class NimbusOAuthClientTest {
@InjectUser(config = OAuthUserConfig.class)
ManagedUser user;
@InjectOAuthClient
OAuthClient oAuthClient;
@InjectWebDriver
WebDriver webDriver;
@InjectPage
LoginPage loginPage;
@Test
public void testClientCredentials() throws Exception {
TokenResponse tokenResponse = oAuthClient.clientCredentialGrant();
Assertions.assertTrue(tokenResponse.indicatesSuccess());
Assertions.assertNotNull(tokenResponse.toSuccessResponse().getTokens().getAccessToken());
}
@Test
public void testIntrospection() throws Exception {
AccessToken accessToken = oAuthClient.clientCredentialGrant().toSuccessResponse().getTokens().getAccessToken();
TokenIntrospectionResponse introspectionResponse = oAuthClient.introspection(accessToken);
Assertions.assertTrue(introspectionResponse.indicatesSuccess());
Assertions.assertNotNull(introspectionResponse.toSuccessResponse().getIssuer());
}
@Test
public void testAuthorizationCode() throws Exception {
URL authorizationRequestURL = oAuthClient.authorizationRequest();
webDriver.navigate().to(authorizationRequestURL);
loginPage.fillLogin(user.getUsername(), user.getPassword());
loginPage.submit();
Assertions.assertEquals(1, oAuthClient.getCallbacks().size());
URI callbackUri = oAuthClient.getCallbacks().remove(0);
AuthorizationResponse authorizationResponse = AuthorizationResponse.parse(callbackUri);
Assertions.assertTrue(authorizationResponse.indicatesSuccess());
Assertions.assertNotNull(authorizationResponse.toSuccessResponse().getAuthorizationCode());
TokenResponse tokenResponse = oAuthClient.tokenRequest(authorizationResponse.toSuccessResponse().getAuthorizationCode());
Assertions.assertTrue(tokenResponse.indicatesSuccess());
Assertions.assertNotNull(tokenResponse.toSuccessResponse().getTokens().getAccessToken());
}
@Test
public void testAccessTokenRevocation() throws Exception {
TokenResponse tokenResponse = oAuthClient.clientCredentialGrant();
Assertions.assertTrue(tokenResponse.indicatesSuccess());
Assertions.assertNotNull(tokenResponse.toSuccessResponse().getTokens().getAccessToken());
AccessToken accessToken = tokenResponse.toSuccessResponse().getTokens().getAccessToken();
TokenIntrospectionResponse introspectionResponse = oAuthClient.introspection(accessToken);
Assertions.assertTrue(introspectionResponse.indicatesSuccess());
Assertions.assertNotNull(introspectionResponse.toSuccessResponse().getScope());
Assertions.assertEquals(Response.Status.OK.getStatusCode(), oAuthClient.revokeAccessToken(accessToken).getStatusCode());
introspectionResponse = oAuthClient.introspection(accessToken);
Assertions.assertTrue(introspectionResponse.indicatesSuccess());
Assertions.assertNull(introspectionResponse.toSuccessResponse().getScope());
}
public static class OAuthUserConfig implements UserConfig {
@Override
public UserConfigBuilder configure(UserConfigBuilder user) {
return user.name("First", "Last")
.email("test@local")
.password("password");
}
}
}

View File

@ -2,20 +2,17 @@ package org.keycloak.test.examples;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.testframework.annotations.InjectClient;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ClientConfig;
import org.keycloak.testframework.realm.ClientConfigBuilder;
import org.keycloak.testframework.realm.ManagedClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.ManagedUser;
import org.keycloak.testframework.realm.UserConfig;
import org.keycloak.testframework.realm.UserConfigBuilder;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
import org.keycloak.testsuite.util.oauth.TokenRevocationResponse;
import org.keycloak.testsuite.util.oauth.UserInfoResponse;
@ -23,76 +20,71 @@ import org.keycloak.testsuite.util.oauth.UserInfoResponse;
public class OAuthClientTest {
@InjectOAuthClient
OAuthClient oAuthClient;
OAuthClient oauth;
@InjectRealm
ManagedRealm managedRealm;
@InjectClient(config = OAuthClientConfig.class)
ManagedClient client;
@InjectUser(config = OAuthUserConfig.class)
ManagedUser user;
@Test
public void testConfig() {
Assertions.assertEquals(managedRealm.getName(), oAuthClient.config().getRealm());
Assertions.assertEquals(managedRealm.getBaseUrl() + "/protocol/openid-connect/token", oAuthClient.getEndpoints().getToken());
Assertions.assertEquals(managedRealm.getName(), oauth.config().getRealm());
Assertions.assertEquals(managedRealm.getBaseUrl() + "/protocol/openid-connect/token", oauth.getEndpoints().getToken());
}
@Test
public void testLogin() {
AuthorizationEndpointResponse response = oauth.doLogin(user.getUsername(), user.getPassword());
Assertions.assertTrue(response.isRedirected());
}
@Test
public void testPasswordGrant() {
AccessTokenResponse accessTokenResponse = oAuthClient.doPasswordGrantRequest(user.getUsername(), user.getPassword());
AccessTokenResponse accessTokenResponse = oauth.doPasswordGrantRequest(user.getUsername(), user.getPassword());
Assertions.assertTrue(accessTokenResponse.isSuccess());
accessTokenResponse = oAuthClient.passwordGrantRequest(user.getUsername(), "invalid").send();
accessTokenResponse = oauth.passwordGrantRequest(user.getUsername(), "invalid").send();
Assertions.assertFalse(accessTokenResponse.isSuccess());
Assertions.assertEquals("Invalid user credentials", accessTokenResponse.getErrorDescription());
}
@Test
public void testClientCredential() {
AccessTokenResponse accessTokenResponse = oAuthClient.doClientCredentialsGrantAccessTokenRequest();
AccessTokenResponse accessTokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest();
Assertions.assertTrue(accessTokenResponse.isSuccess());
}
@Test
public void testUserInfo() {
AccessTokenResponse accessTokenResponse = oAuthClient.doPasswordGrantRequest(user.getUsername(), user.getPassword());
AccessTokenResponse accessTokenResponse = oauth.doPasswordGrantRequest(user.getUsername(), user.getPassword());
UserInfoResponse userInfoResponse = oAuthClient.doUserInfoRequest(accessTokenResponse.getAccessToken());
UserInfoResponse userInfoResponse = oauth.doUserInfoRequest(accessTokenResponse.getAccessToken());
Assertions.assertTrue(userInfoResponse.isSuccess());
Assertions.assertEquals(user.getUsername(), userInfoResponse.getUserInfo().getPreferredUsername());
}
@Test
public void testRefresh() {
AccessTokenResponse accessTokenResponse = oAuthClient.doPasswordGrantRequest(user.getUsername(), user.getPassword());
AccessTokenResponse accessTokenResponse = oauth.doPasswordGrantRequest(user.getUsername(), user.getPassword());
AccessTokenResponse refreshResponse = oAuthClient.doRefreshTokenRequest(accessTokenResponse.getRefreshToken());
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken());
Assertions.assertTrue(refreshResponse.isSuccess());
Assertions.assertNotEquals(accessTokenResponse.getAccessToken(), refreshResponse.getAccessToken());
}
@Test
public void testRevocation() {
AccessTokenResponse accessTokenResponse = oAuthClient.doPasswordGrantRequest(user.getUsername(), user.getPassword());
AccessTokenResponse accessTokenResponse = oauth.doPasswordGrantRequest(user.getUsername(), user.getPassword());
TokenRevocationResponse tokenRevocationResponse = oAuthClient.doTokenRevoke(accessTokenResponse.getRefreshToken());
TokenRevocationResponse tokenRevocationResponse = oauth.doTokenRevoke(accessTokenResponse.getRefreshToken());
Assertions.assertTrue(tokenRevocationResponse.isSuccess());
AccessTokenResponse refreshResponse = oAuthClient.doRefreshTokenRequest(accessTokenResponse.getRefreshToken());
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken());
Assertions.assertFalse(refreshResponse.isSuccess());
}
public static class OAuthClientConfig implements ClientConfig {
@Override
public ClientConfigBuilder configure(ClientConfigBuilder client) {
return client.clientId("myclient").secret("mysecret").directAccessGrants().serviceAccount();
}
}
public static class OAuthUserConfig implements UserConfig {
@Override

View File

@ -1,14 +1,22 @@
package org.keycloak.test.examples;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.testframework.ui.annotations.InjectPage;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.ui.annotations.InjectPage;
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
import org.keycloak.testframework.ui.page.LoginPage;
import org.keycloak.testframework.ui.page.WelcomePage;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
@KeycloakIntegrationTest
public class PagesTest {
@InjectWebDriver
WebDriver webDriver;
@InjectPage
WelcomePage welcomePage;
@ -18,8 +26,25 @@ public class PagesTest {
@Test
public void testLoginFromWelcome() {
welcomePage.navigateTo();
loginPage.fillLogin("admin", "admin");
loginPage.submit();
if (welcomePage.isActivePage()) {
welcomePage.fillRegistration("admin", "admin");
welcomePage.submit();
welcomePage.clickOpenAdminConsole();
}
if (webDriver instanceof HtmlUnitDriver) {
String pageId = webDriver.findElement(By.xpath("//body")).getAttribute("data-page-id");
Assertions.assertEquals("admin", pageId);
Assertions.assertTrue(webDriver.getCurrentUrl().endsWith("/admin/master/console/"));
} else {
loginPage.waitForPage();
Assertions.assertTrue(loginPage.isActivePage());
loginPage.fillLogin("admin", "admin");
loginPage.submit();
}
}
}

View File

@ -1,50 +0,0 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-test-framework-parent</artifactId>
<groupId>org.keycloak.testframework</groupId>
<version>999.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-test-framework-oauth-nimbus-poc</artifactId>
<name>Keycloak Test Framework</name>
<packaging>jar</packaging>
<description>Nimbus OAuth PoC extension for Keycloak Test Framework</description>
<properties>
<nimbus-sdk.version>11.13</nimbus-sdk.version>
</properties>
<dependencies>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>${nimbus-sdk.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,15 +0,0 @@
package org.keycloak.testframework.oauth.nimbus;
import org.keycloak.testframework.TestFrameworkExtension;
import org.keycloak.testframework.injection.Supplier;
import java.util.List;
public class NimbusTestFrameworkExtension implements TestFrameworkExtension {
@Override
public List<Supplier<?, ?>> suppliers() {
return List.of(new OAuthClientSupplier());
}
}

View File

@ -1,146 +0,0 @@
package org.keycloak.testframework.oauth.nimbus;
import com.nimbusds.oauth2.sdk.AuthorizationCode;
import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
import com.nimbusds.oauth2.sdk.AuthorizationGrant;
import com.nimbusds.oauth2.sdk.AuthorizationRequest;
import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
import com.nimbusds.oauth2.sdk.GeneralException;
import com.nimbusds.oauth2.sdk.ResourceOwnerPasswordCredentialsGrant;
import com.nimbusds.oauth2.sdk.ResponseType;
import com.nimbusds.oauth2.sdk.TokenIntrospectionRequest;
import com.nimbusds.oauth2.sdk.TokenIntrospectionResponse;
import com.nimbusds.oauth2.sdk.TokenRequest;
import com.nimbusds.oauth2.sdk.TokenResponse;
import com.nimbusds.oauth2.sdk.TokenRevocationRequest;
import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import jakarta.ws.rs.core.Response;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.testframework.realm.ClientConfig;
import org.keycloak.testframework.realm.ClientConfigBuilder;
import org.keycloak.testframework.realm.ManagedClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.util.ApiUtil;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.List;
public class OAuthClient {
private final ManagedRealm realm;
private final ManagedClient client;
private final OAuthCallbackServer callbackServer;
private OIDCProviderMetadata oidcProviderMetadata;
public OAuthClient(ManagedRealm realm, ClientConfig clientConfig) {
this.realm = realm;
this.client = registerClient(clientConfig);
this.callbackServer = new OAuthCallbackServer();
}
private ManagedClient registerClient(ClientConfig clientConfig) {
ClientRepresentation clientRepresentation = clientConfig.configure(ClientConfigBuilder.create()).build();
Response response = realm.admin().clients().create(clientRepresentation);
String id = ApiUtil.handleCreatedResponse(response);
clientRepresentation.setId(id);
return new ManagedClient(clientRepresentation, realm.admin().clients().get(id));
}
public TokenResponse clientCredentialGrant() throws IOException, GeneralException {
AuthorizationGrant clientGrant = new ClientCredentialsGrant();
ClientAuthentication clientAuthentication = getClientAuthentication();
URI tokenEndpoint = getOIDCProviderMetadata().getTokenEndpointURI();
TokenRequest tokenRequest = new TokenRequest(tokenEndpoint, clientAuthentication, clientGrant);
return TokenResponse.parse(tokenRequest.toHTTPRequest().send());
}
public TokenResponse resourceOwnerCredentialGrant(String username, String password) {
try {
ResourceOwnerPasswordCredentialsGrant credentialsGrant = new ResourceOwnerPasswordCredentialsGrant(username, new Secret(password));
ClientAuthentication clientAuthentication = getClientAuthentication();
URI tokenEndpoint = getOIDCProviderMetadata().getTokenEndpointURI();
TokenRequest tokenRequest = new TokenRequest(tokenEndpoint, clientAuthentication, credentialsGrant);
return TokenResponse.parse(tokenRequest.toHTTPRequest().send());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public TokenResponse tokenRequest(AuthorizationCode authorizationCode) throws IOException, GeneralException {
AuthorizationGrant grant = new AuthorizationCodeGrant(authorizationCode, callbackServer.getRedirectionUri());
ClientAuthentication clientAuthentication = getClientAuthentication();
URI tokenEndpoint = getOIDCProviderMetadata().getTokenEndpointURI();
TokenRequest tokenRequest = new TokenRequest(tokenEndpoint, clientAuthentication, grant);
return TokenResponse.parse(tokenRequest.toHTTPRequest().send());
}
public TokenIntrospectionResponse introspection(AccessToken accessToken) throws IOException, GeneralException {
ClientAuthentication clientAuthentication = getClientAuthentication();
URI introspectionEndpoint = getOIDCProviderMetadata().getIntrospectionEndpointURI();
TokenIntrospectionRequest introspectionRequest = new TokenIntrospectionRequest(introspectionEndpoint, clientAuthentication, accessToken);
return TokenIntrospectionResponse.parse(introspectionRequest.toHTTPRequest().send());
}
public HTTPResponse revokeAccessToken(AccessToken token) throws GeneralException, IOException {
URI revocationEndpoint = getOIDCProviderMetadata().getRevocationEndpointURI();
TokenRevocationRequest revocationRequest = new TokenRevocationRequest(revocationEndpoint, getClientAuthentication(), token);
return revocationRequest.toHTTPRequest().send();
}
public URL authorizationRequest() {
try {
URI authorizationEndpoint = getOIDCProviderMetadata().getAuthorizationEndpointURI();
State state = new State();
ClientID clientID = new ClientID(client.getClientId());
AuthorizationRequest authorizationRequest = new AuthorizationRequest.Builder(new ResponseType(ResponseType.Value.CODE), clientID)
.state(state)
.redirectionURI(callbackServer.getRedirectionUri())
.endpointURI(authorizationEndpoint)
.build();
return authorizationRequest.toURI().toURL();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public List<URI> getCallbacks() {
return callbackServer.getCallbacks();
}
public void close() {
client.admin().remove();
callbackServer.close();
}
private ClientAuthentication getClientAuthentication() {
ClientID clientID = new ClientID(client.getClientId());
Secret clientSecret = new Secret(client.getSecret());
return new ClientSecretBasic(clientID, clientSecret);
}
private OIDCProviderMetadata getOIDCProviderMetadata() throws GeneralException, IOException {
if (oidcProviderMetadata == null) {
Issuer issuer = new Issuer(realm.getBaseUrl());
oidcProviderMetadata = OIDCProviderMetadata.resolve(issuer);
}
return oidcProviderMetadata;
}
}

View File

@ -1,35 +0,0 @@
package org.keycloak.testframework.oauth.nimbus;
import org.keycloak.testframework.oauth.nimbus.annotations.InjectOAuthClient;
import org.keycloak.testframework.injection.InstanceContext;
import org.keycloak.testframework.injection.LifeCycle;
import org.keycloak.testframework.injection.RequestedInstance;
import org.keycloak.testframework.injection.Supplier;
import org.keycloak.testframework.injection.SupplierHelpers;
import org.keycloak.testframework.realm.ClientConfig;
import org.keycloak.testframework.realm.ManagedRealm;
public class OAuthClientSupplier implements Supplier<OAuthClient, InjectOAuthClient> {
@Override
public OAuthClient getValue(InstanceContext<OAuthClient, InjectOAuthClient> instanceContext) {
ManagedRealm realm = instanceContext.getDependency(ManagedRealm.class);
ClientConfig clientConfig = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
return new OAuthClient(realm, clientConfig);
}
@Override
public boolean compatible(InstanceContext<OAuthClient, InjectOAuthClient> a, RequestedInstance<OAuthClient, InjectOAuthClient> b) {
return true;
}
@Override
public LifeCycle getDefaultLifecycle() {
return LifeCycle.GLOBAL;
}
@Override
public void close(InstanceContext<OAuthClient, InjectOAuthClient> instanceContext) {
instanceContext.getValue().close();
}
}

View File

@ -1,17 +0,0 @@
package org.keycloak.testframework.oauth.nimbus.annotations;
import org.keycloak.testframework.oauth.nimbus.DefaultOAuthClientConfiguration;
import org.keycloak.testframework.realm.ClientConfig;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectOAuthClient {
Class<? extends ClientConfig> config() default DefaultOAuthClientConfiguration.class;
}

View File

@ -1 +0,0 @@
org.keycloak.testframework.oauth.nimbus.NimbusTestFrameworkExtension

View File

@ -1,4 +1,4 @@
package org.keycloak.testframework.oauth.nimbus;
package org.keycloak.testframework.oauth;
import org.keycloak.testframework.realm.ClientConfig;
import org.keycloak.testframework.realm.ClientConfigBuilder;
@ -7,7 +7,7 @@ public class DefaultOAuthClientConfiguration implements ClientConfig {
@Override
public ClientConfigBuilder configure(ClientConfigBuilder client) {
return client.clientId("test-oauth-client")
return client.clientId("test-app")
.serviceAccount()
.directAccessGrants()
.redirectUris("http://127.0.0.1/callback/oauth")

View File

@ -2,9 +2,11 @@ package org.keycloak.testframework.oauth;
import org.apache.http.impl.client.CloseableHttpClient;
import org.keycloak.OAuth2Constants;
import org.keycloak.testframework.ui.page.LoginPage;
import org.keycloak.testsuite.util.oauth.AbstractOAuthClient;
import org.keycloak.testsuite.util.oauth.OAuthClientConfig;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
public class OAuthClient extends AbstractOAuthClient<OAuthClient> {
@ -15,6 +17,14 @@ public class OAuthClient extends AbstractOAuthClient<OAuthClient> {
.responseType(OAuth2Constants.CODE);
}
@Override
public void fillLoginForm(String username, String password) {
LoginPage loginPage = new LoginPage(driver);
PageFactory.initElements(driver, loginPage);
loginPage.fillLogin(username, password);
loginPage.submit();
}
public void close() {
}

View File

@ -2,15 +2,18 @@ package org.keycloak.testframework.oauth;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.CloseableHttpClient;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.testframework.injection.InstanceContext;
import org.keycloak.testframework.injection.LifeCycle;
import org.keycloak.testframework.injection.RequestedInstance;
import org.keycloak.testframework.injection.StringUtil;
import org.keycloak.testframework.injection.Supplier;
import org.keycloak.testframework.injection.SupplierHelpers;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ManagedClient;
import org.keycloak.testframework.realm.ClientConfig;
import org.keycloak.testframework.realm.ClientConfigBuilder;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.server.KeycloakUrls;
import org.keycloak.testframework.util.ApiUtil;
import org.openqa.selenium.WebDriver;
public class OAuthClientSupplier implements Supplier<OAuthClient, InjectOAuthClient> {
@ -19,24 +22,27 @@ public class OAuthClientSupplier implements Supplier<OAuthClient, InjectOAuthCli
public OAuthClient getValue(InstanceContext<OAuthClient, InjectOAuthClient> instanceContext) {
InjectOAuthClient annotation = instanceContext.getAnnotation();
String clientId = StringUtil.convertEmptyToNull(annotation.clientId());
String clientSecret = StringUtil.convertEmptyToNull(annotation.clientSecret());
KeycloakUrls keycloakUrls = instanceContext.getDependency(KeycloakUrls.class);
CloseableHttpClient httpClient = (CloseableHttpClient) instanceContext.getDependency(HttpClient.class);
WebDriver webDriver = instanceContext.getDependency(WebDriver.class);
TestApp testApp = instanceContext.getDependency(TestApp.class);
ManagedRealm realm = instanceContext.getDependency(ManagedRealm.class, annotation.realmRef());
// ClientConfig clientConfig = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
if ("".equals(annotation.clientId())) {
ManagedClient managedClient = instanceContext.getDependency(ManagedClient.class, annotation.clientRef());
clientId = managedClient.getClientId();
clientSecret = managedClient.getSecret();
}
String redirectUri = testApp.getRedirectionUri().toString();
ClientConfig clientConfig = SupplierHelpers.getInstance(annotation.config());
ClientRepresentation testAppClient = clientConfig.configure(ClientConfigBuilder.create())
.redirectUris(redirectUri)
.build();
String clientId = testAppClient.getClientId();
String clientSecret = testAppClient.getSecret();
ApiUtil.handleCreatedResponse(realm.admin().clients().create(testAppClient));
OAuthClient oAuthClient = new OAuthClient(keycloakUrls.getBase(), httpClient, webDriver);
oAuthClient.config().realm(realm.getName()).client(clientId, clientSecret);
oAuthClient.config().realm(realm.getName()).client(clientId, clientSecret).redirectUri(redirectUri);
return oAuthClient;
}

View File

@ -9,7 +9,7 @@ public class OAuthTestFrameworkExtension implements TestFrameworkExtension {
@Override
public List<Supplier<?, ?>> suppliers() {
return List.of(new OAuthClientSupplier());
return List.of(new OAuthClientSupplier(), new TestAppSupplier());
}
}

View File

@ -1,4 +1,4 @@
package org.keycloak.testframework.oauth.nimbus;
package org.keycloak.testframework.oauth;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
@ -11,13 +11,13 @@ import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
class OAuthCallbackServer {
public class TestApp {
private final HttpServer httpServer;
private final OAuthCallbackHandler callbackHandler;
private final URI redirectionUri;
public OAuthCallbackServer() {
public TestApp() {
this.callbackHandler = new OAuthCallbackHandler();
try {

View File

@ -0,0 +1,25 @@
package org.keycloak.testframework.oauth;
import org.keycloak.testframework.injection.InstanceContext;
import org.keycloak.testframework.injection.LifeCycle;
import org.keycloak.testframework.injection.RequestedInstance;
import org.keycloak.testframework.injection.Supplier;
import org.keycloak.testframework.oauth.annotations.InjectTestApp;
public class TestAppSupplier implements Supplier<TestApp, InjectTestApp> {
@Override
public TestApp getValue(InstanceContext<TestApp, InjectTestApp> instanceContext) {
return new TestApp();
}
@Override
public boolean compatible(InstanceContext<TestApp, InjectTestApp> a, RequestedInstance<TestApp, InjectTestApp> b) {
return true;
}
@Override
public LifeCycle getDefaultLifecycle() {
return LifeCycle.GLOBAL;
}
}

View File

@ -1,5 +1,8 @@
package org.keycloak.testframework.oauth.annotations;
import org.keycloak.testframework.oauth.DefaultOAuthClientConfiguration;
import org.keycloak.testframework.realm.ClientConfig;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -9,10 +12,8 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
public @interface InjectOAuthClient {
Class<? extends ClientConfig> config() default DefaultOAuthClientConfiguration.class;
String realmRef() default "";
String clientRef() default "";
String clientId() default "";
String clientSecret() default "";
}

View File

@ -0,0 +1,12 @@
package org.keycloak.testframework.oauth.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectTestApp {
}

View File

@ -43,7 +43,6 @@
<module>email-server</module>
<module>examples</module>
<module>oauth</module>
<module>oauth-nimbus-poc</module>
<module>remote</module>
<module>remote-providers</module>
<module>ui</module>

View File

@ -1,9 +1,17 @@
package org.keycloak.testframework.ui.page;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.WebDriverWait;
public class AbstractPage {
import java.time.Duration;
public abstract class AbstractPage {
@FindBy(xpath = "//body")
private WebElement body;
protected final WebDriver driver;
@ -12,4 +20,22 @@ public class AbstractPage {
PageFactory.initElements(driver, this);
}
public abstract String getExpectedPageId();
public String getCurrentPageId() {
return body.getAttribute("data-page-id");
}
public void waitForPage() {
try {
new WebDriverWait(driver, Duration.ofSeconds(10)).until(d -> isActivePage());
} catch (RuntimeException e) {
throw new RuntimeException("Waiting for '" + getExpectedPageId() + "', but was '" + getCurrentPageId() + "'");
}
}
public boolean isActivePage() {
return getExpectedPageId().equals(getCurrentPageId());
}
}

View File

@ -28,4 +28,8 @@ public class LoginPage extends AbstractPage {
submitButton.click();
}
@Override
public String getExpectedPageId() {
return "login-login";
}
}

View File

@ -27,6 +27,9 @@ public class WelcomePage extends AbstractPage {
@FindBy(css = ".pf-v5-c-login__main-header-desc")
private WebElement welcomeDescription;
@FindBy(css = ".pf-v5-c-button")
private WebElement openAdminConsoleLink;
public WelcomePage(WebDriver driver) {
super(driver);
}
@ -45,6 +48,10 @@ public class WelcomePage extends AbstractPage {
submitButton.click();
}
public void clickOpenAdminConsole() {
openAdminConsoleLink.click();
}
public String getWelcomeMessage() {
return welcomeMessage.getText();
}
@ -57,4 +64,8 @@ public class WelcomePage extends AbstractPage {
return pageAlert.getText();
}
@Override
public String getExpectedPageId() {
return "welcome";
}
}

View File

@ -78,7 +78,7 @@
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>
<artifactId>keycloak-test-framework-oauth-nimbus-poc</artifactId>
<artifactId>keycloak-test-framework-oauth</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak.testframework</groupId>

View File

@ -27,8 +27,8 @@ import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.mail.MailServer;
import org.keycloak.testframework.mail.annotations.InjectMailServer;
import org.keycloak.testframework.oauth.nimbus.OAuthClient;
import org.keycloak.testframework.oauth.nimbus.annotations.InjectOAuthClient;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.ManagedUser;
import org.keycloak.testframework.realm.RealmConfig;
@ -53,7 +53,7 @@ public class EmailEventListenerTest {
@Test
public void testFailedLoginEmailEvent() throws MessagingException {
oAuthClient.resourceOwnerCredentialGrant(user.getUsername(), "invalid");
oAuthClient.doPasswordGrantRequest(user.getUsername(), "invalid");
mail.waitForIncomingEmail(1);
MimeMessage lastReceivedMessage = mail.getLastReceivedMessage();

View File

@ -1,8 +1,5 @@
package org.keycloak.tests.admin.metric;
import com.nimbusds.oauth2.sdk.AuthorizationResponse;
import com.nimbusds.oauth2.sdk.GeneralException;
import com.nimbusds.oauth2.sdk.TokenResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;
@ -13,8 +10,8 @@ import org.keycloak.testframework.annotations.InjectKeycloakUrls;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.oauth.nimbus.OAuthClient;
import org.keycloak.testframework.oauth.nimbus.annotations.InjectOAuthClient;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.ManagedUser;
import org.keycloak.testframework.realm.UserConfig;
@ -22,14 +19,10 @@ import org.keycloak.testframework.realm.UserConfigBuilder;
import org.keycloak.testframework.server.KeycloakServerConfig;
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
import org.keycloak.testframework.server.KeycloakUrls;
import org.keycloak.testframework.ui.annotations.InjectPage;
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
import org.keycloak.testframework.ui.page.LoginPage;
import org.openqa.selenium.WebDriver;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -51,16 +44,10 @@ public class PasswordValidationMetricCustomTagsTest {
@InjectHttpClient
HttpClient httpClient;
@InjectWebDriver
WebDriver webDriver;
@InjectPage
LoginPage loginPage;
Pattern passValidationRegex = Pattern.compile("keycloak_credentials_password_hashing_validations_total\\{realm=\"([^\"]+)\"} ([.0-9]*)");
@Test
void testValidAndInvalidPasswordValidation() throws GeneralException, IOException {
void testValidAndInvalidPasswordValidation() throws IOException {
runAuthorizationCodeFlow(user.getUsername(), "invalid_password", false);
runAuthorizationCodeFlow(user.getUsername(), user.getPassword(), true);
@ -73,27 +60,14 @@ public class PasswordValidationMetricCustomTagsTest {
Assertions.assertFalse(matcher.find());
}
private void runAuthorizationCodeFlow(String username, String password, boolean success) throws GeneralException, IOException {
URL authorizationRequestURL = oAuthClient.authorizationRequest();
webDriver.navigate().to(authorizationRequestURL);
loginPage.fillLogin(username, password);
loginPage.submit();
private void runAuthorizationCodeFlow(String username, String password, boolean success) {
AuthorizationEndpointResponse authorizationEndpointResponse = oAuthClient.doLogin(username, password);
if (!success) {
Assertions.assertTrue(oAuthClient.getCallbacks().isEmpty());
Assertions.assertFalse(authorizationEndpointResponse.isRedirected());
return;
}
Assertions.assertEquals(1, oAuthClient.getCallbacks().size());
URI callbackUri = oAuthClient.getCallbacks().remove(0);
AuthorizationResponse authorizationResponse = AuthorizationResponse.parse(callbackUri);
Assertions.assertTrue(authorizationResponse.indicatesSuccess());
Assertions.assertNotNull(authorizationResponse.toSuccessResponse().getAuthorizationCode());
TokenResponse tokenResponse = oAuthClient.tokenRequest(authorizationResponse.toSuccessResponse().getAuthorizationCode());
Assertions.assertTrue(tokenResponse.indicatesSuccess());
Assertions.assertNotNull(tokenResponse.toSuccessResponse().getTokens().getAccessToken());
AccessTokenResponse accessTokenResponse = oAuthClient.doAccessTokenRequest(authorizationEndpointResponse.getCode());
Assertions.assertTrue(accessTokenResponse.isSuccess());
}
public static class ServerConfigWithMetrics implements KeycloakServerConfig {

View File

@ -1,8 +1,5 @@
package org.keycloak.tests.admin.metric;
import com.nimbusds.oauth2.sdk.AuthorizationResponse;
import com.nimbusds.oauth2.sdk.GeneralException;
import com.nimbusds.oauth2.sdk.TokenResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;
@ -13,8 +10,8 @@ import org.keycloak.testframework.annotations.InjectKeycloakUrls;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.oauth.nimbus.OAuthClient;
import org.keycloak.testframework.oauth.nimbus.annotations.InjectOAuthClient;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.ManagedUser;
import org.keycloak.testframework.realm.UserConfig;
@ -22,14 +19,12 @@ import org.keycloak.testframework.realm.UserConfigBuilder;
import org.keycloak.testframework.server.KeycloakServerConfig;
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
import org.keycloak.testframework.server.KeycloakUrls;
import org.keycloak.testframework.ui.annotations.InjectPage;
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
import org.keycloak.testframework.ui.page.LoginPage;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
import org.openqa.selenium.WebDriver;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -54,13 +49,10 @@ public class PasswordValidationMetricTest {
@InjectWebDriver
WebDriver webDriver;
@InjectPage
LoginPage loginPage;
Pattern passValidationRegex = Pattern.compile("keycloak_credentials_password_hashing_validations_total\\{algorithm=\"([^\"]+)\",hashing_strength=\"([^\"]+)\",outcome=\"([^\"]+)\",realm=\"([^\"]+)\"} ([.0-9]*)");
@Test
void testValidAndInvalidPasswordValidation() throws GeneralException, IOException {
void testValidAndInvalidPasswordValidation() throws IOException {
runAuthorizationCodeFlow(user.getUsername(), "invalid_password");
webDriver.manage().deleteAllCookies();
runAuthorizationCodeFlow(user.getUsername(), user.getPassword());
@ -89,25 +81,13 @@ public class PasswordValidationMetricTest {
Assertions.assertFalse(matcher.find());
}
private void runAuthorizationCodeFlow(String username, String password) throws GeneralException, IOException {
URL authorizationRequestURL = oAuthClient.authorizationRequest();
webDriver.navigate().to(authorizationRequestURL);
loginPage.fillLogin(username, password);
loginPage.submit();
if (oAuthClient.getCallbacks().isEmpty()) {
return;
private void runAuthorizationCodeFlow(String username, String password) {
AuthorizationEndpointResponse authorizationEndpointResponse = oAuthClient.doLogin(username, password);
if (authorizationEndpointResponse.isRedirected()) {
AccessTokenResponse tokenResponse = oAuthClient.doAccessTokenRequest(authorizationEndpointResponse.getCode());
Assertions.assertTrue(tokenResponse.isSuccess());
Assertions.assertNotNull(tokenResponse.getAccessToken());
}
URI callbackUri = oAuthClient.getCallbacks().remove(0);
AuthorizationResponse authorizationResponse = AuthorizationResponse.parse(callbackUri);
Assertions.assertTrue(authorizationResponse.indicatesSuccess());
Assertions.assertNotNull(authorizationResponse.toSuccessResponse().getAuthorizationCode());
TokenResponse tokenResponse = oAuthClient.tokenRequest(authorizationResponse.toSuccessResponse().getAuthorizationCode());
Assertions.assertTrue(tokenResponse.indicatesSuccess());
Assertions.assertNotNull(tokenResponse.toSuccessResponse().getTokens().getAccessToken());
}
public static class ServerConfigWithMetrics implements KeycloakServerConfig {

View File

@ -5,19 +5,28 @@ import org.openqa.selenium.WebDriver;
import java.util.Map;
public class AbstractOAuthClient<T> {
public abstract class AbstractOAuthClient<T> {
protected String baseUrl;
protected OAuthClientConfig config;
protected Map<String, String> customParameters;
protected String codeChallenge;
protected String codeChallengeMethod;
protected String codeVerifier;
protected String clientSessionState;
protected String clientSessionHost;
protected String dpopJkt;
protected String dpopProof;
protected String request;
protected String requestUri;
protected String claims;
protected String kcAction;
protected String uiLocales;
protected String maxAge;
protected String prompt;
protected StateParamProvider state;
protected String nonce;
protected HttpClientManager httpClientManager;
protected WebDriver driver;
@ -38,6 +47,26 @@ public class AbstractOAuthClient<T> {
return (T) this;
}
public String getLoginFormUrl() {
return new LoginUrlBuilder(this).toString();
}
public void openLoginForm() {
driver.navigate().to(getLoginFormUrl());
}
public AuthorizationEndpointResponse doLogin(String username, String password) {
openLoginForm();
fillLoginForm(username, password);
return parseLoginResponse();
}
public abstract void fillLoginForm(String username, String password);
public AuthorizationEndpointResponse parseLoginResponse() {
return new AuthorizationEndpointResponse(this);
}
public PasswordGrantRequest passwordGrantRequest(String username, String password) {
return new PasswordGrantRequest(username, password, this);
}
@ -120,6 +149,14 @@ public class AbstractOAuthClient<T> {
return clientSessionHost;
}
String getCodeChallenge() {
return codeChallenge;
}
String getCodeChallengeMethod() {
return codeChallengeMethod;
}
String getCodeVerifier() {
return codeVerifier;
}
@ -128,6 +165,10 @@ public class AbstractOAuthClient<T> {
return customParameters;
}
String getDpopJkt() {
return dpopJkt;
}
String getDpopProof() {
return dpopProof;
}
@ -144,4 +185,34 @@ public class AbstractOAuthClient<T> {
return claims;
}
String getKcAction() {
return kcAction;
}
String getUiLocales() {
return uiLocales;
}
public String getState() {
return state != null ? state.getState() : null;
}
String getNonce() {
return nonce;
}
String getMaxAge() {
return maxAge;
}
String getPrompt() {
return prompt;
}
protected interface StateParamProvider {
String getState();
}
}

View File

@ -0,0 +1,34 @@
package org.keycloak.testsuite.util.oauth;
import jakarta.ws.rs.core.UriBuilder;
public abstract class AbstractUrlBuilder {
protected final AbstractOAuthClient<?> client;
protected UriBuilder uriBuilder;
public AbstractUrlBuilder(AbstractOAuthClient<?> client) {
this.client = client;
}
public abstract String getEndpoint();
protected abstract void initRequest();
public void open() {
client.driver.navigate().to(toString());
}
protected void parameter(String name, String value) {
if (value != null) {
uriBuilder.queryParam(name, value);
}
}
public String toString() {
uriBuilder = UriBuilder.fromUri(getEndpoint());
initRequest();
return uriBuilder.build().toString();
}
}

View File

@ -0,0 +1,123 @@
package org.keycloak.testsuite.util.oauth;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.keycloak.OAuth2Constants;
import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.openqa.selenium.WebDriver;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AuthorizationEndpointResponse {
private boolean isRedirected;
private Map<String, String> params;
public AuthorizationEndpointResponse(AbstractOAuthClient<?> client) {
WebDriver driver = client.driver;
String currentUrl = driver.getCurrentUrl();
boolean fragment = isFragment(client);
int parametersIndex = fragment ? currentUrl.indexOf('#') : currentUrl.indexOf('?');
if (parametersIndex != -1) {
String urlWithoutParameters = currentUrl.substring(0, parametersIndex);
String parameters = currentUrl.substring(parametersIndex + 1);
isRedirected = urlWithoutParameters.equals(client.getRedirectUri());
params = new HashMap<>();
URLEncodedUtils.parse(parameters, StandardCharsets.UTF_8)
.stream().filter(p -> p.getValue() != null)
.forEach(p -> params.put(p.getName(), p.getValue()));
}
}
private URI getCurrentUri(WebDriver driver) {
try {
return new URI(driver.getCurrentUrl());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public Map<String, String> getCurrentQuery(WebDriver driver) {
Map<String, String> m = new HashMap<>();
List<NameValuePair> pairs = URLEncodedUtils.parse(getCurrentUri(driver), StandardCharsets.UTF_8);
for (NameValuePair p : pairs) {
m.put(p.getName(), p.getValue());
}
return m;
}
private boolean isFragment(AbstractOAuthClient<?> client) {
try {
OIDCResponseType responseType = OIDCResponseType.parse(client.config().getResponseType());
OIDCResponseMode responseMode = OIDCResponseMode.parse(client.config().getResponseMode(), responseType);
return switch (responseMode) {
case FRAGMENT, FRAGMENT_JWT -> true;
default -> false;
};
} catch (IllegalArgumentException e) {
return false;
}
}
public boolean isRedirected() {
return isRedirected;
}
public String getCode() {
return params.get(OAuth2Constants.CODE);
}
public String getState() {
return params.get(OAuth2Constants.STATE);
}
public String getError() {
return params.get(OAuth2Constants.ERROR);
}
public String getErrorDescription() {
return params.get(OAuth2Constants.ERROR_DESCRIPTION);
}
public String getSessionState() {
return params.get(OAuth2Constants.SESSION_STATE);
}
public String getAccessToken() {
return params.get(OAuth2Constants.ACCESS_TOKEN);
}
public String getIdToken() {
return params.get(OAuth2Constants.ID_TOKEN);
}
public String getTokenType() {
return params.get(OAuth2Constants.TOKEN_TYPE);
}
public String getExpiresIn() {
return params.get(OAuth2Constants.EXPIRES_IN);
}
public String getResponse() {
return params.get(OAuth2Constants.RESPONSE);
}
public String getIssuer() {
return params.get(OAuth2Constants.ISSUER);
}
public String getKcActionStatus() {
return params.get("kc_action_status");
}
}

View File

@ -16,6 +16,14 @@ public class Endpoints {
this.realm = realm;
}
public String getAuthorization() {
return asString(OIDCLoginProtocolService.authUrl(getBase()));
}
public String getRegistration() {
return asString(OIDCLoginProtocolService.registrationsUrl(getBase()));
}
public String getToken() {
return asString(OIDCLoginProtocolService.tokenUrl(getBase()));
}

View File

@ -0,0 +1,48 @@
package org.keycloak.testsuite.util.oauth;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
public class LoginUrlBuilder extends AbstractUrlBuilder {
public LoginUrlBuilder(AbstractOAuthClient<?> client) {
super(client);
}
@Override
public String getEndpoint() {
return client.getEndpoints().getAuthorization();
}
@Override
protected void initRequest() {
parameter(OAuth2Constants.RESPONSE_TYPE, client.config().getResponseType());
parameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM, client.config().getResponseMode());
parameter(OAuth2Constants.CLIENT_ID, client.config().getClientId());
parameter(OAuth2Constants.REDIRECT_URI, client.config().getRedirectUri());
parameter(OAuth2Constants.STATE, client.getState());
parameter(OIDCLoginProtocol.NONCE_PARAM, client.getNonce());
parameter(OAuth2Constants.SCOPE, client.config().getScope());
parameter(OAuth2Constants.CODE_CHALLENGE, client.getCodeChallenge());
parameter(OAuth2Constants.CODE_CHALLENGE_METHOD, client.getCodeChallengeMethod());
parameter(OIDCLoginProtocol.DPOP_JKT, client.getDpopJkt());
parameter(OIDCLoginProtocol.REQUEST_PARAM, client.getRequest());
parameter(OIDCLoginProtocol.REQUEST_URI_PARAM, client.getRequestUri());
parameter(OIDCLoginProtocol.CLAIMS_PARAM, client.getClaims());
parameter(Constants.KC_ACTION, client.getKcAction());
parameter(OAuth2Constants.UI_LOCALES_PARAM, client.getUiLocales());
parameter(OIDCLoginProtocol.MAX_AGE_PARAM, client.getMaxAge());
parameter(OIDCLoginProtocol.PROMPT_PARAM, client.getPrompt());
if (client.getCustomParameters() != null) {
client.getCustomParameters().forEach(this::parameter);
}
}
}

View File

@ -0,0 +1,14 @@
package org.keycloak.testsuite.util.oauth;
public class RegistrationUrlBuilder extends LoginUrlBuilder {
public RegistrationUrlBuilder(AbstractOAuthClient<?> client) {
super(client);
}
@Override
public String getEndpoint() {
return client.getEndpoints().getRegistration();
}
}

View File

@ -42,7 +42,7 @@ public abstract class Login extends LoginBase {
setUriParameter(PROTOCOL, protocol);
}
public String getProtocol() {
private String getProtocol() {
return getUriParameter(PROTOCOL).toString();
}

View File

@ -1,48 +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.auth.page.login;
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 LoginError extends LoginBase {
@FindBy(xpath = "//div[@id='kc-error-message']/p[@class='instruction']")
private WebElement errorMessage;
@FindBy(id = "backToApplication")
private WebElement backToApplicationLink;
public String getErrorMessage() {
return getTextFromElement(errorMessage);
}
public void backToApplication() {
clickLink(backToApplicationLink);
}
@Override
public boolean isCurrent() {
return getTitleText().equals("We are sorry...");
}
}

View File

@ -34,6 +34,7 @@ import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
*
* @author tkyjovsk
*/
@Deprecated
public class LoginForm extends Form {
@Page

View File

@ -1,124 +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.auth.page.login;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.models.UserModel;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class OTPSetup extends RequiredActions {
@Page
private LoginForm.TotpSetupForm form;
@FindBy(id = "kc-totp-secret-qr-code")
private WebElement barcodeImg;
@FindBy(id = "kc-totp-secret-key")
private WebElement secretKey;
@FindBy(id = "mode-manual")
private WebElement manualModeLink;
@FindBy(id = "mode-barcode")
private WebElement barcodeModeLink;
@FindBy(id = "kc-totp-type")
private WebElement otpType;
@FindBy(id = "kc-totp-algorithm")
private WebElement otpAlgorithm;
@FindBy(id = "kc-totp-digits")
private WebElement otpDigits;
@FindBy(id = "kc-totp-period")
private WebElement otpPeriod;
@FindBy(id = "kc-totp-counter")
private WebElement otpCounter;
public void setTotp(String value) {
form.setTotp(value);
}
public boolean isBarcodePresent() {
try {
return barcodeImg.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public String getSecretKey() {
return secretKey.getText().replace(" ", "");
}
public boolean isSecretKeyPresent() {
try {
return secretKey.isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public void clickManualMode() {
clickLink(manualModeLink);
}
public void clickBarcodeMode() {
clickLink(barcodeModeLink);
}
public String getOtpType() {
return otpType.getText();
}
public String getOtpAlgorithm() {
return otpAlgorithm.getText();
}
public String getOtpDigits() {
return otpDigits.getText();
}
public String getOtpPeriod() {
return otpPeriod.getText();
}
public String getOtpCounter() {
return otpCounter.getText();
}
public void setUserLabel(String value) {
form.setUserLabel(value);
}
@Override
public String getActionId() {
return UserModel.RequiredAction.CONFIGURE_TOTP.name();
}
}

View File

@ -83,8 +83,4 @@ public class UpdateEmailPage extends LogoutSessionsPage {
clickLink(submitActionButton);
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
}

View File

@ -1,91 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.testsuite.auth.page.AuthServer;
import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
import org.keycloak.testsuite.console.page.fragment.Menu;
import org.keycloak.testsuite.console.page.fragment.ModalDialog;
import org.keycloak.testsuite.page.PageWithLogOutAction;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import jakarta.ws.rs.core.UriBuilder;
import java.net.URI;
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
/**
*
* @author Petr Mensik
*/
public class AdminConsole extends AuthServer implements PageWithLoginUrl, PageWithLogOutAction {
public static final String ADMIN_REALM = "adminRealm";
public AdminConsole() {
setUriParameter(ADMIN_REALM, MASTER);
}
public AdminConsole setAdminRealm(String adminRealm) {
setUriParameter(ADMIN_REALM, adminRealm);
return this;
}
public String getAdminRealm() {
return getUriParameter(ADMIN_REALM).toString();
}
@Override
public UriBuilder createUriBuilder() {
return super.createUriBuilder().path("admin/{" + ADMIN_REALM + "}/console");
}
@Page
private Menu menu;
@FindBy(xpath = "//div[@class='modal-dialog']")
protected ModalDialog modalDialog;
/**
*
* @return OIDC Login URL for adminRealm parameter
*/
@Override
public URI getOIDCLoginUrl() {
return OIDCLoginProtocolService.authUrl(UriBuilder.fromPath(getAuthRoot()))
.build(getAdminRealm());
}
@FindBy(css = ".btn-danger")
protected WebElement dangerButton;
//@FindByJQuery(".btn-primary:visible")
@FindBy(css = ".btn-primary")
protected WebElement primaryButton;
@FindBy(css = "navbar-brand")
protected WebElement brandLink;
@Override
public void logOut() {
menu.logOut();
}
}

View File

@ -1,65 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page;
import jakarta.ws.rs.core.UriBuilder;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.console.page.AdminConsoleRealm.CONSOLE_REALM;
/**
*
* @author tkyjovsk
*/
public class AdminConsoleCreate extends AdminConsole {
public static final String ENTITY = "entity";
public AdminConsoleCreate() {
setUriParameter(CONSOLE_REALM, TEST);
}
@Override
public UriBuilder createUriBuilder() {
return super.createUriBuilder().path("/");
}
@Override
public String getUriFragment() {
return "/create/{" + ENTITY + "}/{" + CONSOLE_REALM + "}";
}
public AdminConsoleCreate setEntity(String entity) {
setUriParameter(ENTITY, entity);
return this;
}
public String getEntity() {
return getUriParameter(ENTITY).toString();
}
public AdminConsoleCreate setConsoleRealm(String consoleRealm) {
setUriParameter(CONSOLE_REALM, consoleRealm);
return this;
}
public String getConsoleRealm() {
return getUriParameter(CONSOLE_REALM).toString();
}
}

View File

@ -1,228 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.Optional;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
/**
*
* @author tkyjovsk
*/
public class AdminConsoleRealm extends AdminConsoleRealmsRoot {
public static final String CONSOLE_REALM = "consoleRealm";
public AdminConsoleRealm() {
setUriParameter(CONSOLE_REALM, TEST);
}
public AdminConsoleRealm setConsoleRealm(String realm) {
setUriParameter(CONSOLE_REALM, realm);
return this;
}
public String getConsoleRealm() {
return getUriParameter(CONSOLE_REALM).toString();
}
@Override
public String getUriFragment() {
return super.getUriFragment() + "/{" + CONSOLE_REALM + "}";
}
@FindBy(xpath = "//div[./h2[text()='Configure']]")
private ConfigureMenu configureMenu;
public ConfigureMenu configure() {
waitUntilElement(By.xpath("//div[./h2[text()='Configure']]")).is().present();
return configureMenu;
}
public static class ConfigureMenu {
@FindBy(partialLinkText = "Realm Settings")
private WebElement realmSettingsLink;
@FindBy(partialLinkText = "Clients")
private WebElement clientsLink;
@FindBy(partialLinkText = "Client Scopes")
private WebElement clientScopesLink;
@FindBy(partialLinkText = "Roles")
private WebElement rolesLink;
@FindBy(partialLinkText = "Identity Providers")
private WebElement identityProvidersLink;
@FindBy(partialLinkText = "User Federation")
private WebElement userFederationLink;
@FindBy(partialLinkText = "Authentication")
private WebElement authenticationLink;
public void realmSettings() {
navigateToTab(realmSettingsLink);
}
public void clients() {
navigateToTab(clientsLink);
}
public void clientScopesLink() {
navigateToTab(clientScopesLink);
}
public void roles() {
navigateToTab(rolesLink);
}
public void identityProviders() {
navigateToTab(identityProvidersLink);
}
public void userFederation() {
navigateToTab(userFederationLink);
}
public void authentication() {
navigateToTab(authenticationLink);
}
// Elements
public WebElement getRealmSettingsTab() {
return realmSettingsLink;
}
public WebElement getClientsTab() {
return clientsLink;
}
public WebElement getClientScopesTab() {
return clientScopesLink;
}
public WebElement getRolesTab() {
return rolesLink;
}
public WebElement getUserFederationTab() {
return userFederationLink;
}
public WebElement getIdentityProvidersTab() {
return identityProvidersLink;
}
public WebElement getAuthenticationTab() {
return authenticationLink;
}
}
@FindBy(xpath = "//div[./h2[text()='Manage']]")
protected ManageMenu manageMenu;
public ManageMenu manage() {
waitUntilElement(By.xpath("//div[./h2[text()='Manage']]")).is().present();
return manageMenu;
}
public static class ManageMenu {
@FindBy(partialLinkText = "Groups")
private WebElement groupsLink;
@FindBy(partialLinkText = "Users")
private WebElement usersLink;
@FindBy(partialLinkText = "Sessions")
private WebElement sessionsLink;
@FindBy(partialLinkText = "Events")
private WebElement eventsLink;
@FindBy(partialLinkText = "Import")
private WebElement importLink;
@FindBy(partialLinkText = "Export")
private WebElement exportLink;
public void groups() {
navigateToTab(groupsLink);
}
public void users() {
navigateToTab(usersLink);
}
public void sessions() {
navigateToTab(sessionsLink);
}
public void events() {
navigateToTab(eventsLink);
}
public void importTab() {
navigateToTab(importLink);
}
public void exportTab() {
navigateToTab(exportLink);
}
// Elements
public WebElement getGroupsTab() {
return groupsLink;
}
public WebElement getUsersTab() {
return groupsLink;
}
public WebElement getSessionsTab() {
return groupsLink;
}
public WebElement getEventsTab() {
return groupsLink;
}
public WebElement getImportTab() {
return groupsLink;
}
public WebElement getExportTab() {
return groupsLink;
}
}
public static void navigateToTab(WebElement tab) {
if (tab == null) return;
UIUtils.clickLink(tab);
}
public static boolean isTabActive(WebElement tab) {
try {
final WebElement parent = tab != null ? tab.findElement(By.xpath("./..")) : null;
return parent != null && Optional.ofNullable(parent.getAttribute("class"))
.map(f -> f.equals("active"))
.orElse(false);
} catch (NoSuchElementException e) {
return false;
}
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page;
import org.keycloak.testsuite.console.page.fragment.RealmSelector;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import jakarta.ws.rs.core.UriBuilder;
import java.util.List;
/**
*
* @author tkyjovsk
*/
public class AdminConsoleRealmsRoot extends AdminConsole {
@FindBy(xpath = "//tr[@data-ng-repeat='r in realms']//a[contains(@class,'ng-binding')]")
private List<WebElement> realmLinks;
@Override
public UriBuilder createUriBuilder() {
return super.createUriBuilder().path("/");
}
@Override
public String getUriFragment() {
return "/realms";
}
public void clickRealm(String realm) {
boolean linkFound = false;
for (WebElement realmLink : realmLinks) {
if (realmLink.getText().equals(realm)) {
linkFound = true;
realmLink.click();
}
}
if (!linkFound) {
throw new IllegalStateException("A link for realm '" + realm + "' not found on the Realms page.");
}
}
@FindBy(css = "realm-selector")
protected RealmSelector realmSelector;
// public RealmsResource realmsResource() {
// return keycloak.realms();
// }
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page;
import jakarta.ws.rs.core.UriBuilder;
public class ForbiddenPage extends AdminConsole {
@Override
public UriBuilder createUriBuilder() {
return super.createUriBuilder().path("/");
}
@Override
public String getUriFragment() {
return "/forbidden";
}
}

View File

@ -1,124 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.events;
import org.keycloak.testsuite.console.page.fragment.DataTable;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* @author tkyjovsk
* @author mhajas
*/
public class AdminEvents extends Events {
@Override
public String getUriFragment() {
return super.getUriFragment() + "/admin-events";
}
@FindBy(tagName = "table")
private AdminEventsTable table;
public AdminEventsTable table() {
return table;
}
public static class AdminEventsTable extends DataTable {
@FindBy(xpath = "//button[text()[contains(.,'Filter')]]")
private WebElement filterButton;
@FindBy(tagName = "form")
private AdminEventsTableFilterForm filterForm;
public void update() {
clickHeaderButton("Update");
}
public void reset() {
clickHeaderButton("Reset");
}
public void filter() {
filterButton.click();
}
public AdminEventsTableFilterForm filterForm() {
return filterForm;
}
public class AdminEventsTableFilterForm extends Form {
@FindBy(id = "resource")
private WebElement resourcePathInput;
@FindBy(id = "realm")
private WebElement realmInput;
@FindBy(id = "client")
private WebElement clientInput;
@FindBy(id = "user")
private WebElement userInput;
@FindBy(id = "ipAddress")
private WebElement ipAddressInput;
@FindBy(xpath = "//div[@id='s2id_adminEnabledEventOperations']/ul")
private WebElement operationTypesInput;
@FindBy(xpath = "//div[@id='select2-drop']")
private WebElement operationTypesValues;
public void addOperationType(String type) {
operationTypesInput.click();
operationTypesValues.findElement(By.xpath("//div[text() = '" + type + "']")).click();
}
public void removeOperationType(String type) {
operationTypesInput.findElement(By.xpath("//div[text()='" + type + "']/../a")).click();
}
public void setResourcePathInput(String value) {
UIUtils.setTextInputValue(resourcePathInput, value);
}
public void setRealmInput(String value) {
UIUtils.setTextInputValue(realmInput, value);
}
public void setClientInput(String value) {
UIUtils.setTextInputValue(clientInput, value);
}
public void setUserInput(String value) {
UIUtils.setTextInputValue(userInput, value);
}
public void setIpAddressInput(String value) {
UIUtils.setTextInputValue(ipAddressInput, value);
}
}
}
}

View File

@ -1,129 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.events;
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
/**
* @author tkyjovsk
* @author mhajas
*/
public class Config extends Events {
@Override
public String getUriFragment() {
return super.getUriFragment() + "/events-settings";
}
@FindBy(xpath = "//form")
private ConfigForm form;
public ConfigForm form() {
return form;
}
public static class ConfigForm extends Form {
@FindBy(id = "s2id_autogen1")
private WebElement eventListenersInput;
@FindBy(xpath = "//div[@id='s2id_autogen1']/..//select")
private Select eventListenersSelect;
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='enabled']]")
private OnOffSwitch SaveEvents;
@FindBy(xpath = "//div[@id='s2id_enabledEventTypes']//input")
private WebElement savedTypesInput;
@FindBy(xpath = "//div[@id='select2-drop']/ul")
private WebElement savedTypesOptions;
@FindBy(id = "expiration")
private WebElement expirationInput;
@FindBy(name = "expirationUnit")
private Select expirationUnitSelect;
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='adminEventsEnabled']]")
private OnOffSwitch saveAdminEvents;
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='adminEventsDetailsEnabled']]")
private OnOffSwitch includeRepresentation;
@FindBy(xpath = "//button[@data-ng-click='clearEvents()']")
private WebElement clearLoginEventsButton;
@FindBy(xpath = "//button[@data-ng-click='clearAdminEvents()']")
private WebElement clearAdminEventsButton;
public void addEventListener(String listener) {
eventListenersInput.click();
eventListenersSelect.selectByVisibleText(listener);
}
public void removeEventListener(String listener) {
eventListenersInput.findElement(By.xpath("//div[text()='" + listener + "']/../a")).click();
}
public void setSaveEvents(boolean value) {
SaveEvents.setOn(value);
}
public void addSaveType(String type) {
savedTypesInput.click();
savedTypesOptions.findElement(By.xpath("//div[text()='" + type + "']")).click();
}
public void removeSaveType(String type) {
savedTypesInput.findElement(By.xpath("//div[text()='" + type + "']/../a")).click();
}
public void clearLoginEvents() {
clearLoginEventsButton.click();
}
public void setExpiration(String value, String unit) {
expirationUnitSelect.selectByVisibleText(unit);
UIUtils.setTextInputValue(expirationInput, value);
}
public void setSaveAdminEvents(boolean value) {
saveAdminEvents.setOn(value);
}
public void setIncludeRepresentation(boolean value) {
includeRepresentation.setOn(value);
}
public void clearAdminEvents() {
clearAdminEventsButton.click();
}
public void waitForClearEventsButtonPresent() {
waitUntilElement(clearLoginEventsButton).is().present();
}
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.events;
import org.keycloak.testsuite.console.page.AdminConsoleRealm;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
*
* @author tkyjovsk
*/
public class Events extends AdminConsoleRealm {
@Override
public String getUriFragment() {
return super.getUriFragment();
}
@FindBy(linkText = "Login Events")
private WebElement loginEventsTab;
@FindBy(linkText = "Admin Events")
private WebElement adminEventsTab;
@FindBy(linkText = "Config")
private WebElement configTab;
public void loginEvents() {
loginEventsTab.click();
}
public void adminEvents() {
adminEventsTab.click();
}
public void config() {
configTab.click();
}
}

View File

@ -1,101 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.events;
import org.keycloak.testsuite.console.page.fragment.DataTable;
import org.keycloak.testsuite.page.Form;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* @author tkyjovsk
* @author mhajas
*/
public class LoginEvents extends Events {
@Override
public String getUriFragment() {
return super.getUriFragment() + "/events";
}
@FindBy(tagName = "table")
private LoginEventsTable table;
public LoginEventsTable table() {
return table;
}
public static class LoginEventsTable extends DataTable {
@FindBy(xpath = "//button[text()[contains(.,'Filter')]]")
private WebElement filterButton;
@FindBy(tagName = "form")
private LoginEventsTableFilterForm filterForm;
public void update() {
clickHeaderButton("Update");
}
public void reset() {
clickHeaderButton("Reset");
}
public void filter() {
filterButton.click();
}
public LoginEventsTableFilterForm filterForm() {
return filterForm;
}
public class LoginEventsTableFilterForm extends Form {
@FindBy(id = "client")
private WebElement clientInput;
@FindBy(id = "user")
private WebElement userInput;
@FindBy(xpath = "//div[@id='s2id_eventTypes']/ul")
private WebElement eventTypeInput;
@FindBy(xpath = "//div[@id='select2-drop']")
private WebElement eventTypeValues;
public void addEventType(String type) {
eventTypeInput.click();
eventTypeValues.findElement(By.xpath("//div[text()='" + type + "']")).click();
}
public void removeOperationType(String type) {
eventTypeInput.findElement(By.xpath("//div[text()='" + type + "']/../a]")).click();
}
public void setClientInput(String value) {
UIUtils.setTextInputValue(clientInput, value);
}
public void setUserInput(String value) {
UIUtils.setTextInputValue(userInput, value);
}
}
}
}

View File

@ -1,160 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import static org.keycloak.testsuite.util.UIUtils.clickLink;
import static org.keycloak.testsuite.util.WaitUtils.pause;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public abstract class AbstractMultipleSelect2<R> {
@Root
private WebElement root;
@Drone
private WebDriver driver;
@FindBy(xpath = "//input[contains(@class,'select2-focused')]")
private WebElement search;
@FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
private List<WebElement> result;
public void update(Set<R> values) {
Set<R> selection = getSelected();
for (R value : values) {
if (!selection.contains(value)) {
select(value);
}
}
for (R selected : selection) {
boolean isSelected = false;
for (R value : values) {
if (selected.equals(value)) {
isSelected = true;
break;
}
}
if (!isSelected) {
deselect(selected);
}
}
}
public void select(R value) {
pause(500);
root.click();
pause(500);
String id = identity().apply(value);
search.sendKeys(id);
waitForPageToLoad();
if (result.isEmpty()) {
search.sendKeys(Keys.ESCAPE);
return;
}
for (WebElement result : result) {
if (match(result.getText(), id)) {
clickLink(result);
return;
}
}
}
protected abstract Function<R, String> identity();
protected boolean match(String result, String search) {
return result != null && result.equalsIgnoreCase(search);
};
public Set<R> getSelected() {
Set<R> values = new HashSet<>();
for (WebElement selected : getSelectedElements()) {
R value = representation().apply(selected);
if (value != null) {
values.add(value);
}
}
return values;
}
protected abstract List<WebElement> getSelectedElements();
protected abstract Function<WebElement, R> representation();
public void deselect(R value) {
onDeselect(value);
}
protected void onDeselect(R value) {
for (WebElement selected : getSelectedElements()) {
if (deselect().apply(selected, value)) {
return;
}
}
}
protected BiFunction<WebElement, R, Boolean> deselect() {
return (selected, value) -> {
WebElement selection = selected.findElements(By.tagName("div")).get(0);
if (identity().apply(value).equals(selection.getText())) {
WebElement element = selected.findElement(By.xpath(".//a[contains(@class,'select2-search-choice-close')]"));
JavascriptExecutor executor = (JavascriptExecutor) driver;
executor.executeScript("arguments[0].click();", element);
pause(500);
return true;
}
return false;
};
}
protected WebElement getRoot() {
return root;
}
protected WebDriver getDriver() {
return driver;
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
/**
*
* @author tkyjovsk
*/
public class Breadcrumb {
public static final String BREADCRUMB_XPATH = "//ol[@class='breadcrumb']";
@FindBy(xpath = "./li[not(contains(@class,'ng-hide'))]/a")
private List<WebElement> items;
public int size() {
return items.size();
}
public WebElement getItem(int index) {
return items.get(index);
}
public WebElement getItemFromEnd(int index) {
return items.get(size() - index - 1);
}
public void clickItemOneLevelUp() {
getItemFromEnd(0).click();
}
}

View File

@ -1,139 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
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.WaitUtils.pause;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
import static org.openqa.selenium.By.tagName;
import static org.openqa.selenium.By.xpath;
/**
*
* @author tkyjovsk
*/
public class DataTable {
@Drone
protected WebDriver driver;
@FindBy(css = "input[class*='search']")
private WebElement searchInput;
@FindBy(css = "div[class='input-group-addon'] i")
private WebElement searchButton;
@FindBy(tagName = "thead")
private WebElement header;
@FindBy(css = "tbody")
private WebElement body;
@FindBy(xpath = "tbody/tr[@class='ng-scope']")
private List<WebElement> rows;
@FindBy(tagName = "tfoot")
private WebElement footer;
public void search(String pattern) {
searchInput.sendKeys(pattern);
clickLink(searchButton);
}
public void clickHeaderButton(String buttonText) {
clickLink(header.findElement(By.xpath(".//button[text()='" + buttonText + "']")));
}
public void clickHeaderLink(String linkText) {
clickLink(header.findElement(By.linkText(linkText)));
}
public WebElement body() {
return body;
}
public WebElement footer() {
return footer;
}
public List<WebElement> rows() {
waitForPageToLoad();
pause(500); // wait a bit to ensure the table is no more changing
return rows;
}
public WebElement getRowByLinkText(String text) {
WebElement row = body.findElement(By.xpath(".//tr[./td/a[text()='" + text + "']]"));
return row;
}
public void clickRowByLinkText(String text) {
clickLink(body.findElement(By.xpath(".//tr/td/a[text()='" + text + "']")));
}
public WebElement getActionButton(WebElement row, String buttonText) {
return row.findElement(xpath(".//td[contains(@class, 'kc-action-cell') and text()='" + buttonText + "']"));
}
public WebElement getActionButton(String rowLinkText, String buttonText) {
return getActionButton(getRowByLinkText(rowLinkText), buttonText);
}
public boolean isActionButtonVisible(String rowLinkText, String buttonText) {
try {
return getActionButton(rowLinkText, buttonText).isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
public void clickRowActionButton(WebElement row, String buttonText) {
clickLink(getActionButton(row, buttonText));
}
public void clickRowActionButton(String rowLinkText, String buttonText) {
clickLink(getActionButton(rowLinkText, buttonText));
}
public String getColumnText(WebElement row, int colIndex) {
return getTextFromElement(row.findElements(tagName("td")).get(colIndex));
}
public String getColumnText(String rowLinkText, int colIndex) {
return getColumnText(getRowByLinkText(rowLinkText), colIndex);
}
public boolean isRowPresent(String rowLinkText) {
try {
return getRowByLinkText(rowLinkText).isDisplayed();
}
catch (NoSuchElementException e) {
return false;
}
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
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.UIUtils.getTextInputValue;
/**
*
* @author tkyjovsk
*/
public class InputList {
@FindBy(xpath=".//input[@ng-model='client.redirectUris[i]']")
private List<WebElement> inputs;
public List<String> getValues() {
List<String> values = new ArrayList<>();
for (WebElement input: inputs) {
values.add(getTextInputValue(input));
}
return values;
}
}

View File

@ -1,59 +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.console.page.fragment;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.ElementNotInteractableException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class KcPassword {
@Root
private WebElement inputField;
@FindBy(xpath = "../span[contains(@class,'input-group-addon') and ./span[contains(@class,'fa-eye')]]")
private WebElement eyeButton;
public void setValue(final String value) {
setTextInputValue(inputField, value);
}
public boolean isMasked() {
return inputField.getAttribute("class").contains("password-conceal");
}
public boolean isEyeButtonDisabled() {
return eyeButton.getAttribute("class").contains("disabled");
}
public void clickEyeButton() {
if (isEyeButtonDisabled()) {
throw new ElementNotInteractableException("The eye button is disabled and cannot be clicked");
}
eyeButton.click();
}
public WebElement getElement() {
return inputField;
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
import org.openqa.selenium.By;
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.WaitUtils.waitUntilElement;
/**
*
* @author Petr Mensik
*/
public class Menu {
private static final String MENU_LOCATOR = "ul[class='dropdown-menu']";
@FindBy(css = MENU_LOCATOR)
private List<WebElement> menuList;
@FindBy(css = ".dropdown-toggle")
private List<WebElement> toggle;
public void logOut() {
clickOnMenuElement(MenuType.USER, "Sign Out");
}
public void goToAccountManagement() {
clickOnMenuElement(MenuType.USER, "Manage Account");
}
public void switchRealm(String realmName) {
if (!realmName.equals(getCurrentRealm())) {
clickOnMenuElement(MenuType.REALM, realmName);
}
}
public String getCurrentRealm() {
waitUntilElement(By.cssSelector(MENU_LOCATOR)).is().present();
return toggle.get(1).getText();
}
private void clickOnMenuElement(MenuType menuType, String linkText) {
int menuOrder = 0;
switch (menuType) {
case REALM:
menuOrder = 1;
break;
case USER:
menuOrder = 0;
break;
}
waitUntilElement(By.cssSelector(MENU_LOCATOR)).is().present();
if (!menuList.get(menuOrder).isDisplayed()) {
toggle.get(menuOrder).click();
}
for (WebElement item : menuList.get(menuOrder).findElements(By.cssSelector(MENU_LOCATOR + " a"))) {
if (item.getText().contains(linkText)) {
clickLink(item);
return;
}
}
throw new RuntimeException("Could not find menu item containing \"" + linkText + "\"");
}
private enum MenuType {
USER, REALM
}
}

View File

@ -1,80 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.WaitUtils.waitForModalFadeIn;
import static org.keycloak.testsuite.util.WaitUtils.waitForModalFadeOut;
/**
*
* @author tkyjovsk
*/
public class ModalDialog {
@Root
private WebElement root;
@Drone
private WebDriver driver;
@FindBy(xpath = ".//button[text()='Cancel']")
private WebElement cancelButton;
@FindBy(xpath = ".//button[text()='Delete']")
private WebElement deleteButton;
@FindBy(xpath = ".//button[@ng-click='ok()']")
private WebElement okButton;
@FindBy(id = "name")
private WebElement nameInput;
@FindBy(className = "modal-body")
private WebElement message;
public void ok() {
waitForModalFadeIn();
okButton.click();
waitForModalFadeOut();
}
public void confirmDeletion() {
waitForModalFadeIn();
deleteButton.click();
waitForModalFadeOut();
}
public void cancel() {
waitForModalFadeIn();
cancelButton.click();
waitForModalFadeOut();
}
public void setName(String name) {
nameInput.clear();
nameInput.sendKeys(name);
}
public WebElement getMessage() {
return message;
}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2017 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.console.page.fragment;
import java.util.List;
import java.util.function.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class MultipleStringSelect2 extends AbstractMultipleSelect2<String> {
@Override
protected Function<String, String> identity() {
return r -> r.toString();
}
@Override
protected List<WebElement> getSelectedElements() {
return getRoot().findElements(By.xpath(".//li[contains(@class,'select2-search-choice')]"));
}
@Override
protected Function<WebElement, String> representation() {
return webElement -> {
List<WebElement> element = webElement.findElements(By.tagName("div"));
if (element.isEmpty()) {
return null;
}
String value = element.get(0).getText();
return "".equals(value) ? null : value;
};
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
import org.jboss.arquillian.graphene.fragment.Root;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
/**
*
* @author Petr Mensik
*/
public class PickList {
@Root
private WebElement root;
private Select firstSelect;
private Select secondSelect;
@FindBy(className = "kc-icon-arrow-right")
private WebElement rightArrow;
@FindBy(className = "kc-icon-arrow-left")
private WebElement leftArrow;
public void addItems(String... values) {
for(String value : values) {
firstSelect.selectByVisibleText(value);
}
rightArrow.click();
}
public void setFirstSelect(By locator) {
firstSelect = new Select(root.findElement(locator));
}
public void setSecondSelect(By locator) {
secondSelect = new Select(root.findElement(locator));
}
}

View File

@ -1,28 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
/**
*
* @author tkyjovsk
*/
public class RealmSelector {
// TODO
}

View File

@ -1,64 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.console.page.fragment;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class SingleStringSelect2 extends AbstractMultipleSelect2<String> {
@Override
protected Function<String, String> identity() {
return r -> r.toString();
}
@Override
protected List<WebElement> getSelectedElements() {
return getRoot().findElements(By.xpath(".//span[contains(@class,'select2-chosen')]"));
}
@Override
protected Function<WebElement, String> representation() {
return webElement -> {
String value = webElement.getText();
return "".equals(value) ? null : value;
};
}
@Override
protected BiFunction<WebElement, String, Boolean> deselect() {
return (selected, value) -> {
if (identity().apply(value).equals(selected.getText())) {
WebElement element = selected.findElement(By.xpath(".//a[contains(@class,'select2-search-choice-close')]"));
JavascriptExecutor executor = (JavascriptExecutor) getDriver();
executor.executeScript("arguments[0].click();", element);
WaitUtils.pause(500);
return true;
}
return false;
};
}
}

View File

@ -17,22 +17,23 @@
package org.keycloak.testsuite.page;
import jakarta.ws.rs.core.UriBuilder;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.keycloak.testsuite.util.DroneUtils;
import org.keycloak.testsuite.util.URLUtils;
import org.openqa.selenium.WebDriver;
import jakarta.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
/**
*
* @author tkyjovsk
*/
@Deprecated
public abstract class AbstractPage {
protected final Logger log = Logger.getLogger(this.getClass());

View File

@ -17,10 +17,8 @@
package org.keycloak.testsuite.page;
import org.keycloak.testsuite.util.ServerURLs;
import jakarta.ws.rs.core.UriBuilder;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
@ -28,6 +26,7 @@ import java.net.URL;
*
* @author tkyjovsk
*/
@Deprecated
public abstract class AbstractPageWithInjectedUrl extends AbstractPage {
public abstract URL getInjectedUrl();

View File

@ -42,6 +42,7 @@ import static org.keycloak.testsuite.util.WaitUtils.PAGELOAD_TIMEOUT_MILLIS;
*
* @author tkyjovsk
*/
@Deprecated
public abstract class AbstractPatternFlyAlert {
public static final String ALERT_CLASS_NAME = "pf-v5-c-alert";

View File

@ -31,6 +31,7 @@ import static org.keycloak.testsuite.util.UIUtils.clickLink;
*
* @author tkyjovsk
*/
@Deprecated
public class Form {
protected final Logger log = Logger.getLogger(this.getClass());

View File

@ -1,72 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.page;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.junit.Assert;
import org.keycloak.testsuite.pages.PageUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.UIUtils.getTextFromElement;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class LoginPasswordUpdatePage {
@Drone
protected WebDriver driver;
@FindBy(id = "password-new")
private WebElement newPasswordInput;
@FindBy(id = "password-confirm")
private WebElement passwordConfirmInput;
@FindBy(css = "button[type=\"submit\"]")
private WebElement submitButton;
@FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginErrorMessage;
public void changePassword(String newPassword, String passwordConfirm) {
newPasswordInput.sendKeys(newPassword);
passwordConfirmInput.sendKeys(passwordConfirm);
submitButton.click();
}
public boolean isCurrent() {
return driver.getTitle().equals("Update password");
}
public void assertCurrent() {
Assert.assertEquals("Update password", PageUtils.getPageTitle(driver));
}
public void open() {
throw new UnsupportedOperationException();
}
public String getError() {
return loginErrorMessage != null ? getTextFromElement(loginErrorMessage) : null;
}
}

View File

@ -1,11 +0,0 @@
package org.keycloak.testsuite.page;
/**
*
* @author tkyjovsk
*/
public interface PageWithLogOutAction {
public void logOut();
}

View File

@ -1,95 +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.page;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
*
* @author Petr Mensik
* @author tkyjovsk
*/
public class PatternFlyClosableAlert extends AbstractPatternFlyAlert {
@FindBy(xpath = ".//button[@class='close']")
protected WebElement closeButton;
public boolean isInfo() {
return checkAlertType("info");
}
public boolean isWarning() {
return checkAlertType("warning");
}
public boolean isDanger() {
return checkAlertType("danger");
}
@Override
public void assertSuccess(String expectedText) {
super.assertSuccess(expectedText);
close();
}
public void assertInfo() {
assertInfo(null);
}
public void assertInfo(String expectedText) {
assertDisplayed();
assertTrue("Alert type should be info", isInfo());
if (expectedText != null) assertEquals(expectedText, getText());
close();
}
public void assertWarning() {
assertWarning(null);
}
public void assertWarning(String expectedText) {
assertDisplayed();
assertTrue("Alert type should be warning", isWarning());
if (expectedText != null) assertEquals(expectedText, getText());
close();
}
public void assertDanger() {
assertDanger(null);
}
public void assertDanger(String expectedText) {
assertDisplayed();
assertTrue("Alert type should be danger", isDanger());
if (expectedText != null) assertEquals(expectedText, getText());
close();
}
public void close() {
closeButton.click();
WaitUtils.pause(500);
// Sometimes, when a test is too fast,
// one of the consecutive alerts is not displayed;
// to prevent this we need to slow down a bit
}
}

View File

@ -19,23 +19,14 @@ package org.keycloak.testsuite.pages;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.Assert;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.testsuite.arquillian.SuiteContext;
import org.keycloak.testsuite.util.DroneUtils;
import org.keycloak.testsuite.util.oauth.OAuthClient;
import org.openqa.selenium.WebDriver;
import java.net.URI;
import java.net.URISyntaxException;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public abstract class AbstractPage {
@ArquillianResource
protected SuiteContext suiteContext;
@ArquillianResource
protected WebDriver driver;
@ -44,30 +35,16 @@ public abstract class AbstractPage {
public void assertCurrent() {
String name = getClass().getSimpleName();
Assert.assertTrue("Expected " + name + " but was " + DroneUtils.getCurrentDriver().getTitle() + " (" + DroneUtils.getCurrentDriver().getCurrentUrl() + ")",
Assert.assertTrue("Expected " + name + " but was " + driver.getTitle() + " (" + driver.getCurrentUrl() + ")",
isCurrent());
}
protected URI getAuthServerRoot() {
try {
return KeycloakUriBuilder.fromUri(suiteContext.getAuthServerInfo().getBrowserContextRoot().toURI()).path("/auth/").build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
abstract public boolean isCurrent();
public boolean isCurrent(String expectedTitle) {
return PageUtils.getPageTitle(driver).equals(expectedTitle);
}
abstract public void open() throws Exception;
public WebDriver getDriver() {
return driver;
}
public void setDriver(WebDriver driver) {
this.driver = driver ;
oauth.setDriver(driver);

View File

@ -33,7 +33,6 @@ public class AppPage extends AbstractPage {
@FindBy(id = "account")
private WebElement accountLink;
@Override
public void open() {
DroneUtils.getCurrentDriver().navigate().to(OAuthClient.APP_AUTH_ROOT);
}

View File

@ -48,9 +48,4 @@ public class ConsentPage extends AbstractPage {
public boolean isCurrent(WebDriver driver1) {
return PageUtils.getPageTitle(driver1).contains("Grant Access to ");
}
@Override
public void open() throws Exception {
}
}

View File

@ -53,9 +53,4 @@ public class DeleteCredentialPage extends AbstractPage {
public void assertCredentialInMessage(String expectedLabel) {
Assert.assertEquals("Do you want to delete " + expectedLabel + "?", message.getText());
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
}

View File

@ -53,11 +53,6 @@ public class EmailUpdatePage extends AbstractPage {
return PageUtils.getPageTitle(driver).equals("Update email");
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
public String getEmailError() {
try {
return getTextFromElement(emailError);

View File

@ -50,11 +50,6 @@ public class EnterRecoveryAuthnCodePage extends LanguageComboboxAwarePage {
return true;
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
public String getFeedbackText() {
return feedbackText.getText().trim();
}

View File

@ -56,9 +56,4 @@ public class ErrorPage extends LanguageComboboxAwarePage {
return PageUtils.getPageTitle(driver) != null && PageUtils.getPageTitle(driver).equals("We are sorry...");
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
}

View File

@ -51,8 +51,4 @@ public class IdpConfirmLinkPage extends LanguageComboboxAwarePage {
linkAccountButton.click();
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
}

View File

@ -41,8 +41,4 @@ public class IdpConfirmOverrideLinkPage extends LanguageComboboxAwarePage {
confirmOverrideButton.click();
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
}

View File

@ -39,11 +39,6 @@ public class IdpLinkEmailPage extends AbstractPage {
return PageUtils.getPageTitle(driver).startsWith("Link ");
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
public String getMessage() {
return message.getText();
}

View File

@ -56,11 +56,6 @@ public class InfoPage extends LanguageComboboxAwarePage {
return DroneUtils.getCurrentDriver().getPageSource().contains("kc-info-message");
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
public void clickBackToApplicationLink() {
UIUtils.clickLink(backToApplicationLink);
}

View File

@ -47,12 +47,6 @@ public class InstalledAppRedirectPage extends AbstractPage {
@FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement errorBox;
@Override
public void open() {
throw new UnsupportedOperationException("Use method: open(code, error, errorDescription)");
}
public void open(String realmName, String code, String error, String errorDescription) {
try {
KeycloakUriBuilder kcUriBuilder = KeycloakUriBuilder.fromUri(Urls.realmInstalledAppUrnCallback(new URI(oauth.AUTH_SERVER_ROOT), realmName));

View File

@ -89,10 +89,6 @@ public class LoginConfigTotpPage extends LogoutSessionsPage {
}
}
public void open() {
throw new UnsupportedOperationException();
}
public void clickManual() {
UIUtils.clickLink(manualLink);
}

View File

@ -47,7 +47,4 @@ public class LoginExpiredPage extends AbstractPage {
return PageUtils.getPageTitle(driver).equals("Page has expired");
}
public void open() {
throw new UnsupportedOperationException();
}
}

View File

@ -274,7 +274,6 @@ public class LoginPage extends LanguageComboboxAwarePage {
return rememberMe.isSelected();
}
@Override
public void open() {
oauth.openLoginForm();
assertCurrent();

View File

@ -62,10 +62,6 @@ public class LoginPasswordResetPage extends LanguageComboboxAwarePage {
return PageUtils.getPageTitle(driver).equals("Forgot Your Password?");
}
public void open() {
throw new UnsupportedOperationException();
}
public String getSuccessMessage() {
return emailSuccessMessage != null ? emailSuccessMessage.getText() : null;
}

View File

@ -59,10 +59,6 @@ public class LoginPasswordUpdatePage extends LogoutSessionsPage {
return PageUtils.getPageTitle(driver).equals("Update password");
}
public void open() {
throw new UnsupportedOperationException();
}
public String getError() {
return loginErrorMessage != null ? loginErrorMessage.getText() : null;
}

View File

@ -87,12 +87,6 @@ public class LoginTotpPage extends LanguageComboboxAwarePage {
}
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
// If false, we don't expect that credentials combobox is available. If true, we expect that it is available on the page
public void assertOtpCredentialSelectorAvailability(boolean expectedAvailability) {
try {

View File

@ -46,11 +46,6 @@ public class LoginUpdateProfileEditUsernameAllowedPage extends LoginUpdateProfil
}
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
public static class Update extends LoginUpdateProfilePage.Update {
private final LoginUpdateProfileEditUsernameAllowedPage page;

View File

@ -140,11 +140,6 @@ public class LoginUpdateProfilePage extends AbstractPage {
}
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
public boolean isCancelDisplayed() {
try {
return cancelAIAButton.isDisplayed();

View File

@ -44,12 +44,6 @@ public class LogoutConfirmPage extends LanguageComboboxAwarePage {
return "Logging out".equals(PageUtils.getPageTitle(driver1));
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException("Not supported to directly open logout confirmation page");
}
public void confirmLogout() {
UIUtils.clickLink(confirmLogoutButton);
}

View File

@ -127,7 +127,4 @@ public class OAuth2DeviceVerificationPage extends LanguageComboboxAwarePage {
return false;
}
@Override
public void open() {
}
}

View File

@ -60,10 +60,6 @@ public class OAuthGrantPage extends LanguageComboboxAwarePage {
return PageUtils.getPageTitle(driver).contains("Grant Access to ");
}
@Override
public void open() {
}
public List<String> getDisplayedGrants() {
List<String> table = new ArrayList<>();
WebElement divKcOauth = driver.findElement(By.id("kc-oauth"));

View File

@ -91,9 +91,4 @@ public class PasswordPage extends LanguageComboboxAwarePage {
return true;
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
}

View File

@ -39,11 +39,6 @@ public class ProceedPage extends AbstractPage {
return driver.getPageSource().contains("kc-info-message") && proceedLink.isDisplayed();
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
public void clickProceedLink() {
proceedLink.click();
}

View File

@ -33,11 +33,6 @@ public class PushTheButtonPage extends AbstractPage {
@FindBy(name = "submit1")
private WebElement submitButton;
@Override
public void open() {
throw new UnsupportedOperationException();
}
@Override
public boolean isCurrent() {
return DroneUtils.getCurrentDriver().getTitle().equals("PushTheButton")

View File

@ -276,12 +276,6 @@ public class RegisterPage extends LanguageComboboxAwarePage
return passwordErrors;
}
@Override
public void open() {
oauth.openRegistrationForm();
assertCurrent();
}
public void openWithLoginHint(String loginHint) {
oauth.addCustomParameter(OIDCLoginProtocol.LOGIN_HINT_PARAM, loginHint).openRegistrationForm();
assertCurrent();

View File

@ -17,11 +17,6 @@ public class ResetOtpPage extends AbstractPage {
return description.getText().equals("Which OTP configuration should be removed?");
}
@Override
public void open() throws Exception {
// This page is part of the reset credentials flow, so you shouldn't be able to open it by itself.
}
public void selectOtp(int index) {
driver.findElement(By.id("kc-otp-credential-" + index)).click();
}

View File

@ -94,8 +94,4 @@ public class SelectAuthenticatorPage extends LanguageComboboxAwarePage {
return true;
}
@Override
public void open() throws Exception {
throw new UnsupportedOperationException();
}
}

Some files were not shown because too many files have changed in this diff Show More