diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html index 3706d74f0f..e23199ac01 100755 --- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html +++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html @@ -68,12 +68,12 @@
- +
- +
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingResponseBuilder.java index 0d7d4f5df0..a1145a16d5 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingResponseBuilder.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingResponseBuilder.java @@ -146,7 +146,7 @@ public class SAML2PostBindingResponseBuilder { return this; } - public Response error(String status) throws ConfigurationException, ProcessingException, IOException { + public Response buildErrorResponse(String status) throws ConfigurationException, ProcessingException, IOException { Document doc = getErrorResponse(status); return buildResponse(doc); @@ -207,7 +207,7 @@ public class SAML2PostBindingResponseBuilder { return samlResponse; } - public Response build() throws ConfigurationException, ProcessingException, IOException { + public Response buildLoginResponse() throws ConfigurationException, ProcessingException, IOException { Document responseDoc = getResponse(); return buildResponse(responseDoc); } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLogin.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLogin.java index 8344170e5d..dfc4081cee 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLogin.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLogin.java @@ -1,8 +1,10 @@ package org.keycloak.protocol.saml; import org.jboss.logging.Logger; -import org.jboss.resteasy.spi.HttpRequest; -import org.keycloak.ClientConnection; +import org.jboss.resteasy.client.ClientRequest; +import org.jboss.resteasy.client.ClientResponse; +import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor; +import org.keycloak.models.ApplicationModel; import org.keycloak.models.ClaimMask; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; @@ -13,17 +15,31 @@ import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.LoginProtocol; import org.keycloak.services.managers.ClientSessionCode; +import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.resources.RealmsResource; import org.keycloak.services.resources.flows.Flows; import org.picketlink.common.constants.GeneralConstants; import org.picketlink.common.constants.JBossSAMLURIConstants; import org.picketlink.common.exceptions.ConfigurationException; +import org.picketlink.common.exceptions.ParsingException; import org.picketlink.common.exceptions.ProcessingException; +import org.picketlink.common.util.StringUtil; +import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request; import org.picketlink.identity.federation.core.saml.v2.constants.X500SAMLProfileConstants; +import org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil; +import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil; +import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS; +import org.picketlink.identity.federation.saml.v2.assertion.NameIDType; +import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType; +import org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler; +import org.picketlink.identity.federation.web.util.PostBindingUtil; +import org.w3c.dom.Document; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import java.io.IOException; +import java.net.URI; +import java.security.Principal; /** * @author Bill Burke @@ -39,11 +55,8 @@ public class SamlLogin implements LoginProtocol { protected RealmModel realm; - protected HttpRequest request; - protected UriInfo uriInfo; - protected ClientConnection clientConnection; @Override @@ -58,24 +71,12 @@ public class SamlLogin implements LoginProtocol { return this; } - @Override - public SamlLogin setRequest(HttpRequest request) { - this.request = request; - return this; - } - @Override public SamlLogin setUriInfo(UriInfo uriInfo) { this.uriInfo = uriInfo; return this; } - @Override - public SamlLogin setClientConnection(ClientConnection clientConnection) { - this.clientConnection = clientConnection; - return this; - } - @Override public Response cancelLogin(ClientSessionModel clientSession) { return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get()); @@ -100,7 +101,7 @@ public class SamlLogin implements LoginProtocol { .responseIssuer(responseIssuer) .requestIssuer(clientSession.getClient().getClientId()); try { - return builder.error(status); + return builder.buildErrorResponse(status); } catch (Exception e) { return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response"); } @@ -140,7 +141,7 @@ public class SamlLogin implements LoginProtocol { } try { - return builder.build(); + return builder.buildLoginResponse(); } catch (Exception e) { logger.error("failed", e); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response"); @@ -163,6 +164,66 @@ public class SamlLogin implements LoginProtocol { return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get()); } + @Override + public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) { + ClientModel client = clientSession.getClient(); + if (!(client instanceof ApplicationModel)) return; + ApplicationModel app = (ApplicationModel)client; + if (app.getManagementUrl() == null) return; + + String logoutRequestString = null; + try { + LogoutRequestType logoutRequest = createLogoutRequest(userSession.getUser(), client); + Document logoutRequestDocument = new SAML2Request().convert(logoutRequest); + + byte[] responseBytes = DocumentUtil.getDocumentAsString(logoutRequestDocument).getBytes("UTF-8"); + logoutRequestString = PostBindingUtil.base64Encode(new String(responseBytes)); + } catch (Exception e) { + logger.warn("failed to send saml logout", e); + } + + + String adminUrl = ResourceAdminManager.getManagementUrl(uriInfo.getRequestUri(), app); + + ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor(); + + + try { + ClientRequest request = executor.createRequest(adminUrl); + request.formParameter(GeneralConstants.SAML_REQUEST_KEY, logoutRequestString); + request.formParameter(SAML2LogOutHandler.BACK_CHANNEL_LOGOUT, SAML2LogOutHandler.BACK_CHANNEL_LOGOUT); + ClientResponse response = null; + try { + response = request.post(); + } catch (Exception e) { + logger.warn("failed to send saml logout", e); + } + response.releaseConnection(); + + } finally { + executor.getHttpClient().getConnectionManager().shutdown(); + } + + } + + private LogoutRequestType createLogoutRequest(UserModel user, ClientModel client) throws ConfigurationException, ProcessingException { + LogoutRequestType lort = new SAML2Request().createLogoutRequest(getResponseIssuer(realm)); + + NameIDType nameID = new NameIDType(); + nameID.setValue(user.getUsername()); + //Deal with NameID Format + String nameIDFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get(); + nameID.setFormat(URI.create(nameIDFormat)); + lort.setNameID(nameID); + + long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout(); + + lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionValidity)); + lort.setDestination(URI.create(client.getClientId())); + return lort; + } + + @Override public void close() { diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java index a5ed37d4e2..3449d65a6b 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -26,6 +26,7 @@ import org.picketlink.common.constants.GeneralConstants; import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder; import org.picketlink.identity.federation.saml.v2.SAML2Object; import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType; +import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; @@ -94,38 +95,66 @@ public class SamlService { @Path("POST") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public Response loginPage(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest, - @FormParam(GeneralConstants.RELAY_STATE) String relayState) { - event.event(EventType.LOGIN); + public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest, + @FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse, + @FormParam(GeneralConstants.RELAY_STATE) String relayState) { if (!checkSsl()) { + event.event(EventType.LOGIN_ERROR); event.error(Errors.SSL_REQUIRED); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required"); } if (!realm.isEnabled()) { + event.event(EventType.LOGIN_ERROR); event.error(Errors.REALM_DISABLED); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled"); } - if (samlRequest == null) { + if (samlRequest == null && samlResponse == null) { + event.event(EventType.LOGIN_ERROR); event.error(Errors.INVALID_TOKEN); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request"); } + if (samlRequest != null) return handleSamlRequest(samlRequest, relayState); + else return handleSamlResponse(samlResponse, relayState); + } + + protected Response handleSamlResponse(String samleResponse, String relayState) { + event.event(EventType.LOGIN_ERROR); + event.error(Errors.INVALID_TOKEN); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request"); + } + + + protected Response handleSamlRequest(String samlRequest, String relayState) { SAMLDocumentHolder documentHolder = SAMLRequestParser.parsePostBinding(samlRequest); if (documentHolder == null) { + event.event(EventType.LOGIN_ERROR); event.error(Errors.INVALID_TOKEN); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request"); } SAML2Object samlObject = documentHolder.getSamlObject(); - if (!(samlObject instanceof AuthnRequestType)) { + + if (samlObject instanceof AuthnRequestType) { + event.event(EventType.LOGIN); + // Get the SAML Request Message + AuthnRequestType requestAbstractType = (AuthnRequestType) samlObject; + return loginRequest(relayState, requestAbstractType); + } else if (samlObject instanceof LogoutRequestType) { + event.event(EventType.LOGOUT); + LogoutRequestType requestAbstractType = (LogoutRequestType) samlObject; + return logoutRequest(relayState, requestAbstractType); + + } else { + event.event(EventType.LOGIN_ERROR); event.error(Errors.INVALID_TOKEN); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request"); } + } - // Get the SAML Request Message - AuthnRequestType requestAbstractType = (AuthnRequestType) samlObject; + protected Response loginRequest(String relayState, AuthnRequestType requestAbstractType) { String issuer = requestAbstractType.getIssuer().getValue(); ClientModel client = realm.findClient(issuer); @@ -189,29 +218,42 @@ public class SamlService { return forms.createLogin(); } + protected Response logoutRequest(String relayState, LogoutRequestType requestAbstractType) { + String issuer = requestAbstractType.getIssuer().getValue(); + ClientModel client = realm.findClient(issuer); - /** - * Logout user session. User must be logged in via a session cookie. - * - * @param redirectUri - * @return - */ - @Path("logout") - @GET - @NoCache - public Response logout(final @QueryParam("shit") String redirectUri) { - event.event(EventType.LOGOUT); - if (redirectUri != null) { - event.detail(Details.REDIRECT_URI, redirectUri); + if (client == null) { + event.error(Errors.CLIENT_NOT_FOUND); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester."); } + + if (!client.isEnabled()) { + event.error(Errors.CLIENT_DISABLED); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled."); + } + if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) { + event.error(Errors.NOT_ALLOWED); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login"); + } + if (client.isDirectGrantsOnly()) { + event.error(Errors.NOT_ALLOWED); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login"); + } + // authenticate identity cookie, but ignore an access token timeout as we're logging out anyways. AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false); if (authResult != null) { logout(authResult.getSession()); } + String redirectUri = null; + + if (client instanceof ApplicationModel) { + redirectUri = ((ApplicationModel)client).getBaseUrl(); + } + if (redirectUri != null) { - String validatedRedirect = OpenIDConnectService.verifyRealmRedirectUri(uriInfo, redirectUri, realm); + String validatedRedirect = OpenIDConnectService.verifyRedirectUri(uriInfo, redirectUri, realm, client);; if (validatedRedirect == null) { return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri."); } @@ -219,6 +261,7 @@ public class SamlService { } else { return Response.ok().build(); } + } private void logout(UserSessionModel userSession) { @@ -233,14 +276,4 @@ public class SamlService { return !realm.getSslRequired().isRequired(clientConnection); } } - - private Response createError(String error, String errorDescription, Response.Status status) { - Map e = new HashMap(); - e.put(OAuth2Constants.ERROR, error); - if (errorDescription != null) { - e.put(OAuth2Constants.ERROR_DESCRIPTION, errorDescription); - } - return Response.status(status).entity(e).type("application/json").build(); - } - } diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocol.java b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java index 86de30a657..47e672a4f1 100755 --- a/services/src/main/java/org/keycloak/protocol/LoginProtocol.java +++ b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java @@ -22,14 +22,12 @@ public interface LoginProtocol extends Provider { LoginProtocol setRealm(RealmModel realm); - LoginProtocol setRequest(HttpRequest request); - LoginProtocol setUriInfo(UriInfo uriInfo); - LoginProtocol setClientConnection(ClientConnection clientConnection); - Response cancelLogin(ClientSessionModel clientSession); Response invalidSessionError(ClientSessionModel clientSession); Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode); Response consentDenied(ClientSessionModel clientSession); + + void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnect.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnect.java index b3afe30d59..c67960c6ea 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnect.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnect.java @@ -22,15 +22,18 @@ package org.keycloak.protocol.oidc; import org.jboss.logging.Logger; +import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.ClientConnection; import org.keycloak.OAuth2Constants; +import org.keycloak.models.ApplicationModel; import org.keycloak.models.ClientSessionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.LoginProtocol; import org.keycloak.services.managers.ClientSessionCode; +import org.keycloak.services.managers.ResourceAdminManager; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; @@ -56,19 +59,12 @@ public class OpenIDConnect implements LoginProtocol { protected RealmModel realm; - protected HttpRequest request; - protected UriInfo uriInfo; - protected ClientConnection clientConnection; - - public OpenIDConnect(KeycloakSession session, RealmModel realm, HttpRequest request, UriInfo uriInfo, - ClientConnection clientConnection) { + public OpenIDConnect(KeycloakSession session, RealmModel realm, UriInfo uriInfo) { this.session = session; this.realm = realm; - this.request = request; this.uriInfo = uriInfo; - this.clientConnection = clientConnection; } public OpenIDConnect() { @@ -86,24 +82,12 @@ public class OpenIDConnect implements LoginProtocol { return this; } - @Override - public OpenIDConnect setRequest(HttpRequest request) { - this.request = request; - return this; - } - @Override public OpenIDConnect setUriInfo(UriInfo uriInfo) { this.uriInfo = uriInfo; return this; } - @Override - public OpenIDConnect setClientConnection(ClientConnection clientConnection) { - this.clientConnection = clientConnection; - return this; - } - @Override public Response cancelLogin(ClientSessionModel clientSession) { String redirect = clientSession.getRedirectUri(); @@ -151,6 +135,19 @@ public class OpenIDConnect implements LoginProtocol { return Response.status(302).location(redirectUri.build()).build(); } + @Override + public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) { + if (!(clientSession.getClient() instanceof ApplicationModel)) return; + ApplicationModel app = (ApplicationModel)clientSession.getClient(); + ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor(); + + try { + new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, app, null, userSession.getId(), executor, 0); + } finally { + executor.getHttpClient().getConnectionManager().shutdown(); + } + } + @Override public void close() { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java index b0574e9b6c..b88648e730 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java @@ -801,7 +801,7 @@ public class OpenIDConnectService { if (response != null) return response; if (prompt != null && prompt.equals("none")) { - OpenIDConnect oauth = new OpenIDConnect(session, realm, request, uriInfo, clientConnection); + OpenIDConnect oauth = new OpenIDConnect(session, realm, uriInfo); return oauth.cancelLogin(clientSession); } diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 78ba53df90..450dcf68d1 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -86,7 +86,17 @@ public class AuthenticationManager { expireIdentityCookie(realm, uriInfo, connection); expireRememberMeCookie(realm, uriInfo, connection); - new ResourceAdminManager().logoutSession(uriInfo.getRequestUri(), realm, userSession); + for (ClientSessionModel clientSession : userSession.getClientSessions()) { + ClientModel client = clientSession.getClient(); + if (client instanceof ApplicationModel) { + String authMethod = clientSession.getAuthMethod(); + if (authMethod == null) continue; // must be a keycloak service like account + LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod); + protocol.setRealm(realm) + .setUriInfo(uriInfo); + protocol.backchannelLogout(userSession, clientSession); + } + } session.sessions().removeUserSession(realm, userSession); } @@ -235,9 +245,7 @@ public class AuthenticationManager { if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection); LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod()); protocol.setRealm(realm) - .setRequest(request) - .setUriInfo(uriInfo) - .setClientConnection(clientConnection); + .setUriInfo(uriInfo); return protocol.authenticated(userSession, new ClientSessionCode(realm, clientSession)); } diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java index 7d233935b7..105751fa13 100755 --- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java @@ -102,7 +102,7 @@ public class ResourceAdminManager { } - protected String getManagementUrl(URI requestUri, ApplicationModel application) { + public static String getManagementUrl(URI requestUri, ApplicationModel application) { String mgmtUrl = application.getManagementUrl(); if (mgmtUrl == null || mgmtUrl.equals("")) { return null; @@ -234,7 +234,7 @@ public class ResourceAdminManager { } - protected boolean logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource, String user, String session, ApacheHttpClient4Executor client, int notBefore) { + public boolean logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource, String user, String session, ApacheHttpClient4Executor client, int notBefore) { String managementUrl = getManagementUrl(requestUri, resource); if (managementUrl != null) { LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getName(), user, session, notBefore); diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index c6bcc8cd51..7310d74abf 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -309,9 +309,7 @@ public class LoginActionsService { event.error(Errors.REJECTED_BY_USER); LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod()); protocol.setRealm(realm) - .setRequest(request) - .setUriInfo(uriInfo) - .setClientConnection(clientConnection); + .setUriInfo(uriInfo); return protocol.cancelLogin(clientSession); } @@ -565,9 +563,7 @@ public class LoginActionsService { LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod()); protocol.setRealm(realm) - .setRequest(request) - .setUriInfo(uriInfo) - .setClientConnection(clientConnection); + .setUriInfo(uriInfo); if (formData.containsKey("cancel")) { event.error(Errors.REJECTED_BY_USER); return protocol.consentDenied(clientSession); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java index 10bf32171c..28bd4b61b1 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java @@ -40,8 +40,10 @@ import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.OpenIDConnectService; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.adapters.action.SessionStats; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.resources.admin.AdminRoot; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.pages.LoginPage; @@ -71,7 +73,6 @@ import java.util.Map; * * @author Bill Burke */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class AdapterTest { public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString(); @@ -157,17 +158,17 @@ public class AdapterTest { Client client = ClientBuilder.newClient(); UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth"); WebTarget adminTarget = client.target(AdminRoot.realmsUrl(authBase)).path("demo"); - Map stats = adminTarget.path("application-session-stats").request() + Map stats = adminTarget.path("session-stats").request() .header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken) - .get(new GenericType>() { + .get(new GenericType>() { }); - Integer custSessionsCount = stats.get("customer-portal"); - Assert.assertNotNull(custSessionsCount); - Assert.assertTrue(1 == custSessionsCount); - Integer prodStatsCount = stats.get("product-portal"); - Assert.assertNotNull(prodStatsCount); - Assert.assertTrue(1 == prodStatsCount); + SessionStats custStats = stats.get("customer-portal"); + Assert.assertNotNull(custStats); + Assert.assertEquals(1, custStats.getActiveSessions()); + SessionStats prodStats = stats.get("product-portal"); + Assert.assertNotNull(prodStats); + Assert.assertEquals(1, prodStats.getActiveSessions()); client.close(); @@ -296,6 +297,9 @@ public class AdapterTest { session = keycloakRule.startSession(); realm = session.realms().getRealmByName("demo"); + // need to cleanup so other tests don't fail, so invalidate http sessions on remote clients. + UserModel user = session.users().getUserByUsername("bburke@redhat.com", realm); + new ResourceAdminManager().logoutUser(null, realm, user.getId(), null); realm.setSsoSessionIdleTimeout(originalIdle); session.getTransaction().commit(); session.close(); diff --git a/testsuite/integration/src/test/resources/testsaml.json b/testsuite/integration/src/test/resources/testsaml.json index 6dc9d71c33..4c0687574b 100755 --- a/testsuite/integration/src/test/resources/testsaml.json +++ b/testsuite/integration/src/test/resources/testsaml.json @@ -32,6 +32,8 @@ "name": "http://localhost:8080/sales-post/", "enabled": true, "fullScopeAllowed": true, + "baseUrl": "http://localhost:8080/sales-post/", + "adminUrl": "http://localhost:8080/sales-post/", "redirectUris": [ "http://localhost:8080/sales-post/*" ]