saml frontchannel logout

This commit is contained in:
Bill Burke 2015-01-28 17:09:13 -05:00
parent 15feb39ecc
commit ea3bba98aa
15 changed files with 182 additions and 19 deletions

View file

@ -22,6 +22,7 @@ public class ApplicationRepresentation {
protected Integer notBefore; protected Integer notBefore;
protected Boolean bearerOnly; protected Boolean bearerOnly;
protected Boolean publicClient; protected Boolean publicClient;
protected Boolean frontchannelLogout;
protected String protocol; protected String protocol;
protected Map<String, String> attributes; protected Map<String, String> attributes;
protected Boolean fullScopeAllowed; protected Boolean fullScopeAllowed;
@ -179,4 +180,12 @@ public class ApplicationRepresentation {
public void setRegisteredNodes(Map<String, Integer> registeredNodes) { public void setRegisteredNodes(Map<String, Integer> registeredNodes) {
this.registeredNodes = registeredNodes; this.registeredNodes = registeredNodes;
} }
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
} }

View file

@ -21,6 +21,7 @@ public class OAuthClientRepresentation {
protected Map<String, String> attributes; protected Map<String, String> attributes;
protected Boolean directGrantsOnly; protected Boolean directGrantsOnly;
protected Boolean fullScopeAllowed; protected Boolean fullScopeAllowed;
protected Boolean frontchannelLogout;
public String getId() { public String getId() {
@ -126,4 +127,12 @@ public class OAuthClientRepresentation {
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes; this.attributes = attributes;
} }
public Boolean isFrontchannelLogout() {
return frontchannelLogout;
}
public void setFrontchannelLogout(Boolean frontchannelLogout) {
this.frontchannelLogout = frontchannelLogout;
}
} }

View file

@ -230,6 +230,7 @@ public class ModelToRepresentation {
rep.setEnabled(applicationModel.isEnabled()); rep.setEnabled(applicationModel.isEnabled());
rep.setAdminUrl(applicationModel.getManagementUrl()); rep.setAdminUrl(applicationModel.getManagementUrl());
rep.setPublicClient(applicationModel.isPublicClient()); rep.setPublicClient(applicationModel.isPublicClient());
rep.setFrontchannelLogout(applicationModel.isFrontchannelLogout());
rep.setProtocol(applicationModel.getProtocol()); rep.setProtocol(applicationModel.getProtocol());
rep.setAttributes(applicationModel.getAttributes()); rep.setAttributes(applicationModel.getAttributes());
rep.setFullScopeAllowed(applicationModel.isFullScopeAllowed()); rep.setFullScopeAllowed(applicationModel.isFullScopeAllowed());
@ -266,6 +267,7 @@ public class ModelToRepresentation {
rep.setName(model.getClientId()); rep.setName(model.getClientId());
rep.setEnabled(model.isEnabled()); rep.setEnabled(model.isEnabled());
rep.setPublicClient(model.isPublicClient()); rep.setPublicClient(model.isPublicClient());
rep.setFrontchannelLogout(model.isFrontchannelLogout());
rep.setProtocol(model.getProtocol()); rep.setProtocol(model.getProtocol());
rep.setAttributes(model.getAttributes()); rep.setAttributes(model.getAttributes());
rep.setFullScopeAllowed(model.isFullScopeAllowed()); rep.setFullScopeAllowed(model.isFullScopeAllowed());

View file

@ -372,6 +372,7 @@ public class RepresentationToModel {
applicationModel.setBaseUrl(resourceRep.getBaseUrl()); applicationModel.setBaseUrl(resourceRep.getBaseUrl());
if (resourceRep.isBearerOnly() != null) applicationModel.setBearerOnly(resourceRep.isBearerOnly()); if (resourceRep.isBearerOnly() != null) applicationModel.setBearerOnly(resourceRep.isBearerOnly());
if (resourceRep.isPublicClient() != null) applicationModel.setPublicClient(resourceRep.isPublicClient()); if (resourceRep.isPublicClient() != null) applicationModel.setPublicClient(resourceRep.isPublicClient());
if (resourceRep.isFrontchannelLogout() != null) applicationModel.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
if (resourceRep.getProtocol() != null) applicationModel.setProtocol(resourceRep.getProtocol()); if (resourceRep.getProtocol() != null) applicationModel.setProtocol(resourceRep.getProtocol());
if (resourceRep.isFullScopeAllowed() != null) { if (resourceRep.isFullScopeAllowed() != null) {
applicationModel.setFullScopeAllowed(resourceRep.isFullScopeAllowed()); applicationModel.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
@ -458,6 +459,7 @@ public class RepresentationToModel {
if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly()); if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly());
if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient()); if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed()); if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed());
if (rep.isFrontchannelLogout() != null) resource.setFrontchannelLogout(rep.isFrontchannelLogout());
if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl()); if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl());
if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl()); if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl());
if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired()); if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
@ -579,6 +581,7 @@ public class RepresentationToModel {
if (rep.getName() != null) model.setClientId(rep.getName()); if (rep.getName() != null) model.setClientId(rep.getName());
if (rep.isEnabled() != null) model.setEnabled(rep.isEnabled()); if (rep.isEnabled() != null) model.setEnabled(rep.isEnabled());
if (rep.isPublicClient() != null) model.setPublicClient(rep.isPublicClient()); if (rep.isPublicClient() != null) model.setPublicClient(rep.isPublicClient());
if (rep.isFrontchannelLogout() != null) model.setFrontchannelLogout(rep.isFrontchannelLogout());
if (rep.isFullScopeAllowed() != null) model.setFullScopeAllowed(rep.isFullScopeAllowed()); if (rep.isFullScopeAllowed() != null) model.setFullScopeAllowed(rep.isFullScopeAllowed());
if (rep.isDirectGrantsOnly() != null) model.setDirectGrantsOnly(rep.isDirectGrantsOnly()); if (rep.isDirectGrantsOnly() != null) model.setDirectGrantsOnly(rep.isDirectGrantsOnly());
if (rep.getClaims() != null) { if (rep.getClaims() != null) {

View file

@ -21,7 +21,7 @@
<resteasy.version.latest>3.0.9.Final</resteasy.version.latest> <resteasy.version.latest>3.0.9.Final</resteasy.version.latest>
<!-- <undertow.version>1.1.0.Final</undertow.version> --> <!-- <undertow.version>1.1.0.Final</undertow.version> -->
<undertow.version>1.1.1.Final</undertow.version> <undertow.version>1.1.1.Final</undertow.version>
<picketlink.version>2.7.0.CR2</picketlink.version> <picketlink.version>2.7.0.CR3</picketlink.version>
<picketbox.ldap.version>1.0.2.Final</picketbox.ldap.version> <picketbox.ldap.version>1.0.2.Final</picketbox.ldap.version>
<mongo.driver.version>2.11.3</mongo.driver.version> <mongo.driver.version>2.11.3</mongo.driver.version>
<jboss.logging.version>3.1.4.GA</jboss.logging.version> <jboss.logging.version>3.1.4.GA</jboss.logging.version>

View file

@ -1,5 +1,6 @@
package org.keycloak.protocol.saml; package org.keycloak.protocol.saml;
import org.jboss.logging.Logger;
import org.picketlink.common.constants.GeneralConstants; import org.picketlink.common.constants.GeneralConstants;
import org.picketlink.common.constants.JBossSAMLConstants; import org.picketlink.common.constants.JBossSAMLConstants;
import org.picketlink.common.constants.JBossSAMLURIConstants; import org.picketlink.common.constants.JBossSAMLURIConstants;
@ -38,6 +39,7 @@ import static org.picketlink.common.util.StringUtil.isNotNull;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class SAML2BindingBuilder<T extends SAML2BindingBuilder> { public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
protected static final Logger logger = Logger.getLogger(SAML2BindingBuilder.class);
protected KeyPair signingKeyPair; protected KeyPair signingKeyPair;
protected X509Certificate signingCertificate; protected X509Certificate signingCertificate;
@ -148,6 +150,9 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
public Response request() throws ConfigurationException, ProcessingException, IOException { public Response request() throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, destination, true); return buildResponse(document, destination, true);
} }
public Response request(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, actionUrl, true);
}
public Response response() throws ConfigurationException, ProcessingException, IOException { public Response response() throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, destination, false); return buildResponse(document, destination, false);
} }
@ -186,13 +191,16 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
return response(destination, false); return response(destination, false);
} }
public Response request(String redirect) throws ProcessingException, ConfigurationException, IOException {
return response(redirect, true);
}
public Response request() throws ProcessingException, ConfigurationException, IOException { public Response request() throws ProcessingException, ConfigurationException, IOException {
return response(destination, true); return response(destination, true);
} }
private Response response(String redirectUri, boolean asRequest) throws ProcessingException, ConfigurationException, IOException { private Response response(String redirectUri, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
URI uri = responseUri(redirectUri, asRequest); URI uri = responseUri(redirectUri, asRequest);
if (logger.isDebugEnabled()) logger.trace("redirect-binding uri: " + uri.toString());
CacheControl cacheControl = new CacheControl(); CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true); cacheControl.setNoCache(true);
return Response.status(302).location(uri) return Response.status(302).location(uri)
@ -339,7 +347,9 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
} }
protected String base64Encoded(Document document) throws ConfigurationException, ProcessingException, IOException { protected String base64Encoded(Document document) throws ConfigurationException, ProcessingException, IOException {
byte[] responseBytes = org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil.getDocumentAsString(document).getBytes("UTF-8"); String documentAsString = org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil.getDocumentAsString(document);
logger.debugv("saml docment: {0}", documentAsString);
byte[] responseBytes = documentAsString.getBytes("UTF-8");
return RedirectBindingUtil.deflateBase64URLEncode(responseBytes); return RedirectBindingUtil.deflateBase64URLEncode(responseBytes);
} }

View file

@ -53,6 +53,13 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
nameID.setFormat(URI.create(nameIDFormat)); nameID.setFormat(URI.create(nameIDFormat));
lort.setNameID(nameID); lort.setNameID(nameID);
if (issuer != null) {
NameIDType issuerID = new NameIDType();
issuerID.setValue(issuer);
lort.setIssuer(issuerID);
}
long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout(); long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout();
lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionValidity)); lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionValidity));

View file

@ -307,9 +307,10 @@ public class SamlProtocol implements LoginProtocol {
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(clientSession, client); SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(clientSession, client);
try { try {
if (isLogoutPostBindingForClient(app)) { if (isLogoutPostBindingForClient(app)) {
return logoutBuilder.postBinding().response(bindingUri); return logoutBuilder.postBinding().request(bindingUri);
} else { } else {
return logoutBuilder.redirectBinding().response(bindingUri); logger.debug("frontchannel redirect binding");
return logoutBuilder.redirectBinding().request(bindingUri);
} }
} catch (ConfigurationException e) { } catch (ConfigurationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -325,6 +326,7 @@ public class SamlProtocol implements LoginProtocol {
@Override @Override
public Response finishLogout(UserSessionModel userSession) { public Response finishLogout(UserSessionModel userSession) {
logger.debug("finishLogout");
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder(); SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID)); builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
builder.destination(userSession.getNote(SAML_LOGOUT_ISSUER)); builder.destination(userSession.getNote(SAML_LOGOUT_ISSUER));

View file

@ -136,6 +136,7 @@ public class SamlService {
event.error(Errors.INVALID_TOKEN); event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request"); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
} }
logger.debug("logout response");
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection); return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection);
} }
@ -183,12 +184,15 @@ public class SamlService {
event.error(Errors.INVALID_SIGNATURE); event.error(Errors.INVALID_SIGNATURE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid requester."); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid requester.");
} }
logger.debug("verified request");
if (samlObject instanceof AuthnRequestType) { if (samlObject instanceof AuthnRequestType) {
logger.debug("** login request");
event.event(EventType.LOGIN); event.event(EventType.LOGIN);
// Get the SAML Request Message // Get the SAML Request Message
AuthnRequestType authn = (AuthnRequestType) samlObject; AuthnRequestType authn = (AuthnRequestType) samlObject;
return loginRequest(relayState, authn, client); return loginRequest(relayState, authn, client);
} else if (samlObject instanceof LogoutRequestType) { } else if (samlObject instanceof LogoutRequestType) {
logger.debug("** logout request");
event.event(EventType.LOGOUT); event.event(EventType.LOGOUT);
LogoutRequestType logout = (LogoutRequestType) samlObject; LogoutRequestType logout = (LogoutRequestType) samlObject;
return logoutRequest(logout, client, relayState); return logoutRequest(logout, client, relayState);
@ -306,6 +310,13 @@ public class SamlService {
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding); userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
userSession.setNote(SamlProtocol.SAML_LOGOUT_ISSUER, logoutRequest.getIssuer().getValue()); userSession.setNote(SamlProtocol.SAML_LOGOUT_ISSUER, logoutRequest.getIssuer().getValue());
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL); userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
// remove client from logout requests
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
if (clientSession.getClient().getId().equals(client.getId())) {
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT);
}
}
logger.debug("browser Logout");
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection); return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection);
} }
@ -450,6 +461,7 @@ public class SamlService {
public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest, public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
@QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse, @QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
@QueryParam(GeneralConstants.RELAY_STATE) String relayState) { @QueryParam(GeneralConstants.RELAY_STATE) String relayState) {
logger.debug("SAML GET");
return new RedirectBindingProtocol().execute(samlRequest, samlResponse, relayState); return new RedirectBindingProtocol().execute(samlRequest, samlResponse, relayState);
} }
@ -461,6 +473,7 @@ public class SamlService {
public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest, public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
@FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse, @FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
@FormParam(GeneralConstants.RELAY_STATE) String relayState) { @FormParam(GeneralConstants.RELAY_STATE) String relayState) {
logger.debug("SAML POST");
return new PostBindingProtocol().execute(samlRequest, samlResponse, relayState); return new PostBindingProtocol().execute(samlRequest, samlResponse, relayState);
} }

View file

@ -115,19 +115,21 @@ public class AuthenticationManager {
List<ClientSessionModel> redirectClients = new LinkedList<ClientSessionModel>(); List<ClientSessionModel> redirectClients = new LinkedList<ClientSessionModel>();
for (ClientSessionModel clientSession : userSession.getClientSessions()) { for (ClientSessionModel clientSession : userSession.getClientSessions()) {
ClientModel client = clientSession.getClient(); ClientModel client = clientSession.getClient();
if (clientSession.getAction() == ClientSessionModel.Action.LOGGED_OUT) continue;
if (client.isFrontchannelLogout()) { if (client.isFrontchannelLogout()) {
String authMethod = clientSession.getAuthMethod(); String authMethod = clientSession.getAuthMethod();
if (authMethod == null) continue; // must be a keycloak service like account if (authMethod == null) continue; // must be a keycloak service like account
redirectClients.add(clientSession); redirectClients.add(clientSession);
continue; continue;
} }
if (client instanceof ApplicationModel && !client.isFrontchannelLogout() && clientSession.getAction() != ClientSessionModel.Action.LOGGED_OUT) { if (client instanceof ApplicationModel && !client.isFrontchannelLogout()) {
String authMethod = clientSession.getAuthMethod(); String authMethod = clientSession.getAuthMethod();
if (authMethod == null) continue; // must be a keycloak service like account if (authMethod == null) continue; // must be a keycloak service like account
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod); LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
protocol.setRealm(realm) protocol.setRealm(realm)
.setUriInfo(uriInfo); .setUriInfo(uriInfo);
try { try {
logger.debugv("backchannel logout to: {0}", client.getClientId());
protocol.backchannelLogout(userSession, clientSession); protocol.backchannelLogout(userSession, clientSession);
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT); clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT);
} catch (Exception e) { } catch (Exception e) {
@ -147,8 +149,12 @@ public class AuthenticationManager {
// setting this to logged out cuz I"m not sure protocols can always verify that the client was logged out or not // setting this to logged out cuz I"m not sure protocols can always verify that the client was logged out or not
nextRedirectClient.setAction(ClientSessionModel.Action.LOGGED_OUT); nextRedirectClient.setAction(ClientSessionModel.Action.LOGGED_OUT);
try { try {
logger.debugv("frontchannel logout to: {0}", nextRedirectClient.getClient().getClientId());
Response response = protocol.frontchannelLogout(userSession, nextRedirectClient); Response response = protocol.frontchannelLogout(userSession, nextRedirectClient);
if (response != null) return response; if (response != null) {
logger.debug("returning frontchannel logout request to client");
return response;
}
} catch (Exception e) { } catch (Exception e) {
logger.warn("Failed to logout client, continuing", e); logger.warn("Failed to logout client, continuing", e);
} }

View file

@ -328,7 +328,7 @@ public class ResourceAdminManager {
return new GlobalRequestResult(); return new GlobalRequestResult();
} }
if (logger.isDebugEnabled()) logger.info("Sending push revocation to URLS: " + mgmtUrls); if (logger.isDebugEnabled()) logger.debug("Sending push revocation to URLS: " + mgmtUrls);
// Propagate this to all hosts // Propagate this to all hosts
GlobalRequestResult result = new GlobalRequestResult(); GlobalRequestResult result = new GlobalRequestResult();

View file

@ -54,6 +54,7 @@ public class SamlBindingTest {
initializeSamlSecuredWar("/saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader); initializeSamlSecuredWar("/saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader);
initializeSamlSecuredWar("/saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader); initializeSamlSecuredWar("/saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
initializeSamlSecuredWar("/saml/signed-get", "/employee-sig", "employee-sig.war", classLoader); initializeSamlSecuredWar("/saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
initializeSamlSecuredWar("/saml/signed-front-get", "/employee-sig-front", "employee-sig-front.war", classLoader);
initializeSamlSecuredWar("/saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader); initializeSamlSecuredWar("/saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
initializeSamlSecuredWar("/saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader); initializeSamlSecuredWar("/saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
initializeSamlSecuredWar("/saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader); initializeSamlSecuredWar("/saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
@ -79,9 +80,14 @@ public class SamlBindingTest {
Thread.sleep(10000000); Thread.sleep(10000000);
} }
protected void checkLoggedOut() { protected void checkLoggedOut(String mainUrl) {
Assert.assertTrue(driver.getPageSource().contains("request-path: /logout.jsp")); String pageSource = driver.getPageSource();
Assert.assertTrue(driver.getPageSource().contains("principal=null")); System.out.println("*** logout pagesouce ***");
System.out.println(pageSource);
System.out.println("driver url: " + driver.getCurrentUrl());
Assert.assertTrue(pageSource.contains("request-path: /logout.jsp"));
driver.navigate().to(mainUrl);
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
} }
@ -94,7 +100,7 @@ public class SamlBindingTest {
System.out.println(driver.getPageSource()); System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getPageSource().contains("bburke")); Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-post?GLO=true"); driver.navigate().to("http://localhost:8081/sales-post?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/sales-post/");
} }
@Test @Test
public void testPostSignedLoginLogout() { public void testPostSignedLoginLogout() {
@ -104,7 +110,7 @@ public class SamlBindingTest {
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig/"); Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-sig/");
Assert.assertTrue(driver.getPageSource().contains("bburke")); Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-post-sig?GLO=true"); driver.navigate().to("http://localhost:8081/sales-post-sig?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/sales-post-sig/");
} }
@Test @Test
@ -117,7 +123,7 @@ public class SamlBindingTest {
Assert.assertFalse(driver.getPageSource().contains("bburke")); Assert.assertFalse(driver.getPageSource().contains("bburke"));
Assert.assertTrue(driver.getPageSource().contains("principal=G-")); Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
driver.navigate().to("http://localhost:8081/sales-post-sig-transient?GLO=true"); driver.navigate().to("http://localhost:8081/sales-post-sig-transient?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/sales-post-sig-transient/");
} }
@Test @Test
@ -130,7 +136,7 @@ public class SamlBindingTest {
Assert.assertFalse(driver.getPageSource().contains("bburke")); Assert.assertFalse(driver.getPageSource().contains("bburke"));
Assert.assertTrue(driver.getPageSource().contains("principal=G-")); Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
driver.navigate().to("http://localhost:8081/sales-post-sig-persistent?GLO=true"); driver.navigate().to("http://localhost:8081/sales-post-sig-persistent?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/sales-post-sig-persistent/");
} }
@Test @Test
@ -142,7 +148,7 @@ public class SamlBindingTest {
System.out.println(driver.getPageSource()); System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getPageSource().contains("principal=bburke@redhat.com")); Assert.assertTrue(driver.getPageSource().contains("principal=bburke@redhat.com"));
driver.navigate().to("http://localhost:8081/sales-post-sig-email?GLO=true"); driver.navigate().to("http://localhost:8081/sales-post-sig-email?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/sales-post-sig-email/");
} }
@Test @Test
@ -153,7 +159,45 @@ public class SamlBindingTest {
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig/"); Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig/");
Assert.assertTrue(driver.getPageSource().contains("bburke")); Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/employee-sig?GLO=true"); driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/employee-sig/");
}
@Test
public void testRedirectSignedLoginLogoutFrontNoSSO() {
driver.navigate().to("http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/employee-sig-front?GLO=true");
checkLoggedOut("http://localhost:8081/employee-sig-front/");
}
@Test
public void testRedirectSignedLoginLogoutFront() {
// visit 1st app an logg in
System.out.println("visit 1st app ");
driver.navigate().to("http://localhost:8081/employee-sig/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
System.out.println("login to form");
loginPage.login("bburke", "password");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
// visit 2nd app
System.out.println("visit 2nd app ");
driver.navigate().to("http://localhost:8081/employee-sig-front/");
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getPageSource().contains("bburke"));
// logout of first app
System.out.println("GLO");
driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
checkLoggedOut("http://localhost:8081/employee-sig/");
driver.navigate().to("http://localhost:8081/employee-sig-front/");
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
} }
@ -165,7 +209,7 @@ public class SamlBindingTest {
Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-enc/"); Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-post-enc/");
Assert.assertTrue(driver.getPageSource().contains("bburke")); Assert.assertTrue(driver.getPageSource().contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-post-enc?GLO=true"); driver.navigate().to("http://localhost:8081/sales-post-enc?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/sales-post-enc/");
} }
@Test @Test
@ -213,7 +257,7 @@ public class SamlBindingTest {
String pageSource = driver.getPageSource(); String pageSource = driver.getPageSource();
Assert.assertTrue(pageSource.contains("bburke")); Assert.assertTrue(pageSource.contains("bburke"));
driver.navigate().to("http://localhost:8081/sales-metadata?GLO=true"); driver.navigate().to("http://localhost:8081/sales-metadata?GLO=true");
checkLoggedOut(); checkLoggedOut("http://localhost:8081/sales-metadata/");
} }

View file

@ -0,0 +1,38 @@
<PicketLink xmlns="urn:picketlink:identity-federation:config:2.1">
<PicketLinkSP xmlns="urn:picketlink:identity-federation:config:2.1"
ServerEnvironment="tomcat" BindingType="REDIRECT" SupportsSignatures="true" LogOutResponseLocation="${idp-sig.url::http://localhost:8081/auth/realms/demo/protocol/saml}">
<IdentityURL>${idp-sig.url::http://localhost:8081/auth/realms/demo/protocol/saml}
</IdentityURL>
<ServiceURL>${employee-sig.url::http://localhost:8081/employee-sig-front/}
</ServiceURL>
<KeyProvider
ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">
<Auth Key="KeyStoreURL" Value="saml/signed-front-get/WEB-INF/keystore.jks" />
<Auth Key="KeyStorePass" Value="store123" />
<Auth Key="SigningKeyPass" Value="test123" />
<Auth Key="SigningKeyAlias" Value="http://localhost:8080/employee-sig/" />
<ValidatingAlias Key="localhost" Value="demo" />
<ValidatingAlias Key="127.0.0.1" Value="demo" />
</KeyProvider>
</PicketLinkSP>
<Handlers xmlns="urn:picketlink:identity-federation:handler:config:2.1">
<Handler
class="org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler" />
<Handler
class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler">
<Option Key="NAMEID_FORMAT" Value="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"/>
</Handler>
<Handler
class="org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler" />
<Handler
class="org.picketlink.identity.federation.web.handlers.saml2.SAML2SignatureGenerationHandler">
<!--
This is a optional configuration. By default, method http://www.w3.org/2000/09/xmldsig#rsa-sha1
and digest http://www.w3.org/2000/09/xmldsig#sha1 are used. -->
<Option Key="SIGN_METHOD" Value="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Option Key="SIGN_DIGEST" Value="http://www.w3.org/2001/04/xmlenc#sha256"/>
</Handler>
<Handler
class="org.picketlink.identity.federation.web.handlers.saml2.SAML2SignatureValidationHandler" />
</Handlers>
</PicketLink>

View file

@ -194,6 +194,26 @@
"saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5", "saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp" "saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
} }
},
{
"name": "http://localhost:8081/employee-sig-front/",
"enabled": true,
"protocol": "saml",
"fullScopeAllowed": true,
"frontchannelLogout": true,
"baseUrl": "http://localhost:8081/employee-sig-front",
"adminUrl": "http://localhost:8081/employee-sig-front",
"redirectUris": [
"http://localhost:8081/employee-sig-front/*"
],
"attributes": {
"saml.server.signature": "true",
"saml.client.signature": "true",
"saml.signature.algorithm": "RSA_SHA1",
"saml.authnstatement": "true",
"saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
}
} }
], ],
"roles" : { "roles" : {