From 6d5ab0f66be0ce7f1cccb4b05bb7038ea88090a9 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Tue, 7 Oct 2014 18:06:02 -0400 Subject: [PATCH] saml backchannel logout --- .../partials/application-detail.html | 4 +- .../saml/SAML2PostBindingResponseBuilder.java | 4 +- .../org/keycloak/protocol/saml/SamlLogin.java | 99 +- .../keycloak/protocol/saml/SamlService.java | 95 +- .../org/keycloak/protocol/LoginProtocol.java | 6 +- .../keycloak/protocol/oidc/OpenIDConnect.java | 37 +- .../protocol/oidc/OpenIDConnectService.java | 2 +- .../managers/AuthenticationManager.java | 16 +- .../managers/ResourceAdminManager.java | 4 +- .../resources/LoginActionsService.java | 8 +- .../testsuite/adapter/AdapterTest.java | 850 +++++++++--------- .../src/test/resources/testsaml.json | 2 + 12 files changed, 615 insertions(+), 512 deletions(-) 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 7ee021b9a7..b310403b3d 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java @@ -800,7 +800,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 20616876fc..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().logoutUser(uriInfo.getRequestUri(), realm, user.getId(), 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 d754b7f886..8b55f007eb 100755 --- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java @@ -101,7 +101,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; @@ -231,7 +231,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 ce0d51f76a..4001dfad2c 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 @@ -1,421 +1,429 @@ -/* - * 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.adapter; - -import org.junit.Assert; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.keycloak.Config; -import org.keycloak.OAuth2Constants; -import org.keycloak.Version; -import org.keycloak.adapters.AdapterConstants; -import org.keycloak.models.ApplicationModel; -import org.keycloak.models.Constants; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -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.resources.admin.AdminRoot; -import org.keycloak.testsuite.OAuthClient; -import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.rule.AbstractKeycloakRule; -import org.keycloak.testsuite.rule.WebResource; -import org.keycloak.testsuite.rule.WebRule; -import org.keycloak.testutils.KeycloakServer; -import org.keycloak.util.BasicAuthHelper; -import org.openqa.selenium.WebDriver; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Form; -import javax.ws.rs.core.GenericType; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import java.net.URI; -import java.net.URL; -import java.security.PublicKey; -import java.util.Map; - -/** - * Tests Undertow Adapter - * - * @author Bill Burke - */ -public class AdapterTest { - - public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString(); - public static PublicKey realmPublicKey; - @ClassRule - public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() { - @Override - protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { - RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/adapter-test/demorealm.json"), RealmRepresentation.class); - RealmModel realm = manager.importRealm(representation); - - realmPublicKey = realm.getPublicKey(); - - URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json"); - deployApplication("customer-portal", "/customer-portal", CustomerServlet.class, url.getPath(), "user"); - url = getClass().getResource("/adapter-test/secure-portal-keycloak.json"); - deployApplication("secure-portal", "/secure-portal", CallAuthenticatedServlet.class, url.getPath(), "user", false); - url = getClass().getResource("/adapter-test/customer-db-keycloak.json"); - deployApplication("customer-db", "/customer-db", CustomerDatabaseServlet.class, url.getPath(), "user"); - url = getClass().getResource("/adapter-test/product-keycloak.json"); - deployApplication("product-portal", "/product-portal", ProductServlet.class, url.getPath(), "user"); - - } - }; - - private static String createToken() { - KeycloakSession session = keycloakRule.startSession(); - try { - RealmManager manager = new RealmManager(session); - - RealmModel adminRealm = manager.getRealm(Config.getAdminRealm()); - ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION); - TokenManager tm = new TokenManager(); - UserModel admin = session.users().getUserByUsername("admin", adminRealm); - UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false); - AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession); - return tm.encodeToken(adminRealm, token); - } finally { - keycloakRule.stopSession(session, true); - } - } - - - @Rule - public WebRule webRule = new WebRule(this); - - @WebResource - protected WebDriver driver; - - @WebResource - protected OAuthClient oauth; - - @WebResource - protected LoginPage loginPage; - - @Test - public void testLoginSSOAndLogout() throws Exception { - // test login to customer-portal which does a bearer request to customer-db - driver.navigate().to("http://localhost:8081/customer-portal"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - loginPage.login("bburke@redhat.com", "password"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); - String pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - - // test SSO - driver.navigate().to("http://localhost:8081/product-portal"); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/product-portal"); - pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad")); - - // View stats - String adminToken = createToken(); - - 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("session-stats").request() - .header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken) - .get(new GenericType>() { - }); - - 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(); - - - // test logout - - String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth")) - .queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8081/customer-portal").build("demo").toString(); - driver.navigate().to(logoutUri); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - driver.navigate().to("http://localhost:8081/product-portal"); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - driver.navigate().to("http://localhost:8081/customer-portal"); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - - - } - - @Test - public void testServletRequestLogout() throws Exception { - // test login to customer-portal which does a bearer request to customer-db - driver.navigate().to("http://localhost:8081/customer-portal"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - loginPage.login("bburke@redhat.com", "password"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); - String pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - - // test SSO - driver.navigate().to("http://localhost:8081/product-portal"); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/product-portal"); - pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad")); - - // back - driver.navigate().to("http://localhost:8081/customer-portal"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); - pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - // test logout - - driver.navigate().to("http://localhost:8081/customer-portal/logout"); - - - - driver.navigate().to("http://localhost:8081/customer-portal"); - String currentUrl = driver.getCurrentUrl(); - Assert.assertTrue(currentUrl.startsWith(LOGIN_URL)); - driver.navigate().to("http://localhost:8081/product-portal"); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - - - } - - @Test - public void testLoginSSOIdle() throws Exception { - // test login to customer-portal which does a bearer request to customer-db - driver.navigate().to("http://localhost:8081/customer-portal"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - loginPage.login("bburke@redhat.com", "password"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); - String pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - - KeycloakSession session = keycloakRule.startSession(); - RealmModel realm = session.realms().getRealmByName("demo"); - int originalIdle = realm.getSsoSessionIdleTimeout(); - realm.setSsoSessionIdleTimeout(1); - session.getTransaction().commit(); - session.close(); - - Thread.sleep(2000); - - - // test SSO - driver.navigate().to("http://localhost:8081/product-portal"); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - - session = keycloakRule.startSession(); - realm = session.realms().getRealmByName("demo"); - realm.setSsoSessionIdleTimeout(originalIdle); - session.getTransaction().commit(); - session.close(); - } - - @Test - public void testLoginSSOIdleRemoveExpiredUserSessions() throws Exception { - // test login to customer-portal which does a bearer request to customer-db - driver.navigate().to("http://localhost:8081/customer-portal"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - loginPage.login("bburke@redhat.com", "password"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); - String pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - - KeycloakSession session = keycloakRule.startSession(); - RealmModel realm = session.realms().getRealmByName("demo"); - int originalIdle = realm.getSsoSessionIdleTimeout(); - realm.setSsoSessionIdleTimeout(1); - session.getTransaction().commit(); - session.close(); - - Thread.sleep(2000); - - session = keycloakRule.startSession(); - realm = session.realms().getRealmByName("demo"); - session.sessions().removeExpiredUserSessions(realm); - session.getTransaction().commit(); - session.close(); - - // test SSO - driver.navigate().to("http://localhost:8081/product-portal"); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - - session = keycloakRule.startSession(); - realm = session.realms().getRealmByName("demo"); - realm.setSsoSessionIdleTimeout(originalIdle); - session.getTransaction().commit(); - session.close(); - } - - @Test - public void testLoginSSOMax() throws Exception { - // test login to customer-portal which does a bearer request to customer-db - driver.navigate().to("http://localhost:8081/customer-portal"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - loginPage.login("bburke@redhat.com", "password"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); - String pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - - KeycloakSession session = keycloakRule.startSession(); - RealmModel realm = session.realms().getRealmByName("demo"); - int original = realm.getSsoSessionMaxLifespan(); - realm.setSsoSessionMaxLifespan(1); - session.getTransaction().commit(); - session.close(); - - Thread.sleep(2000); - - - // test SSO - driver.navigate().to("http://localhost:8081/product-portal"); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - - session = keycloakRule.startSession(); - realm = session.realms().getRealmByName("demo"); - realm.setSsoSessionMaxLifespan(original); - session.getTransaction().commit(); - session.close(); - } - - /** - * KEYCLOAK-518 - * @throws Exception - */ - @Test - public void testNullBearerToken() throws Exception { - Client client = ClientBuilder.newClient(); - WebTarget target = client.target("http://localhost:8081/customer-db"); - Response response = target.request().get(); - Assert.assertEquals(401, response.getStatus()); - response.close(); - response = target.request().header(HttpHeaders.AUTHORIZATION, "Bearer null").get(); - Assert.assertEquals(401, response.getStatus()); - response.close(); - client.close(); - - } - - /** - * KEYCLOAK-518 - * @throws Exception - */ - @Test - public void testBadUser() throws Exception { - Client client = ClientBuilder.newClient(); - UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT); - URI uri = OpenIDConnectService.grantAccessTokenUrl(builder).build("demo"); - WebTarget target = client.target(uri); - String header = BasicAuthHelper.createHeader("customer-portal", "password"); - Form form = new Form(); - form.param("username", "monkey@redhat.com") - .param("password", "password"); - Response response = target.request() - .header(HttpHeaders.AUTHORIZATION, header) - .post(Entity.form(form)); - Assert.assertEquals(400, response.getStatus()); - response.close(); - client.close(); - - } - - @Test - public void testVersion() throws Exception { - Client client = ClientBuilder.newClient(); - WebTarget target = client.target(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT).path("version"); - Version version = target.request().get(Version.class); - Assert.assertNotNull(version); - Assert.assertNotNull(version.getVersion()); - Assert.assertNotNull(version.getBuildTime()); - Assert.assertNotEquals(version.getVersion(), Version.UNKNOWN); - Assert.assertNotEquals(version.getBuildTime(), Version.UNKNOWN); - - Version version2 = client.target("http://localhost:8081/secure-portal").path(AdapterConstants.K_VERSION).request().get(Version.class); - Assert.assertNotNull(version2); - Assert.assertNotNull(version2.getVersion()); - Assert.assertNotNull(version2.getBuildTime()); - Assert.assertEquals(version.getVersion(), version2.getVersion()); - Assert.assertEquals(version.getBuildTime(), version2.getBuildTime()); - client.close(); - - } - - - - @Test - public void testAuthenticated() throws Exception { - // test login to customer-portal which does a bearer request to customer-db - driver.navigate().to("http://localhost:8081/secure-portal"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - loginPage.login("bburke@redhat.com", "password"); - System.out.println("Current url: " + driver.getCurrentUrl()); - Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/secure-portal"); - String pageSource = driver.getPageSource(); - System.out.println(pageSource); - Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - - // test logout - - String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth")) - .queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8081/secure-portal").build("demo").toString(); - driver.navigate().to(logoutUri); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - driver.navigate().to("http://localhost:8081/secure-portal"); - Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - } - - - -} +/* + * 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.adapter; + +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.Config; +import org.keycloak.OAuth2Constants; +import org.keycloak.Version; +import org.keycloak.adapters.AdapterConstants; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +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; +import org.keycloak.testsuite.rule.AbstractKeycloakRule; +import org.keycloak.testsuite.rule.WebResource; +import org.keycloak.testsuite.rule.WebRule; +import org.keycloak.testutils.KeycloakServer; +import org.keycloak.util.BasicAuthHelper; +import org.openqa.selenium.WebDriver; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; +import java.net.URL; +import java.security.PublicKey; +import java.util.Map; + +/** + * Tests Undertow Adapter + * + * @author Bill Burke + */ +public class AdapterTest { + + public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString(); + public static PublicKey realmPublicKey; + @ClassRule + public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() { + @Override + protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { + RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/adapter-test/demorealm.json"), RealmRepresentation.class); + RealmModel realm = manager.importRealm(representation); + + realmPublicKey = realm.getPublicKey(); + + URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json"); + deployApplication("customer-portal", "/customer-portal", CustomerServlet.class, url.getPath(), "user"); + url = getClass().getResource("/adapter-test/secure-portal-keycloak.json"); + deployApplication("secure-portal", "/secure-portal", CallAuthenticatedServlet.class, url.getPath(), "user", false); + url = getClass().getResource("/adapter-test/customer-db-keycloak.json"); + deployApplication("customer-db", "/customer-db", CustomerDatabaseServlet.class, url.getPath(), "user"); + url = getClass().getResource("/adapter-test/product-keycloak.json"); + deployApplication("product-portal", "/product-portal", ProductServlet.class, url.getPath(), "user"); + + } + }; + + private static String createToken() { + KeycloakSession session = keycloakRule.startSession(); + try { + RealmManager manager = new RealmManager(session); + + RealmModel adminRealm = manager.getRealm(Config.getAdminRealm()); + ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION); + TokenManager tm = new TokenManager(); + UserModel admin = session.users().getUserByUsername("admin", adminRealm); + UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false); + AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession); + return tm.encodeToken(adminRealm, token); + } finally { + keycloakRule.stopSession(session, true); + } + } + + + @Rule + public WebRule webRule = new WebRule(this); + + @WebResource + protected WebDriver driver; + + @WebResource + protected OAuthClient oauth; + + @WebResource + protected LoginPage loginPage; + + @Test + public void testLoginSSOAndLogout() throws Exception { + // test login to customer-portal which does a bearer request to customer-db + driver.navigate().to("http://localhost:8081/customer-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); + String pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + + // test SSO + driver.navigate().to("http://localhost:8081/product-portal"); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/product-portal"); + pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad")); + + // View stats + String adminToken = createToken(); + + 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("session-stats").request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + adminToken) + .get(new GenericType>() { + }); + + 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(); + + + // test logout + + String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth")) + .queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8081/customer-portal").build("demo").toString(); + driver.navigate().to(logoutUri); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + driver.navigate().to("http://localhost:8081/product-portal"); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + driver.navigate().to("http://localhost:8081/customer-portal"); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + + + } + + @Test + public void testServletRequestLogout() throws Exception { + // test login to customer-portal which does a bearer request to customer-db + driver.navigate().to("http://localhost:8081/customer-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); + String pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + + // test SSO + driver.navigate().to("http://localhost:8081/product-portal"); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/product-portal"); + pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad")); + + // back + driver.navigate().to("http://localhost:8081/customer-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); + pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + // test logout + + driver.navigate().to("http://localhost:8081/customer-portal/logout"); + + + + driver.navigate().to("http://localhost:8081/customer-portal"); + String currentUrl = driver.getCurrentUrl(); + Assert.assertTrue(currentUrl.startsWith(LOGIN_URL)); + driver.navigate().to("http://localhost:8081/product-portal"); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + + + } + + @Test + public void testLoginSSOIdle() throws Exception { + // test login to customer-portal which does a bearer request to customer-db + driver.navigate().to("http://localhost:8081/customer-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); + String pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("demo"); + int originalIdle = realm.getSsoSessionIdleTimeout(); + realm.setSsoSessionIdleTimeout(1); + session.getTransaction().commit(); + session.close(); + + Thread.sleep(2000); + + + // test SSO + driver.navigate().to("http://localhost:8081/product-portal"); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("demo"); + realm.setSsoSessionIdleTimeout(originalIdle); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testLoginSSOIdleRemoveExpiredUserSessions() throws Exception { + // test login to customer-portal which does a bearer request to customer-db + driver.navigate().to("http://localhost:8081/customer-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); + String pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("demo"); + int originalIdle = realm.getSsoSessionIdleTimeout(); + realm.setSsoSessionIdleTimeout(1); + session.getTransaction().commit(); + session.close(); + + Thread.sleep(2000); + + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("demo"); + session.sessions().removeExpiredUserSessions(realm); + session.getTransaction().commit(); + session.close(); + + // test SSO + driver.navigate().to("http://localhost:8081/product-portal"); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + + + + 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(); + + + } + + @Test + public void testLoginSSOMax() throws Exception { + // test login to customer-portal which does a bearer request to customer-db + driver.navigate().to("http://localhost:8081/customer-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal"); + String pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("demo"); + int original = realm.getSsoSessionMaxLifespan(); + realm.setSsoSessionMaxLifespan(1); + session.getTransaction().commit(); + session.close(); + + Thread.sleep(2000); + + + // test SSO + driver.navigate().to("http://localhost:8081/product-portal"); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName("demo"); + realm.setSsoSessionMaxLifespan(original); + session.getTransaction().commit(); + session.close(); + } + + /** + * KEYCLOAK-518 + * @throws Exception + */ + @Test + public void testNullBearerToken() throws Exception { + Client client = ClientBuilder.newClient(); + WebTarget target = client.target("http://localhost:8081/customer-db"); + Response response = target.request().get(); + Assert.assertEquals(401, response.getStatus()); + response.close(); + response = target.request().header(HttpHeaders.AUTHORIZATION, "Bearer null").get(); + Assert.assertEquals(401, response.getStatus()); + response.close(); + client.close(); + + } + + /** + * KEYCLOAK-518 + * @throws Exception + */ + @Test + public void testBadUser() throws Exception { + Client client = ClientBuilder.newClient(); + UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT); + URI uri = OpenIDConnectService.grantAccessTokenUrl(builder).build("demo"); + WebTarget target = client.target(uri); + String header = BasicAuthHelper.createHeader("customer-portal", "password"); + Form form = new Form(); + form.param("username", "monkey@redhat.com") + .param("password", "password"); + Response response = target.request() + .header(HttpHeaders.AUTHORIZATION, header) + .post(Entity.form(form)); + Assert.assertEquals(400, response.getStatus()); + response.close(); + client.close(); + + } + + @Test + public void testVersion() throws Exception { + Client client = ClientBuilder.newClient(); + WebTarget target = client.target(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT).path("version"); + Version version = target.request().get(Version.class); + Assert.assertNotNull(version); + Assert.assertNotNull(version.getVersion()); + Assert.assertNotNull(version.getBuildTime()); + Assert.assertNotEquals(version.getVersion(), Version.UNKNOWN); + Assert.assertNotEquals(version.getBuildTime(), Version.UNKNOWN); + + Version version2 = client.target("http://localhost:8081/secure-portal").path(AdapterConstants.K_VERSION).request().get(Version.class); + Assert.assertNotNull(version2); + Assert.assertNotNull(version2.getVersion()); + Assert.assertNotNull(version2.getBuildTime()); + Assert.assertEquals(version.getVersion(), version2.getVersion()); + Assert.assertEquals(version.getBuildTime(), version2.getBuildTime()); + client.close(); + + } + + + + @Test + public void testAuthenticated() throws Exception { + // test login to customer-portal which does a bearer request to customer-db + driver.navigate().to("http://localhost:8081/secure-portal"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + loginPage.login("bburke@redhat.com", "password"); + System.out.println("Current url: " + driver.getCurrentUrl()); + Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/secure-portal"); + String pageSource = driver.getPageSource(); + System.out.println(pageSource); + Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + + // test logout + + String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth")) + .queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8081/secure-portal").build("demo").toString(); + driver.navigate().to(logoutUri); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + driver.navigate().to("http://localhost:8081/secure-portal"); + Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); + } + + + +} 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/*" ]