KEYCLOAK-431 View open sessions, and logout all sessions, through account management
This commit is contained in:
parent
f7c3373f75
commit
f4f9b1e323
20 changed files with 298 additions and 26 deletions
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.util;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -9,4 +11,8 @@ public class Time {
|
|||
return (int) (System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
public static Date toDate(int time) {
|
||||
return new Date(((long) time ) * 1000);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.codehaus.jackson.map.SerializationConfig;
|
|||
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.adapters.installed.KeycloakInstalled;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
@ -65,7 +66,7 @@ public class CustomerCli {
|
|||
System.out.println(mapper.writeValueAsString(keycloak.getIdToken()));
|
||||
} else if (s.equals("refresh")) {
|
||||
keycloak.refreshToken();
|
||||
System.out.println("Token refreshed: expires at " + new Date(keycloak.getToken().getExpiration() * 1000));
|
||||
System.out.println("Token refreshed: expires at " + Time.toDate(keycloak.getToken().getExpiration()));
|
||||
} else if (s.equals("exit")) {
|
||||
System.exit(0);
|
||||
} else {
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.account;
|
|||
import org.keycloak.audit.Event;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -31,5 +32,7 @@ public interface Account {
|
|||
|
||||
Account setEvents(List<Event> events);
|
||||
|
||||
Account setSessions(List<UserSessionModel> sessions);
|
||||
|
||||
Account setFeatures(boolean social, boolean audit, boolean passwordUpdateSupported);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ package org.keycloak.account;
|
|||
*/
|
||||
public enum AccountPages {
|
||||
|
||||
ACCOUNT, PASSWORD, TOTP, SOCIAL, LOG;
|
||||
ACCOUNT, PASSWORD, TOTP, SOCIAL, LOG, SESSIONS;
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.account.freemarker.model.FeaturesBean;
|
|||
import org.keycloak.account.freemarker.model.LogBean;
|
||||
import org.keycloak.account.freemarker.model.MessageBean;
|
||||
import org.keycloak.account.freemarker.model.ReferrerBean;
|
||||
import org.keycloak.account.freemarker.model.SessionsBean;
|
||||
import org.keycloak.account.freemarker.model.TotpBean;
|
||||
import org.keycloak.account.freemarker.model.UrlBean;
|
||||
import org.keycloak.audit.Event;
|
||||
|
@ -19,6 +20,7 @@ import org.keycloak.freemarker.ThemeLoader;
|
|||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -43,6 +45,7 @@ public class FreeMarkerAccount implements Account {
|
|||
private RealmModel realm;
|
||||
private String[] referrer;
|
||||
private List<Event> events;
|
||||
private List<UserSessionModel> sessions;
|
||||
private boolean social;
|
||||
private boolean audit;
|
||||
private boolean passwordUpdateSupported;
|
||||
|
@ -100,7 +103,7 @@ public class FreeMarkerAccount implements Account {
|
|||
attributes.put("referrer", new ReferrerBean(referrer));
|
||||
}
|
||||
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri, baseQueryUri));
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri, baseQueryUri, uriInfo.getRequestUri()));
|
||||
|
||||
attributes.put("features", new FeaturesBean(social, audit, passwordUpdateSupported));
|
||||
|
||||
|
@ -116,6 +119,10 @@ public class FreeMarkerAccount implements Account {
|
|||
break;
|
||||
case LOG:
|
||||
attributes.put("log", new LogBean(events));
|
||||
break;
|
||||
case SESSIONS:
|
||||
attributes.put("sessions", new SessionsBean(sessions));
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -178,6 +185,12 @@ public class FreeMarkerAccount implements Account {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account setSessions(List<UserSessionModel> sessions) {
|
||||
this.sessions = sessions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account setFeatures(boolean social, boolean audit, boolean passwordUpdateSupported) {
|
||||
this.social = social;
|
||||
|
|
|
@ -19,6 +19,8 @@ public class Templates {
|
|||
return "social.ftl";
|
||||
case LOG:
|
||||
return "log.ftl";
|
||||
case SESSIONS:
|
||||
return "sessions.ftl";
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package org.keycloak.account.freemarker.model;
|
||||
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class SessionsBean {
|
||||
|
||||
private List<UserSessionBean> events;
|
||||
|
||||
public SessionsBean(List<UserSessionModel> sessions) {
|
||||
this.events = new LinkedList<UserSessionBean>();
|
||||
for (UserSessionModel session : sessions) {
|
||||
this.events.add(new UserSessionBean(session));
|
||||
}
|
||||
}
|
||||
|
||||
public List<UserSessionBean> getSessions() {
|
||||
return events;
|
||||
}
|
||||
|
||||
public static class UserSessionBean {
|
||||
|
||||
private UserSessionModel session;
|
||||
|
||||
public UserSessionBean(UserSessionModel session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return session.getIpAddress();
|
||||
}
|
||||
|
||||
public Date getStarted() {
|
||||
return Time.toDate(session.getStarted());
|
||||
}
|
||||
|
||||
public Date getExpires() {
|
||||
return Time.toDate(session.getExpires());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@ package org.keycloak.account.freemarker.model;
|
|||
|
||||
import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.resources.TokenService;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
|
||||
import java.net.URI;
|
||||
|
@ -15,12 +16,14 @@ public class UrlBean {
|
|||
private Theme theme;
|
||||
private URI baseURI;
|
||||
private URI baseQueryURI;
|
||||
private URI currentURI;
|
||||
|
||||
public UrlBean(RealmModel realm, Theme theme, URI baseURI, URI baseQueryURI) {
|
||||
public UrlBean(RealmModel realm, Theme theme, URI baseURI, URI baseQueryURI, URI currentURI) {
|
||||
this.realm = realm.getName();
|
||||
this.theme = theme;
|
||||
this.baseURI = baseURI;
|
||||
this.baseQueryURI = baseQueryURI;
|
||||
this.currentURI = currentURI;
|
||||
}
|
||||
|
||||
public String getAccessUrl() {
|
||||
|
@ -47,12 +50,20 @@ public class UrlBean {
|
|||
return Urls.accountLogPage(baseQueryURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getSessionsUrl() {
|
||||
return Urls.accountSessionsPage(baseQueryURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getSessionsLogoutUrl() {
|
||||
return Urls.accountSessionsLogoutPage(baseQueryURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getTotpRemoveUrl() {
|
||||
return Urls.accountTotpRemove(baseQueryURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getLogoutUrl() {
|
||||
return Urls.accountLogout(baseQueryURI, realm).toString();
|
||||
return Urls.accountLogout(baseQueryURI, currentURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getResourcesPath() {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.mainLayout active='sessions' bodyClass='sessions'; section>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<h2>Sessions</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>IP</td>
|
||||
<td>Started</td>
|
||||
<td>Expires</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<#list sessions.sessions as session>
|
||||
<tr>
|
||||
<td>${session.ipAddress}</td>
|
||||
<td>${session.started?datetime}</td>
|
||||
<td>${session.expires?datetime}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
<a id="logout-all-sessions" href="${url.sessionsLogoutUrl}">Logout all sessions</a>
|
||||
|
||||
</@layout.mainLayout>
|
|
@ -43,6 +43,7 @@
|
|||
<#if features.passwordUpdateSupported><li class="<#if active=='password'>active</#if>"><a href="${url.passwordUrl}">Password</a></li></#if>
|
||||
<li class="<#if active=='totp'>active</#if>"><a href="${url.totpUrl}">Authenticator</a></li>
|
||||
<#if features.social><li class="<#if active=='social'>active</#if>"><a href="${url.socialUrl}">Social</a></li></#if>
|
||||
<li class="<#if active=='sessions'>active</#if>"><a href="${url.sessionsUrl}">Sessions</a></li>
|
||||
<#if features.log><li class="<#if active=='log'>active</#if>"><a href="${url.logUrl}">Log</a></li></#if>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -257,6 +257,8 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
|
|||
|
||||
UserSessionModel getUserSession(String id);
|
||||
|
||||
List<UserSessionModel> getUserSessions(UserModel user);
|
||||
|
||||
void removeUserSession(UserSessionModel session);
|
||||
|
||||
void removeUserSessions(UserModel user);
|
||||
|
|
|
@ -49,6 +49,7 @@ import java.util.Comparator;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -1405,6 +1406,15 @@ public class RealmAdapter implements RealmModel {
|
|||
return entity != null ? new UserSessionAdapter(entity) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessions(UserModel user) {
|
||||
List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
|
||||
for (UserSessionEntity e : em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class).setParameter("user", ((UserAdapter) user).getUser()).getResultList()) {
|
||||
sessions.add(new UserSessionAdapter(e));
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSession(UserSessionModel session) {
|
||||
em.remove(((UserSessionAdapter) session).getEntity());
|
||||
|
|
|
@ -15,6 +15,7 @@ import javax.persistence.NamedQuery;
|
|||
*/
|
||||
@Entity
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.user = :user"),
|
||||
@NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.user = :user"),
|
||||
@NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.expires < :currentTime")
|
||||
})
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -1375,6 +1376,16 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessions(UserModel user) {
|
||||
DBObject query = new BasicDBObject("user", user.getId());
|
||||
List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
|
||||
for (MongoUserSessionEntity e : getMongoStore().loadEntities(MongoUserSessionEntity.class, query, invocationContext)) {
|
||||
sessions.add(new UserSessionAdapter(e, this, invocationContext));
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSession(UserSessionModel session) {
|
||||
getMongoStore().removeEntity(((UserSessionAdapter) session).getEntity(), invocationContext);
|
||||
|
|
|
@ -241,10 +241,6 @@ public class AccountService {
|
|||
return forwardToPage("social", AccountPages.SOCIAL);
|
||||
}
|
||||
|
||||
public static UriBuilder logUrl(UriBuilder base) {
|
||||
return RealmsResource.accountUrl(base).path(AccountService.class, "logPage");
|
||||
}
|
||||
|
||||
@Path("log")
|
||||
@GET
|
||||
public Response logPage() {
|
||||
|
@ -269,6 +265,15 @@ public class AccountService {
|
|||
return forwardToPage("log", AccountPages.LOG);
|
||||
}
|
||||
|
||||
@Path("sessions")
|
||||
@GET
|
||||
public Response sessionsPage() {
|
||||
if (auth != null) {
|
||||
account.setSessions(realm.getUserSessions(auth.getUser()));
|
||||
}
|
||||
return forwardToPage("sessions", AccountPages.SESSIONS);
|
||||
}
|
||||
|
||||
@Path("/")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
|
@ -314,6 +319,18 @@ public class AccountService {
|
|||
return account.setSuccess("successTotpRemoved").createResponse(AccountPages.TOTP);
|
||||
}
|
||||
|
||||
|
||||
@Path("sessions-logout")
|
||||
@GET
|
||||
public Response processSessionsLogout() {
|
||||
require(AccountRoles.MANAGE_ACCOUNT);
|
||||
|
||||
UserModel user = auth.getUser();
|
||||
realm.removeUserSessions(user);
|
||||
|
||||
return Response.seeOther(Urls.accountSessionsPage(uriInfo.getBaseUri(), realm.getName())).build();
|
||||
}
|
||||
|
||||
@Path("totp")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
|
@ -493,16 +510,6 @@ public class AccountService {
|
|||
}
|
||||
}
|
||||
|
||||
@Path("logout")
|
||||
@GET
|
||||
public Response logout() {
|
||||
URI redirect = Urls.accountBase(uriInfo.getBaseUri()).build(realm.getName());
|
||||
|
||||
return Response.status(302).location(
|
||||
TokenService.logoutUrl(uriInfo).queryParam("redirect_uri", redirect.toString()).build(realm.getName())
|
||||
).build();
|
||||
}
|
||||
|
||||
private Response login(String path) {
|
||||
OAuthRedirect oauth = new OAuthRedirect();
|
||||
String authUrl = Urls.realmLoginPage(uriInfo.getBaseUri(), realm.getName()).toString();
|
||||
|
|
|
@ -76,8 +76,16 @@ public class Urls {
|
|||
return accountBase(baseUri).path(AccountService.class, "logPage").build(realmId);
|
||||
}
|
||||
|
||||
public static URI accountLogout(URI baseUri, String realmId) {
|
||||
return accountBase(baseUri).path(AccountService.class, "logout").build(realmId);
|
||||
public static URI accountSessionsPage(URI baseUri, String realmId) {
|
||||
return accountBase(baseUri).path(AccountService.class, "sessionsPage").build(realmId);
|
||||
}
|
||||
|
||||
public static URI accountSessionsLogoutPage(URI baseUri, String realmId) {
|
||||
return accountBase(baseUri).path(AccountService.class, "processSessionsLogout").build(realmId);
|
||||
}
|
||||
|
||||
public static URI accountLogout(URI baseUri, URI redirectUri, String realmId) {
|
||||
return realmLogout(baseUri).queryParam("redirect_uri", redirectUri).build(realmId);
|
||||
}
|
||||
|
||||
public static URI loginActionUpdatePassword(URI baseUri, String realmId) {
|
||||
|
@ -128,6 +136,10 @@ public class Urls {
|
|||
return tokenBase(baseUri).path(TokenService.class, "loginPage").build(realmId);
|
||||
}
|
||||
|
||||
public static UriBuilder realmLogout(URI baseUri) {
|
||||
return tokenBase(baseUri).path(TokenService.class, "logout");
|
||||
}
|
||||
|
||||
public static URI realmRegisterAction(URI baseUri, String realmId) {
|
||||
return tokenBase(baseUri).path(TokenService.class, "processRegister").build(realmId);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.keycloak.testsuite.OAuthClient;
|
|||
import org.keycloak.testsuite.Retry;
|
||||
import org.keycloak.testsuite.pages.AccountLogPage;
|
||||
import org.keycloak.testsuite.pages.AccountPasswordPage;
|
||||
import org.keycloak.testsuite.pages.AccountSessionsPage;
|
||||
import org.keycloak.testsuite.pages.AccountTotpPage;
|
||||
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
|
@ -130,6 +131,9 @@ public class AccountTest {
|
|||
@WebResource
|
||||
protected AccountLogPage logPage;
|
||||
|
||||
@WebResource
|
||||
protected AccountSessionsPage sessionsPage;
|
||||
|
||||
@WebResource
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
|
@ -212,7 +216,7 @@ public class AccountTest {
|
|||
|
||||
changePasswordPage.logout();
|
||||
|
||||
events.expectLogout(sessionId).detail(Details.REDIRECT_URI, ACCOUNT_URL).assertEvent();
|
||||
events.expectLogout(sessionId).detail(Details.REDIRECT_URI, AccountPasswordPage.PATH).assertEvent();
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
@ -414,4 +418,41 @@ public class AccountTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessions() {
|
||||
loginPage.open();
|
||||
loginPage.clickRegister();
|
||||
|
||||
registerPage.register("view", "sessions", "view-sessions@localhost", "view-sessions", "password", "password");
|
||||
|
||||
Event registerEvent = events.expectRegister("view-sessions", "view-sessions@localhost").assertEvent();
|
||||
String userId = registerEvent.getUserId();
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "view-sessions").assertEvent();
|
||||
|
||||
sessionsPage.open();
|
||||
|
||||
Assert.assertTrue(sessionsPage.isCurrent());
|
||||
|
||||
List<List<String>> sessions = sessionsPage.getSessions();
|
||||
Assert.assertEquals(1, sessions.size());
|
||||
Assert.assertEquals("127.0.0.1", sessions.get(0).get(0));
|
||||
|
||||
// Create second session
|
||||
WebDriver driver2 = WebRule.createWebDriver();
|
||||
OAuthClient oauth2 = new OAuthClient(driver2);
|
||||
oauth2.doLogin("view-sessions", "password");
|
||||
|
||||
Event login2Event = events.expectLogin().user(userId).detail(Details.USERNAME, "view-sessions").assertEvent();
|
||||
|
||||
sessionsPage.open();
|
||||
sessions = sessionsPage.getSessions();
|
||||
Assert.assertEquals(2, sessions.size());
|
||||
|
||||
sessionsPage.logoutAll();
|
||||
|
||||
events.expectLogout(registerEvent.getSessionId());
|
||||
events.expectLogout(login2Event.getSessionId());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,11 +21,10 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.pages;
|
||||
|
||||
import org.keycloak.services.resources.AccountService;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.util.LinkedList;
|
||||
|
@ -36,7 +35,7 @@ import java.util.List;
|
|||
*/
|
||||
public class AccountLogPage extends AbstractAccountPage {
|
||||
|
||||
private static String PATH = AccountService.logUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT)).build("test").toString();
|
||||
private static String PATH = Urls.accountLogPage(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT).build(), "test").toString();
|
||||
|
||||
public boolean isCurrent() {
|
||||
return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/log");
|
||||
|
|
|
@ -33,7 +33,7 @@ import javax.ws.rs.core.UriBuilder;
|
|||
*/
|
||||
public class AccountPasswordPage extends AbstractAccountPage {
|
||||
|
||||
private static String PATH = AccountService.passwordUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT)).build("test").toString();
|
||||
public static String PATH = AccountService.passwordUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT)).build("test").toString();
|
||||
|
||||
@FindBy(id = "password")
|
||||
private WebElement passwordInput;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.testsuite.pages;
|
||||
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class AccountSessionsPage extends AbstractAccountPage {
|
||||
|
||||
private static String PATH = Urls.accountSessionsPage(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT).build(), "test").toString();
|
||||
|
||||
@FindBy(id = "logout-all-sessions")
|
||||
private WebElement logoutAllLink;
|
||||
|
||||
public boolean isCurrent() {
|
||||
return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/sessions");
|
||||
}
|
||||
|
||||
public void open() {
|
||||
driver.navigate().to(PATH);
|
||||
}
|
||||
|
||||
public void logoutAll() {
|
||||
logoutAllLink.click();
|
||||
}
|
||||
|
||||
public List<List<String>> getSessions() {
|
||||
List<List<String>> table = new LinkedList<List<String>>();
|
||||
for (WebElement r : driver.findElements(By.tagName("tr"))) {
|
||||
List<String> row = new LinkedList<String>();
|
||||
for (WebElement col : r.findElements(By.tagName("td"))) {
|
||||
row.add(col.getText());
|
||||
}
|
||||
table.add(row);
|
||||
}
|
||||
table.remove(0);
|
||||
return table;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue