[KEYCLOAK-3837] added session and account linking spring boot tests (#4564)

This commit is contained in:
Wyvie 2017-10-19 06:29:59 +02:00 committed by Stian Thorgersen
parent 19f54111ec
commit 988d660083
11 changed files with 990 additions and 79 deletions

View file

@ -1,28 +1,41 @@
package org.keycloak;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.util.JsonSerialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.UUID;
@Controller
@RequestMapping(path = "/admin")
public class AdminController {
private static Logger logger = LoggerFactory.getLogger(AdminController.class);
@RequestMapping(path = "/TokenServlet", method = RequestMethod.GET)
public String showTokens(WebRequest req, Model model, @RequestParam Map<String, String> attributes) throws IOException {
@ -56,4 +69,74 @@ public class AdminController {
return "tokens";
}
@RequestMapping(path = "/SessionServlet", method = RequestMethod.GET)
public String sessionServlet(WebRequest webRequest, Model model) {
String counterString = (String) webRequest.getAttribute("counter", RequestAttributes.SCOPE_SESSION);
int counter = 0;
try {
counter = Integer.parseInt(counterString, 10);
}
catch (NumberFormatException ignored) {
}
model.addAttribute("counter", counter);
webRequest.setAttribute("counter", Integer.toString(counter+1), RequestAttributes.SCOPE_SESSION);
return "session";
}
@RequestMapping(path = "/LinkServlet", method = RequestMethod.GET)
public String tokenController(WebRequest webRequest,
@RequestParam Map<String, String> attributes,
Model model) {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession httpSession = attr.getRequest().getSession(true);
// response.addHeader("Cache-Control", "no-cache");
String responseAttr = attributes.get("response");
if (StringUtils.isEmpty(responseAttr)) {
String provider = attributes.get("provider");
String realm = attributes.get("realm");
KeycloakSecurityContext keycloakSession =
(KeycloakSecurityContext) webRequest.getAttribute(
KeycloakSecurityContext.class.getName(),
RequestAttributes.SCOPE_REQUEST);
AccessToken token = keycloakSession.getToken();
String clientId = token.getAudience()[0];
String nonce = UUID.randomUUID().toString();
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
String input = nonce + token.getSessionState() + clientId + provider;
byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
String hash = Base64Url.encode(check);
httpSession.setAttribute("hash", hash);
String redirectUri = KeycloakUriBuilder.fromUri("http://localhost:8280/admin/LinkServlet")
.queryParam("response", "true").build().toString();
String accountLinkUrl = KeycloakUriBuilder.fromUri("http://localhost:8180/")
.path("/auth/realms/{realm}/broker/{provider}/link")
.queryParam("nonce", nonce)
.queryParam("hash", hash)
.queryParam("client_id", token.getIssuedFor())
.queryParam("redirect_uri", redirectUri).build(realm, provider).toString();
return "redirect:" + accountLinkUrl;
} else {
String error = attributes.get("link_error");
if (StringUtils.isEmpty(error))
model.addAttribute("error", "Account linked");
else
model.addAttribute("error", error);
return "linking";
}
}
}

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
<title>Linking page result</title>
</head>
<body>
<span id="error" th:text="${error}"/>
</body>
</html>

View file

@ -0,0 +1,9 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org/">
<head>
<title>session counter page</title>
</head>
<body>
<span id="counter" th:text="${counter}"></span>
</body>
</html>

View file

@ -0,0 +1,24 @@
package org.keycloak.testsuite.springboot;
import org.keycloak.testsuite.pages.AbstractPage;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LinkingPage extends AbstractPage {
@FindBy(id = "error")
private WebElement errorMessage;
@Override
public boolean isCurrent() {
return driver.getTitle().equalsIgnoreCase("linking page result");
}
@Override
public void open() throws Exception {
}
public String getErrorMessage() {
return errorMessage.getText();
}
}

View file

@ -0,0 +1,29 @@
package org.keycloak.testsuite.springboot;
import org.apache.commons.lang3.math.NumberUtils;
import org.keycloak.testsuite.pages.AbstractPage;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class SessionPage extends AbstractPage {
public static final String PAGE_TITLE = "session counter page";
@FindBy(id = "counter")
private WebElement counterElement;
@Override
public boolean isCurrent() {
return driver.getTitle().equalsIgnoreCase(PAGE_TITLE);
}
@Override
public void open() throws Exception {
}
public int getCounter() {
String counterString = counterElement.getText();
return NumberUtils.toInt(counterString, 0);
}
}

View file

@ -19,4 +19,8 @@ public class SpringAdminPage extends AbstractPage {
public void open() throws Exception {
}
public String getTestDivString() {
return testDiv.getText();
}
}

View file

@ -36,32 +36,27 @@ import org.openqa.selenium.By;
public abstract class AbstractSpringBootTest extends AbstractKeycloakTest {
protected static final String REALM_ID = "cd8ee421-5100-41ba-95dd-b27c8e5cf042";
static final String REALM_ID = "cd8ee421-5100-41ba-95dd-b27c8e5cf042";
protected static final String REALM_NAME = "test";
static final String REALM_NAME = "test";
protected static final String CLIENT_ID = "spring-boot-app";
protected static final String SECRET = "e3789ac5-bde6-4957-a7b0-612823dac101";
static final String CLIENT_ID = "spring-boot-app";
static final String SECRET = "e3789ac5-bde6-4957-a7b0-612823dac101";
protected static final String APPLICATION_URL = "http://localhost:8280";
protected static final String BASE_URL = APPLICATION_URL + "/admin";
static final String APPLICATION_URL = "http://localhost:8280";
static final String BASE_URL = APPLICATION_URL + "/admin";
protected static final String USER_LOGIN = "testuser";
protected static final String USER_EMAIL = "user@email.test";
protected static final String USER_PASSWORD = "user-password";
static final String USER_LOGIN = "testuser";
static final String USER_EMAIL = "user@email.test";
static final String USER_PASSWORD = "user-password";
protected static final String USER_LOGIN_2 = "testuser2";
protected static final String USER_EMAIL_2 = "user2@email.test";
protected static final String USER_PASSWORD_2 = "user2-password";
static final String CORRECT_ROLE = "admin";
protected static final String CORRECT_ROLE = "admin";
protected static final String INCORRECT_ROLE = "wrong-admin";
protected static final String REALM_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5" +
static final String REALM_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5" +
"mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi7" +
"9NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
protected static final String REALM_PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3Bj" +
static final String REALM_PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3Bj" +
"LGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vj" +
"O2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jY" +
"lQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn" +
@ -72,16 +67,16 @@ public abstract class AbstractSpringBootTest extends AbstractKeycloakTest {
"N39fOYAlo+nTixgeW7X8Y=";
@Page
protected LoginPage loginPage;
LoginPage loginPage;
@Page
protected SpringApplicationPage applicationPage;
SpringApplicationPage applicationPage;
@Page
protected SpringAdminPage adminPage;
SpringAdminPage adminPage;
@Page
protected TokenPage tokenPage;
TokenPage tokenPage;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
@ -117,7 +112,7 @@ public abstract class AbstractSpringBootTest extends AbstractKeycloakTest {
return clientRepresentation;
}
private void addUser(String login, String email, String password, String... roles) {
void addUser(String login, String email, String password, String... roles) {
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setUsername(login);
@ -149,14 +144,14 @@ public abstract class AbstractSpringBootTest extends AbstractKeycloakTest {
return result;
}
protected String logoutPage(String redirectUrl) {
String logoutPage(String redirectUrl) {
return getAuthRoot(suiteContext)
+ "/auth/realms/" + REALM_NAME
+ "/protocol/" + "openid-connect"
+ "/logout?redirect_uri=" + encodeUrl(redirectUrl);
}
protected void setAdapterAndServerTimeOffset(int timeOffset, String url) {
void setAdapterAndServerTimeOffset(int timeOffset, String url) {
setTimeOffset(timeOffset);
String timeOffsetUri = UriBuilder.fromUri(url)
@ -167,51 +162,41 @@ public abstract class AbstractSpringBootTest extends AbstractKeycloakTest {
WaitUtils.waitUntilElement(By.tagName("body")).is().visible();
}
protected String getCorrectUserId() {
return adminClient.realms().realm(REALM_NAME).users().search(USER_LOGIN)
.get(0).getId();
}
@Before
public void createRoles() {
RealmResource realm = realmsResouce().realm(REALM_NAME);
RoleRepresentation correct = new RoleRepresentation(CORRECT_ROLE, CORRECT_ROLE, false);
realm.roles().create(correct);
RoleRepresentation incorrect = new RoleRepresentation(INCORRECT_ROLE, INCORRECT_ROLE, false);
realm.roles().create(incorrect);
}
@Before
public void addUsers() {
addUser(USER_LOGIN, USER_EMAIL, USER_PASSWORD, CORRECT_ROLE);
addUser(USER_LOGIN_2, USER_EMAIL_2, USER_PASSWORD_2, INCORRECT_ROLE);
}
@After
public void cleanupUsers() {
RealmResource providerRealm = adminClient.realm(REALM_NAME);
UserRepresentation userRep = ApiUtil.findUserByUsername(providerRealm, USER_LOGIN);
RealmResource realmResource = adminClient.realm(REALM_NAME);
UserRepresentation userRep = ApiUtil.findUserByUsername(realmResource, USER_LOGIN);
if (userRep != null) {
providerRealm.users().get(userRep.getId()).remove();
}
RealmResource childRealm = adminClient.realm(REALM_NAME);
userRep = ApiUtil.findUserByUsername(childRealm, USER_LOGIN_2);
if (userRep != null) {
childRealm.users().get(userRep.getId()).remove();
realmResource.users().get(userRep.getId()).remove();
}
}
@After
public void cleanupRoles() {
RealmResource realm = realmsResouce().realm(REALM_NAME);
RoleResource correctRole = realm.roles().get(CORRECT_ROLE);
correctRole.remove();
}
RoleResource incorrectRole = realm.roles().get(INCORRECT_ROLE);
incorrectRole.remove();
@Before
public void setUp() {
createRoles();
addUsers();
}
@After
public void tearDown() {
cleanupUsers();
cleanupRoles();
}
}

View file

@ -0,0 +1,560 @@
package org.keycloak.testsuite.springboot;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.Base64Url;
import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.*;
import org.keycloak.testsuite.ActionURIUtils;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
import org.keycloak.testsuite.broker.BrokerTestTools;
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.util.JsonSerialization;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.UriBuilder;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
public class AccountLinkSpringBootTest extends AbstractSpringBootTest {
private static final String PARENT_REALM = "parent-realm";
private static final String LINKING_URL = BASE_URL + "/LinkServlet";
private static final String PARENT_USERNAME = "parent-username";
private static final String PARENT_PASSWORD = "parent-password";
private static final String CHILD_USERNAME_1 = "child-username-1";
private static final String CHILD_PASSWORD_1 = "child-password-1";
private static final String CHILD_USERNAME_2 = "child-username-2";
private static final String CHILD_PASSWORD_2 = "child-password-2";
@Page
private LinkingPage linkingPage;
@Page
private AccountUpdateProfilePage profilePage;
@Page
private LoginUpdateProfilePage loginUpdateProfilePage;
@Page
private ErrorPage errorPage;
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation realm = new RealmRepresentation();
realm.setRealm(REALM_NAME);
realm.setEnabled(true);
realm.setPublicKey(REALM_PUBLIC_KEY);
realm.setPrivateKey(REALM_PRIVATE_KEY);
realm.setAccessTokenLifespan(600);
realm.setAccessCodeLifespan(10);
realm.setAccessCodeLifespanUserAction(6000);
realm.setSslRequired("external");
ClientRepresentation servlet = new ClientRepresentation();
servlet.setClientId(CLIENT_ID);
servlet.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
servlet.setAdminUrl(LINKING_URL);
servlet.setDirectAccessGrantsEnabled(true);
servlet.setBaseUrl(LINKING_URL);
servlet.setRedirectUris(new LinkedList<>());
servlet.getRedirectUris().add(LINKING_URL + "/*");
servlet.setSecret(SECRET);
servlet.setFullScopeAllowed(true);
realm.setClients(new LinkedList<>());
realm.getClients().add(servlet);
testRealms.add(realm);
realm = new RealmRepresentation();
realm.setRealm(PARENT_REALM);
realm.setEnabled(true);
testRealms.add(realm);
}
@Override
public void addUsers() {
addIdpUser();
addChildUser();
}
@Override
public void cleanupUsers() {
}
@Override
public void createRoles() {
}
@Override
protected boolean isImportAfterEachMethod() {
return true;
}
public void addIdpUser() {
RealmResource realm = adminClient.realms().realm(PARENT_REALM);
UserRepresentation user = new UserRepresentation();
user.setUsername(PARENT_USERNAME);
user.setEnabled(true);
createUserAndResetPasswordWithAdminClient(realm, user, PARENT_PASSWORD);
}
private String childUserId = null;
public void addChildUser() {
RealmResource realm = adminClient.realms().realm(REALM_NAME);
UserRepresentation user = new UserRepresentation();
user.setUsername(CHILD_USERNAME_1);
user.setEnabled(true);
childUserId = createUserAndResetPasswordWithAdminClient(realm, user, CHILD_PASSWORD_1);
UserRepresentation user2 = new UserRepresentation();
user2.setUsername(CHILD_USERNAME_2);
user2.setEnabled(true);
String user2Id = createUserAndResetPasswordWithAdminClient(realm, user2, CHILD_PASSWORD_2);
// have to add a role as undertow default auth manager doesn't like "*". todo we can remove this eventually as undertow fixes this in later versions
realm.roles().create(new RoleRepresentation(CORRECT_ROLE, null, false));
RoleRepresentation role = realm.roles().get(CORRECT_ROLE).toRepresentation();
List<RoleRepresentation> roles = new LinkedList<>();
roles.add(role);
realm.users().get(childUserId).roles().realmLevel().add(roles);
realm.users().get(user2Id).roles().realmLevel().add(roles);
ClientRepresentation brokerService = realm.clients().findByClientId(Constants.BROKER_SERVICE_CLIENT_ID).get(0);
role = realm.clients().get(brokerService.getId()).roles().get(Constants.READ_TOKEN_ROLE).toRepresentation();
roles.clear();
roles.add(role);
realm.users().get(childUserId).roles().clientLevel(brokerService.getId()).add(roles);
realm.users().get(user2Id).roles().clientLevel(brokerService.getId()).add(roles);
}
@Before
public void createParentChild() {
BrokerTestTools.createKcOidcBroker(adminClient, REALM_NAME, PARENT_REALM, suiteContext);
}
@Test
public void testErrorConditions() throws Exception {
RealmResource realm = adminClient.realms().realm(REALM_NAME);
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
ClientRepresentation client = adminClient.realms().realm(REALM_NAME).clients().findByClientId(CLIENT_ID).get(0);
UriBuilder redirectUri = UriBuilder.fromUri(LINKING_URL).queryParam("response", "true");
UriBuilder directLinking = UriBuilder.fromUri(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth")
.path("realms/{child-realm}/broker/{provider}/link")
.queryParam("client_id", CLIENT_ID)
.queryParam("redirect_uri", redirectUri.build())
.queryParam("hash", Base64Url.encode("crap".getBytes()))
.queryParam("nonce", UUID.randomUUID().toString());
String linkUrl = directLinking
.build(REALM_NAME, PARENT_REALM).toString();
// test that child user cannot log into parent realm
navigateTo(linkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(driver.getCurrentUrl().contains("link_error=not_logged_in"));
logoutAll();
// now log in
navigateTo(LINKING_URL + "?response=true");
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue("Must be on linking page", linkingPage.isCurrent());
Assert.assertEquals("account linked", linkingPage.getErrorMessage().toLowerCase());
// now test CSRF with bad hash.
navigateTo(linkUrl);
Assert.assertTrue(driver.getPageSource().contains("We're sorry..."));
logoutAll();
// now log in again with client that does not have scope
String accountId = adminClient.realms().realm(REALM_NAME).clients().findByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).get(0).getId();
RoleRepresentation manageAccount = adminClient.realms().realm(REALM_NAME).clients().get(accountId).roles().get(MANAGE_ACCOUNT).toRepresentation();
RoleRepresentation manageLinks = adminClient.realms().realm(REALM_NAME).clients().get(accountId).roles().get(MANAGE_ACCOUNT_LINKS).toRepresentation();
RoleRepresentation userRole = adminClient.realms().realm(REALM_NAME).roles().get(CORRECT_ROLE).toRepresentation();
client.setFullScopeAllowed(false);
ClientResource clientResource = adminClient.realms().realm(REALM_NAME).clients().get(client.getId());
clientResource.update(client);
List<RoleRepresentation> roles = new LinkedList<>();
roles.add(userRole);
clientResource.getScopeMappings().realmLevel().add(roles);
navigateTo(LINKING_URL + "?response=true");
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(linkingPage.isCurrent());
Assert.assertEquals("account linked", linkingPage.getErrorMessage().toLowerCase());
UriBuilder linkBuilder = UriBuilder.fromUri(LINKING_URL);
String clientLinkUrl = linkBuilder.clone()
.queryParam("realm", REALM_NAME)
.queryParam("provider", PARENT_REALM).build().toString();
navigateTo(clientLinkUrl);
Assert.assertTrue(driver.getCurrentUrl().contains("error=not_allowed"));
logoutAll();
// add MANAGE_ACCOUNT_LINKS scope should pass.
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
roles = new LinkedList<>();
roles.add(manageLinks);
clientResource.getScopeMappings().clientLevel(accountId).add(roles);
navigateTo(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(loginPage.isCurrent(PARENT_REALM));
loginPage.login(PARENT_USERNAME, PARENT_PASSWORD);
Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
Assert.assertTrue(driver.getPageSource().contains("Account linked"));
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_REALM);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
clientResource.getScopeMappings().clientLevel(accountId).remove(roles);
logoutAll();
navigateTo(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(driver.getCurrentUrl().contains("link_error=not_allowed"));
logoutAll();
// add MANAGE_ACCOUNT scope should pass
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
roles = new LinkedList<>();
roles.add(manageAccount);
clientResource.getScopeMappings().clientLevel(accountId).add(roles);
navigateTo(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(loginPage.isCurrent(PARENT_REALM));
loginPage.login(PARENT_USERNAME, PARENT_PASSWORD);
Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
Assert.assertTrue(driver.getPageSource().contains("Account linked"));
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_REALM);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
clientResource.getScopeMappings().clientLevel(accountId).remove(roles);
logoutAll();
navigateTo(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(driver.getCurrentUrl().contains("link_error=not_allowed"));
logoutAll();
// undo fullScopeAllowed
client = adminClient.realms().realm(REALM_NAME).clients().findByClientId(CLIENT_ID).get(0);
client.setFullScopeAllowed(true);
clientResource.update(client);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
logoutAll();
}
@Test
public void testAccountLink() throws Exception {
RealmResource realm = adminClient.realms().realm(REALM_NAME);
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
UriBuilder linkBuilder = UriBuilder.fromUri(LINKING_URL);
String linkUrl = linkBuilder.clone()
.queryParam("realm", REALM_NAME)
.queryParam("provider", PARENT_REALM).build().toString();
log.info("linkUrl: " + linkUrl);
navigateTo(linkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
Assert.assertTrue(driver.getPageSource().contains(PARENT_REALM));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(loginPage.isCurrent(PARENT_REALM));
loginPage.login(PARENT_USERNAME, PARENT_PASSWORD);
log.info("After linking: " + driver.getCurrentUrl());
log.info(driver.getPageSource());
Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
Assert.assertTrue(driver.getPageSource().contains("Account linked"));
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest(
REALM_NAME,
CHILD_USERNAME_1,
CHILD_PASSWORD_1,
null,
CLIENT_ID,
SECRET);
Assert.assertNotNull(response.getAccessToken());
Assert.assertNull(response.getError());
Client httpClient = ClientBuilder.newClient();
String firstToken = getToken(response, httpClient);
Assert.assertNotNull(firstToken);
navigateTo(linkUrl);
Assert.assertTrue(driver.getPageSource().contains("Account linked"));
String nextToken = getToken(response, httpClient);
Assert.assertNotNull(nextToken);
Assert.assertNotEquals(firstToken, nextToken);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_REALM);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
logoutAll();
}
@Test
public void testLinkOnlyProvider() throws Exception {
RealmResource realm = adminClient.realms().realm(REALM_NAME);
IdentityProviderRepresentation rep = realm.identityProviders().get(PARENT_REALM).toRepresentation();
rep.setLinkOnly(true);
realm.identityProviders().get(PARENT_REALM).update(rep);
try {
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
UriBuilder linkBuilder = UriBuilder.fromUri(LINKING_URL);
String linkUrl = linkBuilder.clone()
.queryParam("realm", REALM_NAME)
.queryParam("provider", PARENT_REALM).build().toString();
navigateTo(linkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
// should not be on login page. This is what we are testing
Assert.assertFalse(driver.getPageSource().contains(PARENT_REALM));
// now test that we can still link.
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
Assert.assertTrue(loginPage.isCurrent(PARENT_REALM));
loginPage.login(PARENT_USERNAME, PARENT_PASSWORD);
log.info("After linking: " + driver.getCurrentUrl());
log.info(driver.getPageSource());
Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
Assert.assertTrue(driver.getPageSource().contains("Account linked"));
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_REALM);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
logoutAll();
log.info("testing link-only attack");
navigateTo(linkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
log.info("login page uri is: " + driver.getCurrentUrl());
// ok, now scrape the code from page
String pageSource = driver.getPageSource();
String action = ActionURIUtils.getActionURIFromPageSource(pageSource);
System.out.println("action uri: " + action);
Map<String, String> queryParams = ActionURIUtils.parseQueryParamsFromActionURI(action);
System.out.println("query params: " + queryParams);
// now try and use the code to login to remote link-only idp
String uri = "/auth/realms/" + REALM_NAME + "/broker/" + PARENT_REALM + "/login";
uri = UriBuilder.fromUri(AuthServerTestEnricher.getAuthServerContextRoot())
.path(uri)
.queryParam(OAuth2Constants.CODE, queryParams.get(OAuth2Constants.CODE))
.queryParam(Constants.CLIENT_ID, queryParams.get(Constants.CLIENT_ID))
.build().toString();
log.info("hack uri: " + uri);
navigateTo(uri);
Assert.assertTrue(driver.getPageSource().contains("Could not send authentication request to identity provider."));
} finally {
rep.setLinkOnly(false);
realm.identityProviders().get(PARENT_REALM).update(rep);
}
}
@Test
public void testAccountNotLinkedAutomatically() throws Exception {
RealmResource realm = adminClient.realms().realm(REALM_NAME);
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
// Login to account mgmt first
profilePage.open(REALM_NAME);
WaitUtils.waitForPageToLoad();
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
profilePage.assertCurrent();
// Now in another tab, open login screen with "prompt=login" . Login screen will be displayed even if I have SSO cookie
UriBuilder linkBuilder = UriBuilder.fromUri(LINKING_URL);
String linkUrl = linkBuilder.clone()
.queryParam(OIDCLoginProtocol.PROMPT_PARAM, OIDCLoginProtocol.PROMPT_VALUE_LOGIN)
.build().toString();
navigateTo(linkUrl);
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.clickSocial(PARENT_REALM);
Assert.assertTrue(loginPage.isCurrent(PARENT_REALM));
loginPage.login(PARENT_USERNAME, PARENT_PASSWORD);
// Test I was not automatically linked.
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
loginUpdateProfilePage.assertCurrent();
loginUpdateProfilePage.update("Joe", "Doe", "joe@parent.com");
errorPage.assertCurrent();
Assert.assertEquals("You are already authenticated as different user '"
+ CHILD_USERNAME_1
+ "' in this session. Please logout first.", errorPage.getError());
logoutAll();
// Remove newly created user
String newUserId = ApiUtil.findUserByUsername(realm, PARENT_USERNAME).getId();
getCleanup(REALM_NAME).addUserId(newUserId);
}
@Test
public void testAccountLinkingExpired() throws Exception {
RealmResource realm = adminClient.realms().realm(REALM_NAME);
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
// Login to account mgmt first
profilePage.open(REALM_NAME);
WaitUtils.waitForPageToLoad();
Assert.assertTrue(loginPage.isCurrent(REALM_NAME));
loginPage.login(CHILD_USERNAME_1, CHILD_PASSWORD_1);
profilePage.assertCurrent();
// Now in another tab, request account linking
UriBuilder linkBuilder = UriBuilder.fromUri(LINKING_URL);
String linkUrl = linkBuilder.clone()
.queryParam("realm", REALM_NAME)
.queryParam("provider", PARENT_REALM).build().toString();
navigateTo(linkUrl);
Assert.assertTrue(loginPage.isCurrent(PARENT_REALM));
// Logout "child" userSession in the meantime (for example through admin request)
realm.logoutAll();
// Finish login on parent.
loginPage.login(PARENT_USERNAME, PARENT_PASSWORD);
// Test I was not automatically linked
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
errorPage.assertCurrent();
Assert.assertEquals("Requested broker account linking, but current session is no longer valid.", errorPage.getError());
logoutAll();
}
private void navigateTo(String uri) {
driver.navigate().to(uri);
WaitUtils.waitForPageToLoad();
}
public void logoutAll() {
String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()).build(REALM_NAME).toString();
navigateTo(logoutUri);
logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()).build(PARENT_REALM).toString();
navigateTo(logoutUri);
}
private String getToken(OAuthClient.AccessTokenResponse response, Client httpClient) throws Exception {
log.info("target here is " + OAuthClient.AUTH_SERVER_ROOT);
String idpToken = httpClient.target(OAuthClient.AUTH_SERVER_ROOT)
.path("realms")
.path(REALM_NAME)
.path("broker")
.path(PARENT_REALM)
.path("token")
.request()
.header("Authorization", "Bearer " + response.getAccessToken())
.get(String.class);
AccessTokenResponse res = JsonSerialization.readValue(idpToken, AccessTokenResponse.class);
return res.getToken();
}
}

View file

@ -1,9 +1,44 @@
package org.keycloak.testsuite.springboot;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
public class BasicSpringBootTest extends AbstractSpringBootTest {
private static final String USER_LOGIN_2 = "testuser2";
private static final String USER_EMAIL_2 = "user2@email.test";
private static final String USER_PASSWORD_2 = "user2-password";
private static final String INCORRECT_ROLE = "wrong-admin";
@Before
public void addIncorrectUser() {
RolesResource rolesResource = adminClient.realm(REALM_NAME).roles();
RoleRepresentation role = new RoleRepresentation(INCORRECT_ROLE, INCORRECT_ROLE, false);
rolesResource.create(role);
addUser(USER_LOGIN_2, USER_EMAIL_2, USER_PASSWORD_2, INCORRECT_ROLE);
}
@After
public void removeUser() {
UserRepresentation user = ApiUtil.findUserByUsername(adminClient.realm(REALM_NAME), USER_LOGIN_2);
if (user != null) {
adminClient.realm(REALM_NAME).users().delete(user.getId());
}
adminClient.realm(REALM_NAME).roles().deleteRole(INCORRECT_ROLE);
}
@Test
public void testCorrectUser() {
driver.navigate().to(APPLICATION_URL + "/index.html");

View file

@ -7,8 +7,10 @@ import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.Urls;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.pages.AccountApplicationsPage;
import org.keycloak.testsuite.pages.OAuthGrantPage;
import org.keycloak.testsuite.util.ClientManager;
@ -22,7 +24,7 @@ import java.util.List;
import static org.keycloak.testsuite.util.WaitUtils.pause;
public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
private static final String SERVLET_URI = APPLICATION_URL + "/admin/TokenServlet";
private static final String SERVLET_URL = BASE_URL + "/TokenServlet";
@Rule
public AssertEvents events = new AssertEvents(this);
@ -35,7 +37,7 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
@Test
public void testTokens() {
String servletUri = UriBuilder.fromUri(SERVLET_URI)
String servletUri = UriBuilder.fromUri(SERVLET_URL)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.OFFLINE_ACCESS)
.build().toString();
driver.navigate().to(servletUri);
@ -45,31 +47,31 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
WaitUtils.waitUntilElement(By.tagName("body")).is().visible();
Assert.assertTrue(tokenPage.isCurrent());
Assert.assertTrue("Must be on tokens page", tokenPage.isCurrent());
Assert.assertEquals(tokenPage.getRefreshToken().getType(), TokenUtil.TOKEN_TYPE_OFFLINE);
Assert.assertEquals(tokenPage.getRefreshToken().getExpiration(), 0);
Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, tokenPage.getRefreshToken().getType());
Assert.assertEquals(0, tokenPage.getRefreshToken().getExpiration());
String accessTokenId = tokenPage.getAccessToken().getId();
String refreshTokenId = tokenPage.getRefreshToken().getId();
setAdapterAndServerTimeOffset(9999, SERVLET_URI);
setAdapterAndServerTimeOffset(9999, SERVLET_URL);
driver.navigate().to(SERVLET_URI);
driver.navigate().to(SERVLET_URL);
Assert.assertTrue("Must be on tokens page", tokenPage.isCurrent());
Assert.assertNotEquals(tokenPage.getRefreshToken().getId(), refreshTokenId);
Assert.assertNotEquals(tokenPage.getAccessToken().getId(), accessTokenId);
Assert.assertNotEquals(refreshTokenId, tokenPage.getRefreshToken().getId());
Assert.assertNotEquals(accessTokenId, tokenPage.getAccessToken().getId());
setAdapterAndServerTimeOffset(0, SERVLET_URI);
setAdapterAndServerTimeOffset(0, SERVLET_URL);
driver.navigate().to(logoutPage(SERVLET_URI));
driver.navigate().to(logoutPage(SERVLET_URL));
Assert.assertTrue("Must be on login page", loginPage.isCurrent());
}
@Test
public void testRevoke() {
// Login to servlet first with offline token
String servletUri = UriBuilder.fromUri(SERVLET_URI)
String servletUri = UriBuilder.fromUri(SERVLET_URL)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.OFFLINE_ACCESS)
.build().toString();
driver.navigate().to(servletUri);
@ -81,10 +83,10 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
Assert.assertEquals(tokenPage.getRefreshToken().getType(), TokenUtil.TOKEN_TYPE_OFFLINE);
// Assert refresh works with increased time
setAdapterAndServerTimeOffset(9999, SERVLET_URI);
driver.navigate().to(SERVLET_URI);
setAdapterAndServerTimeOffset(9999, SERVLET_URL);
driver.navigate().to(SERVLET_URL);
Assert.assertTrue("Must be on token page", tokenPage.isCurrent());
setAdapterAndServerTimeOffset(0, SERVLET_URI);
setAdapterAndServerTimeOffset(0, SERVLET_URL);
events.clear();
@ -98,14 +100,18 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
pause(500);
Assert.assertEquals(accountAppPage.getApplications().get(CLIENT_ID).getAdditionalGrants().size(), 0);
events.expect(EventType.REVOKE_GRANT).realm(REALM_ID).user(getCorrectUserId())
UserRepresentation userRepresentation =
ApiUtil.findUserByUsername(realmsResouce().realm(REALM_NAME), USER_LOGIN);
Assert.assertNotNull("User should exist", userRepresentation);
events.expect(EventType.REVOKE_GRANT).realm(REALM_ID).user(userRepresentation.getId())
.client("account").detail(Details.REVOKED_CLIENT, CLIENT_ID).assertEvent();
// Assert refresh doesn't work now (increase time one more time)
setAdapterAndServerTimeOffset(9999, SERVLET_URI);
driver.navigate().to(SERVLET_URI);
setAdapterAndServerTimeOffset(9999, SERVLET_URL);
driver.navigate().to(SERVLET_URL);
loginPage.assertCurrent();
setAdapterAndServerTimeOffset(0, SERVLET_URI);
setAdapterAndServerTimeOffset(0, SERVLET_URL);
}
@Test
@ -113,17 +119,15 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
ClientManager.realm(adminClient.realm(REALM_NAME)).clientId(CLIENT_ID).consentRequired(true);
// Assert grant page doesn't have 'Offline Access' role when offline token is not requested
driver.navigate().to(SERVLET_URI);
driver.navigate().to(SERVLET_URL);
loginPage.login(USER_LOGIN, USER_PASSWORD);
oauthGrantPage.assertCurrent();
WaitUtils.waitUntilElement(By.xpath("//body")).text().not().contains("Offline access");
oauthGrantPage.cancel();
// Assert grant page has 'Offline Access' role now
String servletUri = UriBuilder.fromUri(SERVLET_URI)
driver.navigate().to(UriBuilder.fromUri(SERVLET_URL)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.OFFLINE_ACCESS)
.build().toString();
driver.navigate().to(servletUri);
.build().toString());
WaitUtils.waitUntilElement(By.tagName("body")).is().visible();
loginPage.login(USER_LOGIN, USER_PASSWORD);
@ -143,7 +147,7 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
Assert.assertTrue(offlineClient.getAdditionalGrants().contains("Offline Token"));
//This was necessary to be introduced, otherwise other testcases will fail
driver.navigate().to(logoutPage(SERVLET_URI));
driver.navigate().to(logoutPage(SERVLET_URL));
loginPage.assertCurrent();
events.clear();

View file

@ -0,0 +1,169 @@
package org.keycloak.testsuite.springboot;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.auth.page.account.Sessions;
import org.keycloak.testsuite.util.SecondBrowser;
import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class SessionSpringBootTest extends AbstractSpringBootTest {
private static final String SERVLET_URL = BASE_URL + "/SessionServlet";
static final String USER_LOGIN_CORRECT_2 = "testcorrectuser2";
static final String USER_EMAIL_CORRECT_2 = "usercorrect2@email.test";
static final String USER_PASSWORD_CORRECT_2 = "testcorrectpassword2";
@Page
private SessionPage sessionPage;
@Drone
@SecondBrowser
private WebDriver driver2;
@Page
private Sessions realmSessions;
@Override
public void setDefaultPageUriParameters() {
super.setDefaultPageUriParameters();
realmSessions.setAuthRealm(REALM_NAME);
}
private void loginAndCheckSession() {
driver.navigate().to(SERVLET_URL);
Assert.assertTrue("Must be on login page", loginPage.isCurrent());
loginPage.login(USER_LOGIN, USER_PASSWORD);
WaitUtils.waitUntilElement(By.tagName("body")).is().visible();
Assert.assertTrue("Must be on servlet page", sessionPage.isCurrent());
Assert.assertEquals("Counter must be 0", 0, sessionPage.getCounter());
driver.navigate().to(SERVLET_URL);
Assert.assertEquals("Counter now must be 1", 1, sessionPage.getCounter());
}
private boolean checkCounterInSource(WebDriver driver, int counter) {
return driver.getPageSource().replaceAll("\\s", "")
.contains("<spanid=\"counter\">" + counter + "</span>");
}
@Before
public void addUserCorrect2() {
addUser(USER_LOGIN_CORRECT_2, USER_EMAIL_CORRECT_2, USER_PASSWORD_CORRECT_2, CORRECT_ROLE);
}
@After
public void removeUserCorrect2() {
UserRepresentation userRep = ApiUtil.findUserByUsername(realmsResouce().realm(REALM_NAME), USER_LOGIN_CORRECT_2);
if (userRep != null) {
realmsResouce().realm(REALM_NAME).users().get(userRep.getId()).remove();
}
}
@Test
public void testSingleSessionInvalidated() {
loginAndCheckSession();
// cannot pass to loginAndCheckSession becayse loginPage is not working together with driver2, therefore copypasta
driver2.navigate().to(SERVLET_URL);
log.info("current title is " + driver2.getTitle());
Assert.assertTrue("Must be on login page", driver2.getTitle().toLowerCase().startsWith("log in to"));
driver2.findElement(By.id("username")).sendKeys(USER_LOGIN);
driver2.findElement(By.id("password")).sendKeys(USER_PASSWORD);
driver2.findElement(By.id("password")).submit();
Assert.assertTrue("Must be on session page", driver2.getTitle().equals(SessionPage.PAGE_TITLE));
Assert.assertTrue("Counter must be 0", checkCounterInSource(driver2, 0));
// Counter increased now
driver2.navigate().to(SERVLET_URL);
Assert.assertTrue("Counter must be 1", checkCounterInSource(driver2, 1));
// Logout in browser1
driver.navigate().to(logoutPage(SERVLET_URL));
// Assert that I am logged out in browser1
driver.navigate().to(SERVLET_URL);
Assert.assertTrue("Must be on login page", loginPage.isCurrent());
// Assert that I am still logged in browser2 and same session is still preserved
driver2.navigate().to(SERVLET_URL);
Assert.assertTrue("Must be on session page", driver2.getTitle().equals(SessionPage.PAGE_TITLE));
Assert.assertTrue("Counter must be 2", checkCounterInSource(driver2, 2));
driver2.navigate().to(logoutPage(SERVLET_URL));
Assert.assertTrue("Must be on login page", driver2.getTitle().toLowerCase().startsWith("log in to"));
}
@Test
public void testSessionInvalidatedAfterFailedRefresh() {
RealmResource realmResource = adminClient.realm(REALM_NAME);
RealmRepresentation realmRep = realmResource.toRepresentation();
ClientResource clientResource = null;
for (ClientRepresentation clientRep : realmResource.clients().findAll()) {
if (CLIENT_ID.equals(clientRep.getClientId())) {
clientResource = realmResource.clients().get(clientRep.getId());
}
}
Assert.assertNotNull(clientResource);
clientResource.toRepresentation().setAdminUrl("");
int origTokenLifespan = realmRep.getAccessCodeLifespan();
realmRep.setAccessCodeLifespan(1);
realmResource.update(realmRep);
// Login
loginAndCheckSession();
// Logout
String logoutUri = logoutPage(SERVLET_URL);
driver.navigate().to(logoutUri);
// Assert that http session was invalidated
driver.navigate().to(SERVLET_URL);
Assert.assertTrue("Must be on login page", loginPage.isCurrent());
loginPage.login(USER_LOGIN, USER_PASSWORD);
Assert.assertTrue("Must be on session page", sessionPage.isCurrent());
Assert.assertEquals("Counter must be 0", 0, sessionPage.getCounter());
clientResource.toRepresentation().setAdminUrl(BASE_URL);
realmRep.setAccessCodeLifespan(origTokenLifespan);
realmResource.update(realmRep);
}
@Test
public void testAdminApplicationLogout() {
loginAndCheckSession();
// logout user2 with admin client
UserRepresentation correct2 = realmsResouce().realm(REALM_NAME)
.users().search(USER_LOGIN_CORRECT_2, null, null, null, null, null).get(0);
realmsResouce().realm(REALM_NAME).users().get(correct2.getId()).logout();
// user1 should be still logged with original httpSession in our browser window
driver.navigate().to(SERVLET_URL);
Assert.assertTrue("Must be on session page", sessionPage.isCurrent());
Assert.assertEquals("Counter must be 2", 2, sessionPage.getCounter());
driver.navigate().to(logoutPage(SERVLET_URL));
}
@Test
public void testAccountManagementSessionsLogout() {
loginAndCheckSession();
realmSessions.navigateTo();
realmSessions.logoutAll();
// Assert I need to login again (logout was propagated to the app)
loginAndCheckSession();
}
}