mirror of
https://github.com/keycloak/keycloak.git
synced 2026-05-28 04:13:22 -04:00
Move ConsentsTest.java to the new testsuite (#40323)
* Moving files to the new test suite Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com> * Move ConsentsTest.java, UserRoleTest.java to the new testsuite Part of: #34494 Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com> --------- Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com>
This commit is contained in:
parent
7fd3380b19
commit
d7273e6b1f
10 changed files with 541 additions and 694 deletions
|
|
@ -5,7 +5,6 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ClientConfigBuilder {
|
||||
|
|
@ -127,19 +126,22 @@ public class ClientConfigBuilder {
|
|||
}
|
||||
|
||||
public ClientConfigBuilder defaultClientScopes(String... defaultClientScopes) {
|
||||
if (rep.getDefaultClientScopes() == null) {
|
||||
rep.setDefaultClientScopes(new LinkedList<>());
|
||||
}
|
||||
|
||||
rep.getDefaultClientScopes().addAll(List.of(defaultClientScopes));
|
||||
rep.setDefaultClientScopes(Collections.combine(rep.getDefaultClientScopes(), defaultClientScopes));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientConfigBuilder protocolMappers(List<ProtocolMapperRepresentation> mappers) {
|
||||
if (rep.getProtocolMappers() == null) {
|
||||
rep.setProtocolMappers(new LinkedList<>());
|
||||
}
|
||||
rep.getProtocolMappers().addAll(mappers);
|
||||
rep.setProtocolMappers(Collections.combine(rep.getProtocolMappers(), mappers));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientConfigBuilder consentRequired(boolean enabled) {
|
||||
rep.setConsentRequired(enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientConfigBuilder webOrigins(String... webOrigins) {
|
||||
rep.setWebOrigins(Collections.combine(rep.getWebOrigins(), webOrigins));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package org.keycloak.testframework.realm;
|
|||
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RolesRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
|
@ -220,6 +221,11 @@ public class RealmConfigBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public RealmConfigBuilder identityProvider(IdentityProviderRepresentation identityProvider) {
|
||||
rep.addIdentityProvider(identityProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Best practice is to use other convenience methods when configuring a realm, but while the framework is under
|
||||
* active development there may not be a way to perform all updates required. In these cases this method allows
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testframework.ui.page;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class ConsentPage extends AbstractPage {
|
||||
|
||||
@FindBy(id = "kc-login")
|
||||
private WebElement submitButton;
|
||||
|
||||
@FindBy(id = "kc-cancel")
|
||||
private WebElement cancelButton;
|
||||
|
||||
public ConsentPage(WebDriver driver) { super(driver); }
|
||||
|
||||
public void confirm() {
|
||||
submitButton.click();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
cancelButton.click();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpectedPageId() {
|
||||
return "login-login-oauth-grant";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.testframework.ui.page;
|
||||
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
|
@ -28,6 +29,16 @@ public class LoginPage extends AbstractPage {
|
|||
submitButton.click();
|
||||
}
|
||||
|
||||
public void clickSocial(String alias) {
|
||||
WebElement socialButton = findSocialButton(alias);
|
||||
socialButton.click();
|
||||
}
|
||||
|
||||
public WebElement findSocialButton(String alias) {
|
||||
String id = "social-" + alias;
|
||||
return driver.findElement(By.id(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpectedPageId() {
|
||||
return "login-login";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.tests.admin;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectClient;
|
||||
import org.keycloak.testframework.annotations.InjectEvents;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.InjectUser;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.Events;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.oauth.OAuthClient;
|
||||
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
|
||||
import org.keycloak.testframework.realm.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.RealmConfig;
|
||||
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||
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.ConsentPage;
|
||||
import org.keycloak.testframework.ui.page.LoginPage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.keycloak.tests.utils.admin.ApiUtil.findClientByClientId;
|
||||
|
||||
import org.keycloak.testsuite.util.AccountHelper;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@KeycloakIntegrationTest
|
||||
public class ConsentsTest {
|
||||
|
||||
@InjectRealm(ref = "user")
|
||||
ManagedRealm userRealm;
|
||||
|
||||
@InjectRealm(ref = "consumer", config = ConsumerRealmConf.class, lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm consumerRealm;
|
||||
|
||||
@InjectRealm(ref = "provider", lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm providerRealm;
|
||||
|
||||
@InjectUser(ref = "user", realmRef = "user", config = UserRealmUserConf.class)
|
||||
ManagedUser userFromUserRealm;
|
||||
|
||||
@InjectUser(ref = "provider", realmRef = "provider", config = ProviderRealmUserConf.class)
|
||||
ManagedUser userFromProviderRealm;
|
||||
|
||||
@InjectClient(realmRef = "provider", config = ProviderRealmClientConf.class)
|
||||
ManagedClient providerRealmClient;
|
||||
|
||||
@InjectOAuthClient(ref = "user", realmRef = "user")
|
||||
OAuthClient userRealmOAuth;
|
||||
|
||||
@InjectOAuthClient(ref = "consumer", realmRef = "consumer")
|
||||
OAuthClient consumerRealmOAuth;
|
||||
|
||||
@InjectOAuthClient(ref = "provider", realmRef = "provider")
|
||||
OAuthClient providerRealmOAuth;
|
||||
|
||||
@InjectEvents(realmRef = "user")
|
||||
Events userRealmEvents;
|
||||
|
||||
@InjectWebDriver
|
||||
WebDriver driver;
|
||||
|
||||
@InjectPage
|
||||
LoginPage loginPage;
|
||||
|
||||
@InjectPage
|
||||
ConsentPage consentPage;
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ConsentsTest.class);
|
||||
|
||||
private static final String REALM_PROV_NAME = "provider";
|
||||
private static final String REALM_CONS_NAME = "consumer";
|
||||
|
||||
private static final String IDP_OIDC_ALIAS = "kc-oidc-idp";
|
||||
private static final String IDP_OIDC_PROVIDER_ID = "keycloak-oidc";
|
||||
|
||||
private static final String CLIENT_ID = "brokerapp";
|
||||
private static final String CLIENT_SECRET = "secret";
|
||||
|
||||
@Test
|
||||
public void testConsents() {
|
||||
consumerRealmOAuth.openLoginForm();
|
||||
|
||||
LOGGER.debug("Clicking social " + IDP_OIDC_ALIAS);
|
||||
loginPage.clickSocial(IDP_OIDC_ALIAS);
|
||||
|
||||
if (!driver.getCurrentUrl().contains("/realms/" + providerRealm.getName() + "/")) {
|
||||
LOGGER.debug("Not on provider realm page, url: " + driver.getCurrentUrl());
|
||||
}
|
||||
|
||||
Assertions.assertTrue(driver.getCurrentUrl().contains("/realms/" + providerRealm.getName() + "/"), "Driver should be on the provider realm page right now");
|
||||
|
||||
LOGGER.debug("Logging in");
|
||||
loginPage.fillLogin(userFromProviderRealm.getUsername(), userFromProviderRealm.getPassword());
|
||||
loginPage.submit();
|
||||
|
||||
consentPage.waitForPage();
|
||||
consentPage.assertCurrent();
|
||||
consentPage.confirm();
|
||||
|
||||
assertTrue(driver.getPageSource().contains("Happy days"), "Test user should be successfully logged in.");
|
||||
|
||||
UsersResource consumerUsers = consumerRealm.admin().users();
|
||||
Assertions.assertTrue(consumerUsers.count() > 0, "There must be at least one user");
|
||||
|
||||
List<UserRepresentation> users = consumerUsers.search("", 0, 5);
|
||||
|
||||
UserRepresentation foundUser = null;
|
||||
for (UserRepresentation userRep : users) {
|
||||
if (userRep.getUsername().equals(userFromProviderRealm.getUsername()) && userRep.getEmail().equals(userFromProviderRealm.admin().toRepresentation().getEmail())) {
|
||||
foundUser = userRep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assertions.assertNotNull(foundUser, "There must be user " + userFromProviderRealm.getUsername() + " in realm " + consumerRealm.getName());
|
||||
|
||||
// get user with the same username from provider realm
|
||||
users = providerRealm.admin().users().search(null, foundUser.getFirstName(), foundUser.getLastName(), null, 0, 1);
|
||||
Assertions.assertEquals(1, users.size(), "Same user should be in provider realm");
|
||||
|
||||
String userId = users.get(0).getId();
|
||||
UserResource userResource = providerRealm.admin().users().get(userId);
|
||||
|
||||
// list consents
|
||||
List<Map<String, Object>> consents = userResource.getConsents();
|
||||
Assertions.assertEquals(1, consents.size(), "There should be one consent");
|
||||
|
||||
Map<String, Object> consent = consents.get(0);
|
||||
Assertions.assertEquals(CLIENT_ID, consent.get("clientId"), "Consent should be given to " + CLIENT_ID);
|
||||
|
||||
// list sessions. Single client should be in user session
|
||||
List<UserSessionRepresentation> sessions = userResource.getUserSessions();
|
||||
Assertions.assertEquals(1, sessions.size(), "There should be one active session");
|
||||
Assertions.assertEquals(1, sessions.get(0).getClients().size(), "There should be one client in user session");
|
||||
|
||||
// revoke consent
|
||||
userResource.revokeConsent(CLIENT_ID);
|
||||
|
||||
// list consents
|
||||
consents = userResource.getConsents();
|
||||
Assertions.assertEquals(0, consents.size(), "There should be no consents");
|
||||
|
||||
// list sessions
|
||||
sessions = userResource.getUserSessions();
|
||||
Assertions.assertEquals(1, sessions.size(), "There should be one active session");
|
||||
Assertions.assertEquals(0, sessions.get(0).getClients().size(), "There should be no client in user session");
|
||||
|
||||
AccountHelper.logout(providerRealm.admin(), userFromProviderRealm.getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-18954
|
||||
*/
|
||||
@Test
|
||||
public void testRetrieveConsentsForUserWithClientsWithGrantedOfflineAccess() throws Exception {
|
||||
|
||||
RealmRepresentation providerRealmRep = providerRealm.admin().toRepresentation();
|
||||
providerRealmRep.setAccountTheme("keycloak");
|
||||
providerRealm.admin().update(providerRealmRep);
|
||||
providerRealm.admin().clients().create(ClientConfigBuilder.create().clientId("test-app").redirectUris("*").publicClient(true).webOrigins("*").build());
|
||||
|
||||
ClientRepresentation providerAccountRep = providerRealm.admin().clients().findByClientId("test-app").get(0);
|
||||
|
||||
// add offline_scope to default account-console client scope
|
||||
ClientScopeRepresentation offlineAccessScope = providerRealm.admin().getDefaultOptionalClientScopes().stream()
|
||||
.filter(csr -> csr.getName().equals(OAuth2Constants.OFFLINE_ACCESS)).findFirst().get();
|
||||
providerRealm.admin().clients().get(providerAccountRep.getId()).removeOptionalClientScope(offlineAccessScope.getId());
|
||||
providerRealm.admin().clients().get(providerAccountRep.getId()).addDefaultClientScope(offlineAccessScope.getId());
|
||||
|
||||
// enable consent required to explicitly grant offline access
|
||||
providerAccountRep.setConsentRequired(true);
|
||||
providerAccountRep.setDirectAccessGrantsEnabled(true); // for offline token retrieval
|
||||
providerRealm.admin().clients().get(providerAccountRep.getId()).update(providerAccountRep);
|
||||
|
||||
// navigate to account console and login
|
||||
providerRealmOAuth.openLoginForm();
|
||||
|
||||
loginPage.waitForPage();
|
||||
LOGGER.debug("Logging in");
|
||||
loginPage.fillLogin(userFromProviderRealm.getUsername(), userFromProviderRealm.getPassword());
|
||||
loginPage.submit();
|
||||
|
||||
consentPage.waitForPage();
|
||||
LOGGER.debug("Grant consent for offline_access");
|
||||
consentPage.assertCurrent();
|
||||
consentPage.confirm();
|
||||
|
||||
// disable consent required again to enable direct grant token retrieval.
|
||||
providerAccountRep.setConsentRequired(false);
|
||||
providerRealm.admin().clients().get(providerAccountRep.getId()).update(providerAccountRep);
|
||||
|
||||
LOGGER.debug("Obtain offline_token");
|
||||
AccessTokenResponse response = providerRealmOAuth
|
||||
.scope(OAuth2Constants.SCOPE_OPENID +" " + OAuth2Constants.SCOPE_PROFILE + " " + OAuth2Constants.OFFLINE_ACCESS)
|
||||
.doPasswordGrantRequest(userFromProviderRealm.getUsername(), userFromProviderRealm.getPassword());
|
||||
assertNotNull(response.getRefreshToken());
|
||||
|
||||
LOGGER.debug("Check for Offline Token in consents");
|
||||
List<Map<String, Object>> consents = providerRealm.admin().users().get(userFromProviderRealm.getId()).getConsents();
|
||||
assertFalse(consents.isEmpty(), "Consents should not be empty");
|
||||
|
||||
assertTrue(consents.toString().contains("Offline Token"));
|
||||
|
||||
AccountHelper.logout(providerRealm.admin(), userFromProviderRealm.getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsentCancel() {
|
||||
// setup account client to require consent
|
||||
ClientResource accountClient = findClientByClientId(providerRealm.admin(), "test-app");
|
||||
|
||||
ClientRepresentation clientRepresentation = accountClient.toRepresentation();
|
||||
clientRepresentation.setConsentRequired(true);
|
||||
accountClient.update(clientRepresentation);
|
||||
|
||||
// navigate to account console and login
|
||||
providerRealmOAuth.openLoginForm();
|
||||
loginPage.fillLogin(userFromProviderRealm.getUsername(), userFromProviderRealm.getPassword());
|
||||
loginPage.submit();
|
||||
|
||||
consentPage.assertCurrent();
|
||||
consentPage.cancel();
|
||||
|
||||
// check an error page after cancelling the consent
|
||||
assertTrue(driver.getPageSource().contains("Happy days"));
|
||||
assertTrue(driver.getCurrentUrl().contains("error=access_denied"));
|
||||
|
||||
providerRealmOAuth.openLoginForm();
|
||||
loginPage.fillLogin(userFromProviderRealm.getUsername(), userFromProviderRealm.getPassword());
|
||||
loginPage.submit();
|
||||
consentPage.confirm();
|
||||
|
||||
// successful login
|
||||
assertFalse(driver.getCurrentUrl().contains("error"));
|
||||
assertTrue(driver.getPageSource().contains("Happy days"), "Test user should be successfully logged in.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientConsentRequiredAfterLogin() {
|
||||
AuthorizationEndpointResponse response = userRealmOAuth.doLogin(userFromUserRealm.getUsername(), userFromUserRealm.getPassword());
|
||||
AccessTokenResponse accessTokenResponse = userRealmOAuth.doAccessTokenRequest(response.getCode());
|
||||
|
||||
Assertions.assertNotNull(userRealmOAuth.parseLoginResponse().getCode());
|
||||
assertTrue(driver.getPageSource().contains("Happy days"), "Test user should be successfully logged in.");
|
||||
|
||||
EventRepresentation loginEvent = userRealmEvents.poll();
|
||||
Assertions.assertNotNull(loginEvent);
|
||||
Assertions.assertEquals(userFromUserRealm.getId(), loginEvent.getUserId());
|
||||
Assertions.assertEquals(EventType.LOGIN.toString(), loginEvent.getType());
|
||||
loginEvent.getDetails().forEach((key, value) -> {
|
||||
switch (key) {
|
||||
case Details.CODE_ID ->
|
||||
Assertions.assertTrue(isUUID(value));
|
||||
case Details.USERNAME -> Assertions.assertEquals(userFromUserRealm.getUsername(), value);
|
||||
case Details.CONSENT -> Assertions.assertEquals(Details.CONSENT_VALUE_NO_CONSENT_REQUIRED, value);
|
||||
case Details.REDIRECT_URI -> Assertions.assertEquals("http://127.0.0.1:8500/callback/oauth", value);
|
||||
}
|
||||
});
|
||||
|
||||
userRealm.updateWithCleanup(r -> r.enabledEventTypes("REFRESH_TOKEN_ERROR"));
|
||||
String sessionId = loginEvent.getSessionId();
|
||||
|
||||
ClientRepresentation clientRepresentation = userRealm.admin().clients().findByClientId("test-app").get(0);
|
||||
try {
|
||||
clientRepresentation.setConsentRequired(true);
|
||||
userRealm.admin().clients().get(clientRepresentation.getId()).update(clientRepresentation);
|
||||
|
||||
userRealmEvents.clear();
|
||||
|
||||
// try to refresh the token
|
||||
// this fails as client no longer has requested consent from user
|
||||
AccessTokenResponse refreshTokenResponse = userRealmOAuth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken());
|
||||
Assertions.assertEquals(OAuthErrorException.INVALID_SCOPE, refreshTokenResponse.getError());
|
||||
Assertions.assertEquals("Client no longer has requested consent from user", refreshTokenResponse.getErrorDescription());
|
||||
|
||||
EventRepresentation refreshEvent = userRealmEvents.poll();
|
||||
Assertions.assertNotNull(refreshEvent);
|
||||
Assertions.assertNull(refreshEvent.getUserId());
|
||||
Assertions.assertEquals(EventType.REFRESH_TOKEN_ERROR.toString(), refreshEvent.getType());
|
||||
Assertions.assertNull(refreshTokenResponse.getRefreshToken());
|
||||
Assertions.assertEquals(sessionId, refreshEvent.getSessionId());
|
||||
Assertions.assertEquals(Errors.INVALID_TOKEN, refreshEvent.getError());
|
||||
} finally {
|
||||
clientRepresentation.setConsentRequired(false);
|
||||
userRealm.admin().clients().get(clientRepresentation.getId()).update(clientRepresentation);
|
||||
}
|
||||
|
||||
AccountHelper.logout(userRealm.admin(), userFromUserRealm.getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsentWithAdditionalClientAttributes() {
|
||||
// setup account client to require consent
|
||||
ClientResource accountClient = findClientByClientId(providerRealm.admin(), "test-app");
|
||||
|
||||
ClientRepresentation clientRepresentation = accountClient.toRepresentation();
|
||||
clientRepresentation.setConsentRequired(true);
|
||||
clientRepresentation.getAttributes().put(ClientModel.LOGO_URI,"https://www.keycloak.org/resources/images/keycloak_logo_480x108.png");
|
||||
clientRepresentation.getAttributes().put(ClientModel.POLICY_URI,"https://www.keycloak.org/policy");
|
||||
clientRepresentation.getAttributes().put(ClientModel.TOS_URI,"https://www.keycloak.org/tos");
|
||||
accountClient.update(clientRepresentation);
|
||||
|
||||
// navigate to account console and login
|
||||
providerRealmOAuth.openLoginForm();
|
||||
loginPage.fillLogin(userFromProviderRealm.getUsername(), userFromProviderRealm.getPassword());
|
||||
loginPage.submit();
|
||||
|
||||
consentPage.assertCurrent();
|
||||
|
||||
assertTrue(driver.findElement(By.xpath("//img[@src='https://www.keycloak.org/resources/images/keycloak_logo_480x108.png']")).isDisplayed(), "logoUri must be presented");
|
||||
assertTrue(driver.findElement(By.xpath("//a[@href='https://www.keycloak.org/policy']")).isDisplayed(), "policyUri must be presented");
|
||||
assertTrue(driver.findElement(By.xpath("//a[@href='https://www.keycloak.org/tos']")).isDisplayed(), "tosUri must be presented");
|
||||
|
||||
consentPage.confirm();
|
||||
|
||||
// successful login
|
||||
assertTrue(driver.getPageSource().contains("Happy days"), "Test user should be successfully logged in.");
|
||||
AccountHelper.logout(providerRealm.admin(), userFromProviderRealm.getUsername());
|
||||
}
|
||||
|
||||
private static IdentityProviderRepresentation setUpIdentityProvider() {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
|
||||
config.put("clientId", CLIENT_ID);
|
||||
config.put("clientSecret", CLIENT_SECRET);
|
||||
config.put("prompt", "login");
|
||||
config.put("authorizationUrl", "http://localhost:8080/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/auth");
|
||||
config.put("tokenUrl", "http://localhost:8080/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/token");
|
||||
config.put("logoutUrl", "http://localhost:8080/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/logout");
|
||||
config.put("userInfoUrl", "http://localhost:8080/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/userinfo");
|
||||
config.put("defaultScope", "email profile");
|
||||
config.put("backchannelSupported", "true");
|
||||
|
||||
return idp;
|
||||
}
|
||||
|
||||
private static IdentityProviderRepresentation createIdentityProvider(String alias, String providerId) {
|
||||
IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation();
|
||||
|
||||
identityProviderRepresentation.setAlias(alias);
|
||||
identityProviderRepresentation.setDisplayName(providerId);
|
||||
identityProviderRepresentation.setProviderId(providerId);
|
||||
identityProviderRepresentation.setEnabled(true);
|
||||
|
||||
return identityProviderRepresentation;
|
||||
}
|
||||
|
||||
private boolean isUUID(String uuid) {
|
||||
return 36 == uuid.length()
|
||||
&& uuid.charAt(8) == '-'
|
||||
&& uuid.charAt(13) == '-'
|
||||
&& uuid.charAt(18) == '-'
|
||||
&& uuid.charAt(23) == '-';
|
||||
}
|
||||
|
||||
private static class UserRealmUserConf implements UserConfig {
|
||||
|
||||
@Override
|
||||
public UserConfigBuilder configure(UserConfigBuilder builder) {
|
||||
builder.username("user");
|
||||
builder.password("password");
|
||||
builder.email("user@local");
|
||||
builder.emailVerified();
|
||||
builder.name("Local", "User");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProviderRealmUserConf implements UserConfig {
|
||||
|
||||
@Override
|
||||
public UserConfigBuilder configure(UserConfigBuilder builder) {
|
||||
builder.username("provider");
|
||||
builder.password("password");
|
||||
builder.email("provider@local");
|
||||
builder.emailVerified();
|
||||
builder.name("Provider", "User");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProviderRealmClientConf implements ClientConfig {
|
||||
|
||||
@Override
|
||||
public ClientConfigBuilder configure(ClientConfigBuilder builder) {
|
||||
builder.clientId(CLIENT_ID);
|
||||
builder.name(CLIENT_ID);
|
||||
builder.secret(CLIENT_SECRET);
|
||||
builder.consentRequired(true);
|
||||
builder.redirectUris( "http://localhost:8080/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint/*");
|
||||
builder.adminUrl("http://localhost:8080/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConsumerRealmConf implements RealmConfig {
|
||||
|
||||
@Override
|
||||
public RealmConfigBuilder configure(RealmConfigBuilder builder) {
|
||||
builder.identityProvider(setUpIdentityProvider());
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import org.keycloak.events.admin.ResourceType;
|
|||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.MappingsRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
|
|
@ -21,7 +20,6 @@ import org.keycloak.testframework.realm.ManagedRealm;
|
|||
import org.keycloak.testframework.realm.UserConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.events.TestEventsListenerProviderFactory;
|
||||
import org.keycloak.testsuite.util.RoleBuilder;
|
||||
|
||||
import java.util.Collections;
|
||||
|
|
@ -42,10 +40,6 @@ public class UserRoleTest extends AbstractUserTest {
|
|||
@Test
|
||||
public void roleMappings() {
|
||||
RealmResource realm = managedRealm.admin();
|
||||
// Enable events
|
||||
RealmRepresentation realmRep = addTestEventListener(managedRealm.admin().toRepresentation());
|
||||
managedRealm.admin().update(realmRep);
|
||||
|
||||
RoleRepresentation realmCompositeRole = RoleBuilder.create().name("realm-composite").singleAttribute("attribute1", "value1").build();
|
||||
|
||||
realm.roles().create(RoleBuilder.create().name("realm-role").build());
|
||||
|
|
@ -239,16 +233,4 @@ public class UserRoleTest extends AbstractUserTest {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
private RealmRepresentation addTestEventListener(RealmRepresentation rep) {
|
||||
if (rep.getEventsListeners() == null) {
|
||||
rep.setEventsListeners(new LinkedList<String>());
|
||||
}
|
||||
|
||||
if (!rep.getEventsListeners().contains(TestEventsListenerProviderFactory.PROVIDER_ID)) {
|
||||
rep.getEventsListeners().add(TestEventsListenerProviderFactory.PROVIDER_ID);
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.events;
|
||||
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventListenerTransaction;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class TestEventsListenerProvider implements EventListenerProvider {
|
||||
|
||||
private static final BlockingQueue<Event> events = new LinkedBlockingQueue<Event>();
|
||||
private static final BlockingQueue<AdminEvent> adminEvents = new LinkedBlockingQueue<>();
|
||||
private final EventListenerTransaction tx = new EventListenerTransaction((event, includeRepre) -> adminEvents.add(event), events::add);
|
||||
|
||||
public TestEventsListenerProvider(KeycloakSession session) {
|
||||
session.getTransactionManager().enlistAfterCompletion(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
tx.addEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
tx.addAdminEvent(event, includeRepresentation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public static Event poll() {
|
||||
return events.poll();
|
||||
}
|
||||
|
||||
public static AdminEvent pollAdminEvent() {
|
||||
return adminEvents.poll();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
events.clear();
|
||||
}
|
||||
|
||||
public static void clearAdminEvents() {
|
||||
adminEvents.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +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.events;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.events.EventListenerProvider;
|
||||
import org.keycloak.events.EventListenerProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class TestEventsListenerProviderFactory implements EventListenerProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "event-queue";
|
||||
|
||||
@Override
|
||||
public EventListenerProvider create(KeycloakSession session) {
|
||||
return new TestEventsListenerProvider(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +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.
|
||||
#
|
||||
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
org.keycloak.testsuite.events.TestEventsListenerProviderFactory
|
||||
|
|
@ -1,505 +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.admin;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.ConsentPage;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.testsuite.AbstractTestRealmKeycloakTest.TEST_REALM_NAME;
|
||||
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword;
|
||||
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class ConsentsTest extends AbstractKeycloakTest {
|
||||
|
||||
final static String REALM_PROV_NAME = "provider";
|
||||
final static String REALM_CONS_NAME = "consumer";
|
||||
|
||||
final static String IDP_OIDC_ALIAS = "kc-oidc-idp";
|
||||
final static String IDP_OIDC_PROVIDER_ID = "keycloak-oidc";
|
||||
|
||||
final static String CLIENT_ID = "brokerapp";
|
||||
final static String CLIENT_SECRET = "secret";
|
||||
|
||||
final static String USER_LOGIN = "testuser";
|
||||
final static String USER_EMAIL = "user@localhost.com";
|
||||
final static String USER_PASSWORD = "password";
|
||||
final static String USER_FIRSTNAME = "User";
|
||||
final static String USER_LASTNAME = "Tester";
|
||||
|
||||
protected RealmRepresentation createProviderRealm() {
|
||||
RealmRepresentation realm = new RealmRepresentation();
|
||||
realm.setRealm(REALM_PROV_NAME);
|
||||
realm.setEnabled(true);
|
||||
|
||||
return realm;
|
||||
}
|
||||
|
||||
protected RealmRepresentation createConsumerRealm() {
|
||||
RealmRepresentation realm = new RealmRepresentation();
|
||||
realm.setRealm(REALM_CONS_NAME);
|
||||
realm.setEnabled(true);
|
||||
|
||||
return realm;
|
||||
}
|
||||
|
||||
protected List<ClientRepresentation> createProviderClients() {
|
||||
ClientRepresentation client = new ClientRepresentation();
|
||||
client.setClientId(CLIENT_ID);
|
||||
client.setName(CLIENT_ID);
|
||||
client.setSecret(CLIENT_SECRET);
|
||||
client.setEnabled(true);
|
||||
client.setConsentRequired(true);
|
||||
|
||||
client.setRedirectUris(Collections.singletonList(getAuthRoot() +
|
||||
"/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint/*"));
|
||||
|
||||
client.setAdminUrl(getAuthRoot() +
|
||||
"/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint");
|
||||
|
||||
return Collections.singletonList(client);
|
||||
}
|
||||
|
||||
protected IdentityProviderRepresentation setUpIdentityProvider() {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
|
||||
config.put("clientId", CLIENT_ID);
|
||||
config.put("clientSecret", CLIENT_SECRET);
|
||||
config.put("prompt", "login");
|
||||
config.put("authorizationUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/auth");
|
||||
config.put("tokenUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/token");
|
||||
config.put("logoutUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/logout");
|
||||
config.put("userInfoUrl", getAuthRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/userinfo");
|
||||
config.put("defaultScope", "email profile");
|
||||
config.put("backchannelSupported", "true");
|
||||
|
||||
return idp;
|
||||
}
|
||||
|
||||
protected String getUserLogin() {
|
||||
return USER_LOGIN;
|
||||
}
|
||||
|
||||
protected String getUserPassword() {
|
||||
return USER_PASSWORD;
|
||||
}
|
||||
|
||||
protected String getUserEmail() {
|
||||
return USER_EMAIL;
|
||||
}
|
||||
|
||||
protected String getUserFirstName() {
|
||||
return USER_FIRSTNAME;
|
||||
}
|
||||
|
||||
protected String getUserLastName() {
|
||||
return USER_LASTNAME;
|
||||
}
|
||||
protected String providerRealmName() {
|
||||
return REALM_PROV_NAME;
|
||||
}
|
||||
|
||||
protected String consumerRealmName() {
|
||||
return REALM_CONS_NAME;
|
||||
}
|
||||
|
||||
protected String getIDPAlias() {
|
||||
return IDP_OIDC_ALIAS;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Page
|
||||
protected LoginPage accountLoginPage;
|
||||
|
||||
@Page
|
||||
protected ConsentPage consentPage;
|
||||
|
||||
@Page
|
||||
protected AppPage appPage;
|
||||
|
||||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation providerRealm = createProviderRealm();
|
||||
RealmRepresentation consumerRealm = createConsumerRealm();
|
||||
RealmRepresentation realmRepresentation = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
|
||||
|
||||
testRealms.add(providerRealm);
|
||||
testRealms.add(consumerRealm);
|
||||
testRealms.add(realmRepresentation);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void createUser() {
|
||||
log.debug("creating user for realm " + providerRealmName());
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername(getUserLogin());
|
||||
user.setEmail(getUserEmail());
|
||||
user.setFirstName(getUserFirstName());
|
||||
user.setLastName(getUserLastName());
|
||||
user.setEmailVerified(true);
|
||||
user.setEnabled(true);
|
||||
|
||||
RealmResource realmResource = adminClient.realm(providerRealmName());
|
||||
String userId = createUserWithAdminClient(realmResource, user);
|
||||
|
||||
resetUserPassword(realmResource.users().get(userId), getUserPassword(), false);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void addIdentityProviderToProviderRealm() {
|
||||
log.debug("adding identity provider to realm " + consumerRealmName());
|
||||
|
||||
RealmResource realm = adminClient.realm(consumerRealmName());
|
||||
realm.identityProviders().create(setUpIdentityProvider());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void addClients() {
|
||||
List<ClientRepresentation> clients = createProviderClients();
|
||||
if (clients != null) {
|
||||
RealmResource providerRealm = adminClient.realm(providerRealmName());
|
||||
for (ClientRepresentation client : clients) {
|
||||
log.debug("adding client " + client.getName() + " to realm " + providerRealmName());
|
||||
|
||||
providerRealm.clients().create(client);
|
||||
}
|
||||
}
|
||||
createAppClientInRealm(consumerRealmName());
|
||||
}
|
||||
|
||||
protected String getAuthRoot() {
|
||||
return suiteContext.getAuthServerInfo().getContextRoot().toString();
|
||||
}
|
||||
|
||||
protected IdentityProviderRepresentation createIdentityProvider(String alias, String providerId) {
|
||||
IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation();
|
||||
|
||||
identityProviderRepresentation.setAlias(alias);
|
||||
identityProviderRepresentation.setDisplayName(providerId);
|
||||
identityProviderRepresentation.setProviderId(providerId);
|
||||
identityProviderRepresentation.setEnabled(true);
|
||||
|
||||
return identityProviderRepresentation;
|
||||
}
|
||||
|
||||
private void waitForPage(String title) {
|
||||
long startAt = System.currentTimeMillis();
|
||||
|
||||
while (!driver.getTitle().toLowerCase().contains(title)
|
||||
&& System.currentTimeMillis() - startAt < 200) {
|
||||
try {
|
||||
Thread.sleep(5);
|
||||
} catch (InterruptedException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUser() {
|
||||
String userId = adminClient.realm(providerRealmName()).users().search(getUserLogin()).get(0).getId();
|
||||
adminClient.realm(providerRealmName()).users().delete(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsents() {
|
||||
oauth.realm(consumerRealmName());
|
||||
oauth.redirectUri(oauth.SERVER_ROOT + "/auth/realms/" + consumerRealmName() + "/app/auth");
|
||||
oauth.openLoginForm();
|
||||
|
||||
log.debug("Clicking social " + getIDPAlias());
|
||||
accountLoginPage.clickSocial(getIDPAlias());
|
||||
|
||||
if (!driver.getCurrentUrl().contains("/auth/realms/" + providerRealmName() + "/")) {
|
||||
log.debug("Not on provider realm page, url: " + driver.getCurrentUrl());
|
||||
}
|
||||
|
||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + providerRealmName() + "/"));
|
||||
|
||||
log.debug("Logging in");
|
||||
accountLoginPage.login(getUserLogin(), getUserPassword());
|
||||
|
||||
waitForPage("grant access");
|
||||
|
||||
Assert.assertTrue(consentPage.isCurrent());
|
||||
consentPage.confirm();
|
||||
|
||||
Assert.assertTrue("We must be on correct realm right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + consumerRealmName() + "/"));
|
||||
|
||||
UsersResource consumerUsers = adminClient.realm(consumerRealmName()).users();
|
||||
Assert.assertTrue("There must be at least one user", consumerUsers.count() > 0);
|
||||
|
||||
List<UserRepresentation> users = consumerUsers.search("", 0, 5);
|
||||
|
||||
UserRepresentation foundUser = null;
|
||||
for (UserRepresentation user : users) {
|
||||
if (user.getUsername().equals(getUserLogin()) && user.getEmail().equals(getUserEmail())) {
|
||||
foundUser = user;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertNotNull("There must be user " + getUserLogin() + " in realm " + consumerRealmName(),
|
||||
foundUser);
|
||||
|
||||
// get user with the same username from provider realm
|
||||
RealmResource providerRealm = adminClient.realm(providerRealmName());
|
||||
users = providerRealm.users().search(null, foundUser.getFirstName(), foundUser.getLastName(), null, 0, 1);
|
||||
Assert.assertEquals("Same user should be in provider realm", 1, users.size());
|
||||
|
||||
String userId = users.get(0).getId();
|
||||
UserResource userResource = providerRealm.users().get(userId);
|
||||
|
||||
// list consents
|
||||
List<Map<String, Object>> consents = userResource.getConsents();
|
||||
Assert.assertEquals("There should be one consent", 1, consents.size());
|
||||
|
||||
Map<String, Object> consent = consents.get(0);
|
||||
Assert.assertEquals("Consent should be given to " + CLIENT_ID, CLIENT_ID, consent.get("clientId"));
|
||||
|
||||
// list sessions. Single client should be in user session
|
||||
List<UserSessionRepresentation> sessions = userResource.getUserSessions();
|
||||
Assert.assertEquals("There should be one active session", 1, sessions.size());
|
||||
Assert.assertEquals("There should be one client in user session", 1, sessions.get(0).getClients().size());
|
||||
|
||||
// revoke consent
|
||||
userResource.revokeConsent(CLIENT_ID);
|
||||
|
||||
// list consents
|
||||
consents = userResource.getConsents();
|
||||
Assert.assertEquals("There should be no consents", 0, consents.size());
|
||||
|
||||
// list sessions
|
||||
sessions = userResource.getUserSessions();
|
||||
Assert.assertEquals("There should be one active session", 1, sessions.size());
|
||||
Assert.assertEquals("There should be no client in user session", 0, sessions.get(0).getClients().size());
|
||||
|
||||
// oauth clean up
|
||||
oauth.realm("test");
|
||||
oauth.redirectUri(oauth.SERVER_ROOT + "/auth/realms/master/app/auth");
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-18954
|
||||
*/
|
||||
@Test
|
||||
public void testRetrieveConsentsForUserWithClientsWithGrantedOfflineAccess() throws Exception {
|
||||
RealmResource providerRealm = adminClient.realm(providerRealmName());
|
||||
|
||||
RealmRepresentation providerRealmRep = providerRealm.toRepresentation();
|
||||
providerRealmRep.setAccountTheme("keycloak");
|
||||
providerRealm.update(providerRealmRep);
|
||||
providerRealm.clients().create(ClientBuilder.create().clientId("test-app").redirectUris("*").addWebOrigin("*").publicClient().build());
|
||||
|
||||
ClientRepresentation providerAccountRep = providerRealm.clients().findByClientId("test-app").get(0);
|
||||
|
||||
// add offline_scope to default account-console client scope
|
||||
ClientScopeRepresentation offlineAccessScope = providerRealm.getDefaultOptionalClientScopes().stream()
|
||||
.filter(csr -> csr.getName().equals(OAuth2Constants.OFFLINE_ACCESS)).findFirst().get();
|
||||
providerRealm.clients().get(providerAccountRep.getId()).removeOptionalClientScope(offlineAccessScope.getId());
|
||||
providerRealm.clients().get(providerAccountRep.getId()).addDefaultClientScope(offlineAccessScope.getId());
|
||||
|
||||
// enable consent required to explicitly grant offline access
|
||||
providerAccountRep.setConsentRequired(true);
|
||||
providerAccountRep.setDirectAccessGrantsEnabled(true); // for offline token retrieval
|
||||
providerRealm.clients().get(providerAccountRep.getId()).update(providerAccountRep);
|
||||
|
||||
List<UserRepresentation> searchResult = providerRealm.users().search(getUserLogin());
|
||||
UserRepresentation user = searchResult.get(0);
|
||||
|
||||
accountLoginPage.open(providerRealmName());
|
||||
|
||||
waitForPage("Sign in to provider");
|
||||
log.debug("Logging in");
|
||||
accountLoginPage.login(getUserLogin(), getUserPassword());
|
||||
|
||||
waitForPage("grant access");
|
||||
log.debug("Grant consent for offline_access");
|
||||
Assert.assertTrue(consentPage.isCurrent());
|
||||
consentPage.confirm();
|
||||
|
||||
// disable consent required again to enable direct grant token retrieval.
|
||||
providerAccountRep.setConsentRequired(false);
|
||||
providerRealm.clients().get(providerAccountRep.getId()).update(providerAccountRep);
|
||||
|
||||
log.debug("Obtain offline_token");
|
||||
AccessTokenResponse response = oauth.realm(providerRealmRep.getRealm())
|
||||
.client(providerAccountRep.getClientId())
|
||||
.scope(OAuth2Constants.SCOPE_OPENID +" " + OAuth2Constants.SCOPE_PROFILE + " " + OAuth2Constants.OFFLINE_ACCESS)
|
||||
.doPasswordGrantRequest(getUserLogin(), getUserPassword());
|
||||
assertNotNull(response.getRefreshToken());
|
||||
|
||||
log.debug("Check for Offline Token in consents");
|
||||
List<Map<String, Object>> consents = providerRealm.users().get(user.getId()).getConsents();
|
||||
assertFalse("Consents should not be empty", consents.isEmpty());
|
||||
|
||||
assertTrue(consents.toString().contains("Offline Token"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsentCancel() {
|
||||
// setup account client to require consent
|
||||
createAppClientInRealm(providerRealmName());
|
||||
RealmResource providerRealm = adminClient.realm(providerRealmName());
|
||||
ClientResource accountClient = findClientByClientId(providerRealm, "test-app");
|
||||
|
||||
ClientRepresentation clientRepresentation = accountClient.toRepresentation();
|
||||
clientRepresentation.setConsentRequired(true);
|
||||
accountClient.update(clientRepresentation);
|
||||
|
||||
// setup correct realm
|
||||
oauth.realm(providerRealmName());
|
||||
|
||||
// navigate to account console and login
|
||||
oauth.openLoginForm();
|
||||
loginPage.form().login(getUserLogin(), getUserPassword());
|
||||
|
||||
consentPage.assertCurrent();
|
||||
consentPage.cancel();
|
||||
|
||||
// check an error page after cancelling the consent
|
||||
assertTrue(driver.getTitle().contains("AUTH_RESPONSE"));
|
||||
assertTrue(driver.getCurrentUrl().contains("error=access_denied"));
|
||||
|
||||
oauth.openLoginForm();
|
||||
loginPage.form().login(getUserLogin(), getUserPassword());
|
||||
consentPage.confirm();
|
||||
|
||||
// successful login
|
||||
assertFalse(driver.getCurrentUrl().contains("error"));
|
||||
assertTrue("Test user should be successfully logged in.", driver.getTitle().contains("AUTH_RESPONSE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientConsentRequiredAfterLogin() {
|
||||
oauth.realm(TEST_REALM_NAME).client("test-app", "password");
|
||||
AuthorizationEndpointResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||
AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(response.getCode());
|
||||
|
||||
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.parseLoginResponse().getCode());
|
||||
|
||||
EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||
String sessionId = loginEvent.getSessionId();
|
||||
|
||||
ClientRepresentation clientRepresentation = adminClient.realm(TEST_REALM_NAME).clients().findByClientId("test-app").get(0);
|
||||
try {
|
||||
clientRepresentation.setConsentRequired(true);
|
||||
adminClient.realm(TEST_REALM_NAME).clients().get(clientRepresentation.getId()).update(clientRepresentation);
|
||||
|
||||
events.clear();
|
||||
|
||||
// try to refresh the token
|
||||
// this fails as client no longer has requested consent from user
|
||||
AccessTokenResponse refreshTokenResponse = oauth.doRefreshTokenRequest(accessTokenResponse.getRefreshToken());
|
||||
Assert.assertEquals(OAuthErrorException.INVALID_SCOPE, refreshTokenResponse.getError());
|
||||
Assert.assertEquals("Client no longer has requested consent from user", refreshTokenResponse.getErrorDescription());
|
||||
|
||||
events.expectRefresh(accessTokenResponse.getRefreshToken(), sessionId).user((String) null).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
|
||||
} finally {
|
||||
clientRepresentation.setConsentRequired(false);
|
||||
adminClient.realm(TEST_REALM_NAME).clients().get(clientRepresentation.getId()).update(clientRepresentation);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsentWithAdditionalClientAttributes() {
|
||||
// setup account client to require consent
|
||||
RealmResource providerRealm = adminClient.realm(providerRealmName());
|
||||
ClientResource accountClient = findClientByClientId(providerRealm, "test-app");
|
||||
|
||||
ClientRepresentation clientRepresentation = accountClient.toRepresentation();
|
||||
clientRepresentation.setConsentRequired(true);
|
||||
clientRepresentation.getAttributes().put(ClientModel.LOGO_URI,"https://www.keycloak.org/resources/images/keycloak_logo_480x108.png");
|
||||
clientRepresentation.getAttributes().put(ClientModel.POLICY_URI,"https://www.keycloak.org/policy");
|
||||
clientRepresentation.getAttributes().put(ClientModel.TOS_URI,"https://www.keycloak.org/tos");
|
||||
accountClient.update(clientRepresentation);
|
||||
|
||||
// setup correct realm
|
||||
oauth.realm(providerRealmName());
|
||||
|
||||
// navigate to account console and login
|
||||
oauth.openLoginForm();
|
||||
loginPage.form().login(getUserLogin(), getUserPassword());
|
||||
|
||||
consentPage.assertCurrent();
|
||||
|
||||
assertTrue("logoUri must be presented", driver.findElement(By.xpath("//img[@src='https://www.keycloak.org/resources/images/keycloak_logo_480x108.png']")).isDisplayed());
|
||||
assertTrue("policyUri must be presented", driver.findElement(By.xpath("//a[@href='https://www.keycloak.org/policy']")).isDisplayed());
|
||||
assertTrue("tosUri must be presented", driver.findElement(By.xpath("//a[@href='https://www.keycloak.org/tos']")).isDisplayed());
|
||||
|
||||
consentPage.confirm();
|
||||
|
||||
// successful login
|
||||
assertTrue("Test user should be successfully logged in.", driver.getTitle().contains("AUTH_RESPONSE"));
|
||||
}
|
||||
|
||||
private String getAccountUrl(String realmName) {
|
||||
return getAuthRoot() + "/auth/realms/" + realmName + "/account";
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue