Refactor of SAMLP authentication creation to start supporting log request creation as well
Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/f3887c20 Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/f3887c20 Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/f3887c20 Branch: refs/heads/master Commit: f3887c20f664a1a7d5cbc5ab1da7c26b7ed8759d Parents: be392d3 Author: Colm O hEigeartaigh <[email protected]> Authored: Tue Sep 30 18:11:29 2014 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Tue Sep 30 18:11:29 2014 +0100 ---------------------------------------------------------------------- .../cxf/fediz/core/config/SAMLProtocol.java | 30 ++-- .../fediz/core/processor/SAMLProcessorImpl.java | 85 +++++++---- .../fediz/core/samlsso/AuthnRequestBuilder.java | 36 ----- .../samlsso/DefaultAuthnRequestBuilder.java | 105 ------------- .../samlsso/DefaultSAMLPRequestBuilder.java | 151 +++++++++++++++++++ .../fediz/core/samlsso/SAMLPRequestBuilder.java | 47 ++++++ .../samlsso/SamlpRequestComponentBuilder.java | 48 ++++++ .../cxf/fediz/core/samlsso/SAMLRequestTest.java | 16 +- 8 files changed, 331 insertions(+), 187 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/SAMLProtocol.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/SAMLProtocol.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/SAMLProtocol.java index ee59a70..d5a04c5 100644 --- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/SAMLProtocol.java +++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/config/SAMLProtocol.java @@ -22,8 +22,8 @@ package org.apache.cxf.fediz.core.config; import org.apache.cxf.fediz.core.config.jaxb.ProtocolType; import org.apache.cxf.fediz.core.config.jaxb.SamlProtocolType; import org.apache.cxf.fediz.core.saml.SAMLTokenValidator; -import org.apache.cxf.fediz.core.samlsso.AuthnRequestBuilder; -import org.apache.cxf.fediz.core.samlsso.DefaultAuthnRequestBuilder; +import org.apache.cxf.fediz.core.samlsso.DefaultSAMLPRequestBuilder; +import org.apache.cxf.fediz.core.samlsso.SAMLPRequestBuilder; import org.apache.wss4j.common.util.Loader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +32,7 @@ public class SAMLProtocol extends Protocol { private static final Logger LOG = LoggerFactory.getLogger(SAMLProtocol.class); - private AuthnRequestBuilder authnRequestBuilder; + private SAMLPRequestBuilder samlpRequestBuilder; public SAMLProtocol(ProtocolType protocolType) { super(protocolType); @@ -60,17 +60,17 @@ public class SAMLProtocol extends Protocol { getSAMLProtocol().setSignRequest(signRequest); } - public AuthnRequestBuilder getAuthnRequestBuilder() { - if (authnRequestBuilder != null) { - return authnRequestBuilder; + public SAMLPRequestBuilder getSAMLPRequestBuilder() { + if (samlpRequestBuilder != null) { + return samlpRequestBuilder; } - // See if we have a custom AuthnRequestBuilder - String authnRequestBuilderStr = getSAMLProtocol().getAuthnRequestBuilder(); - if (authnRequestBuilderStr != null && !"".equals(authnRequestBuilderStr)) { + // See if we have a custom SAMLPRequestBuilder + String samlpRequestBuilderStr = getSAMLProtocol().getAuthnRequestBuilder(); + if (samlpRequestBuilderStr != null && !"".equals(samlpRequestBuilderStr)) { try { - Class<?> authnRequestBuilderClass = Loader.loadClass(authnRequestBuilderStr); - authnRequestBuilder = (AuthnRequestBuilder) authnRequestBuilderClass.newInstance(); + Class<?> samlpRequestBuilderClass = Loader.loadClass(samlpRequestBuilderStr); + samlpRequestBuilder = (SAMLPRequestBuilder) samlpRequestBuilderClass.newInstance(); } catch (ClassNotFoundException ex) { LOG.debug(ex.getMessage(), ex); } catch (InstantiationException ex) { @@ -81,13 +81,13 @@ public class SAMLProtocol extends Protocol { } // Default implementation - authnRequestBuilder = new DefaultAuthnRequestBuilder(); + samlpRequestBuilder = new DefaultSAMLPRequestBuilder(); - return authnRequestBuilder; + return samlpRequestBuilder; } - public void setAuthnRequestBuilder(AuthnRequestBuilder authnRequestBuilder) { - this.authnRequestBuilder = authnRequestBuilder; + public void setSAMLPRequestBuilder(SAMLPRequestBuilder requestBuilder) { + this.samlpRequestBuilder = requestBuilder; } public boolean isDisableDeflateEncoding() { http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java index 99703af..b3766e8 100644 --- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java +++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/processor/SAMLProcessorImpl.java @@ -34,7 +34,6 @@ import javax.servlet.http.HttpServletRequest; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.apache.cxf.fediz.core.FederationConstants; import org.apache.cxf.fediz.core.RequestState; import org.apache.cxf.fediz.core.SAMLSSOConstants; import org.apache.cxf.fediz.core.TokenValidator; @@ -45,8 +44,8 @@ import org.apache.cxf.fediz.core.config.SAMLProtocol; import org.apache.cxf.fediz.core.exception.ProcessingException; import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE; import org.apache.cxf.fediz.core.metadata.MetadataWriter; -import org.apache.cxf.fediz.core.samlsso.AuthnRequestBuilder; import org.apache.cxf.fediz.core.samlsso.CompressionUtils; +import org.apache.cxf.fediz.core.samlsso.SAMLPRequestBuilder; import org.apache.cxf.fediz.core.samlsso.SAMLProtocolResponseValidator; import org.apache.cxf.fediz.core.samlsso.SAMLSSOResponseValidator; import org.apache.cxf.fediz.core.samlsso.SSOValidatorResponse; @@ -60,6 +59,7 @@ import org.apache.xml.security.exceptions.Base64DecodingException; import org.apache.xml.security.utils.Base64; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.xml.XMLObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -286,8 +286,8 @@ public class SAMLProcessorImpl extends AbstractFedizProcessor { redirectURL = issuerURL; } - AuthnRequestBuilder authnRequestBuilder = - ((SAMLProtocol)config.getProtocol()).getAuthnRequestBuilder(); + SAMLPRequestBuilder samlpRequestBuilder = + ((SAMLProtocol)config.getProtocol()).getSAMLPRequestBuilder(); Document doc = DOMUtils.createDocument(); doc.appendChild(doc.createElement("root")); @@ -296,7 +296,7 @@ public class SAMLProcessorImpl extends AbstractFedizProcessor { String requestURL = request.getRequestURL().toString(); String realm = resolveWTRealm(request, config); AuthnRequest authnRequest = - authnRequestBuilder.createAuthnRequest(realm, requestURL); + samlpRequestBuilder.createAuthnRequest(realm, requestURL); if (((SAMLProtocol)config.getProtocol()).isSignRequest()) { authnRequest.setDestination(redirectURL); @@ -407,7 +407,7 @@ public class SAMLProcessorImpl extends AbstractFedizProcessor { @Override public RedirectionResponse createSignOutRequest(HttpServletRequest request, FedizContext config) throws ProcessingException { - + String redirectURL = null; try { if (!(config.getProtocol() instanceof SAMLProtocol)) { @@ -420,34 +420,63 @@ public class SAMLProcessorImpl extends AbstractFedizProcessor { if (issuerURL != null && issuerURL.length() > 0) { redirectURL = issuerURL; } + redirectURL = "http://localhost:8081/IDBUS/CXF/CXFIDP/SAML2/SLO/REDIR"; + + SAMLPRequestBuilder samlpRequestBuilder = + ((SAMLProtocol)config.getProtocol()).getSAMLPRequestBuilder(); + + Document doc = DOMUtils.createDocument(); + doc.appendChild(doc.createElement("root")); + + // Create the LogoutRequest + String requestURL = request.getRequestURL().toString(); + String realm = resolveWTRealm(request, config); + String reason = "urn:oasis:names:tc:SAML:2.0:logout:user"; + LogoutRequest logoutRequest = + samlpRequestBuilder.createLogoutRequest(realm, reason, null); // TODO + + if (((SAMLProtocol)config.getProtocol()).isSignRequest()) { + logoutRequest.setDestination(redirectURL); + } + + Element logoutRequestElement = OpenSAMLUtil.toDom(logoutRequest, doc); + String logoutRequestEncoded = encodeAuthnRequest(logoutRequestElement); + + String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8"); + RequestState requestState = new RequestState(); + requestState.setTargetAddress(requestURL); + requestState.setIdpServiceAddress(redirectURL); + requestState.setRequestId(logoutRequest.getID()); + requestState.setIssuerId(realm); + requestState.setWebAppContext(logoutRequest.getIssuer().getValue()); + requestState.setState(relayState); + requestState.setCreatedAt(System.currentTimeMillis()); + + String urlEncodedRequest = + URLEncoder.encode(logoutRequestEncoded, "UTF-8"); StringBuilder sb = new StringBuilder(); - sb.append(FederationConstants.PARAM_ACTION).append('=').append(FederationConstants.ACTION_SIGNOUT); - - String logoutRedirectTo = config.getLogoutRedirectTo(); - if (logoutRedirectTo != null && !logoutRedirectTo.isEmpty()) { - - if (logoutRedirectTo.startsWith("/")) { - logoutRedirectTo = extractFullContextPath(request).concat(logoutRedirectTo.substring(1)); - } else { - logoutRedirectTo = extractFullContextPath(request).concat(logoutRedirectTo); - } - - LOG.debug("wreply=" + logoutRedirectTo); - - sb.append('&').append(FederationConstants.PARAM_REPLY).append('='); - sb.append(URLEncoder.encode(logoutRedirectTo, "UTF-8")); + sb.append(SAMLSSOConstants.SAML_REQUEST).append('=').append(urlEncodedRequest); + sb.append("&" + SAMLSSOConstants.RELAY_STATE).append('=').append(relayState); + + if (((SAMLProtocol)config.getProtocol()).isSignRequest()) { + String signature = signRequest(config, sb); + sb.append("&" + SAMLSSOConstants.SIGNATURE).append('=').append(signature); } - + + RedirectionResponse response = new RedirectionResponse(); + response.addHeader("Cache-Control", "no-cache, no-store"); + response.addHeader("Pragma", "no-cache"); + response.setRequestState(requestState); + redirectURL = redirectURL + "?" + sb.toString(); + response.setRedirectionURL(redirectURL); + + return response; } catch (Exception ex) { - LOG.error("Failed to create SignInRequest", ex); - throw new ProcessingException("Failed to create SignInRequest"); + LOG.error("Failed to create SignOutRequest", ex); + throw new ProcessingException("Failed to create SignOutRequest"); } - - RedirectionResponse response = new RedirectionResponse(); - response.setRedirectionURL(redirectURL); - return response; } } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/AuthnRequestBuilder.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/AuthnRequestBuilder.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/AuthnRequestBuilder.java deleted file mode 100644 index bae10dc..0000000 --- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/AuthnRequestBuilder.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.cxf.fediz.core.samlsso; - -import org.opensaml.saml2.core.AuthnRequest; - -/** - * This interface defines a method to create a SAML 2.0 Protocol AuthnRequest. - */ -public interface AuthnRequestBuilder { - - /** - * Create a SAML 2.0 Protocol AuthnRequest - */ - AuthnRequest createAuthnRequest( - String issuerId, - String assertionConsumerServiceAddress - ) throws Exception; -} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultAuthnRequestBuilder.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultAuthnRequestBuilder.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultAuthnRequestBuilder.java deleted file mode 100644 index f7383b5..0000000 --- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultAuthnRequestBuilder.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.cxf.fediz.core.samlsso; - -import java.util.Collections; - -import org.opensaml.common.SAMLVersion; -import org.opensaml.saml2.core.AuthnContextClassRef; -import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration; -import org.opensaml.saml2.core.AuthnRequest; -import org.opensaml.saml2.core.Issuer; -import org.opensaml.saml2.core.NameIDPolicy; -import org.opensaml.saml2.core.RequestedAuthnContext; - -/** - * A default implementation of the AuthnRequestBuilder interface to create a SAML 2.0 - * Protocol AuthnRequest. - */ -public class DefaultAuthnRequestBuilder implements AuthnRequestBuilder { - - private boolean forceAuthn; - private boolean isPassive; - private String protocolBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"; - - /** - * Create a SAML 2.0 Protocol AuthnRequest - */ - public AuthnRequest createAuthnRequest( - String issuerId, - String assertionConsumerServiceAddress - ) throws Exception { - Issuer issuer = - SamlpRequestComponentBuilder.createIssuer(issuerId); - - NameIDPolicy nameIDPolicy = - SamlpRequestComponentBuilder.createNameIDPolicy( - true, "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", issuerId - ); - - AuthnContextClassRef authnCtxClassRef = - SamlpRequestComponentBuilder.createAuthnCtxClassRef( - "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" - ); - RequestedAuthnContext authnCtx = - SamlpRequestComponentBuilder.createRequestedAuthnCtxPolicy( - AuthnContextComparisonTypeEnumeration.EXACT, - Collections.singletonList(authnCtxClassRef), null - ); - - //CHECKSTYLE:OFF - return SamlpRequestComponentBuilder.createAuthnRequest( - assertionConsumerServiceAddress, - forceAuthn, - isPassive, - protocolBinding, - SAMLVersion.VERSION_20, - issuer, - nameIDPolicy, - authnCtx - ); - - } - - public boolean isForceAuthn() { - return forceAuthn; - } - - public void setForceAuthn(boolean forceAuthn) { - this.forceAuthn = forceAuthn; - } - - public boolean isPassive() { - return isPassive; - } - - public void setPassive(boolean isPassive) { - this.isPassive = isPassive; - } - - public String getProtocolBinding() { - return protocolBinding; - } - - public void setProtocolBinding(String protocolBinding) { - this.protocolBinding = protocolBinding; - } - -} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultSAMLPRequestBuilder.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultSAMLPRequestBuilder.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultSAMLPRequestBuilder.java new file mode 100644 index 0000000..3c80e70 --- /dev/null +++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/DefaultSAMLPRequestBuilder.java @@ -0,0 +1,151 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.fediz.core.samlsso; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.opensaml.common.SAMLVersion; +import org.opensaml.saml2.core.AuthnContextClassRef; +import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.AuthnStatement; +import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.LogoutRequest; +import org.opensaml.saml2.core.NameID; +import org.opensaml.saml2.core.NameIDPolicy; +import org.opensaml.saml2.core.RequestedAuthnContext; + +/** + * A default implementation of the SAMLPRequestBuilder interface to create a SAML 2.0 + * Protocol AuthnRequest and LogoutRequest + */ +public class DefaultSAMLPRequestBuilder implements SAMLPRequestBuilder { + + private boolean forceAuthn; + private boolean isPassive; + private String protocolBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"; + + /** + * Create a SAML 2.0 Protocol AuthnRequest + */ + public AuthnRequest createAuthnRequest( + String issuerId, + String assertionConsumerServiceAddress + ) throws Exception { + Issuer issuer = + SamlpRequestComponentBuilder.createIssuer(issuerId); + + NameIDPolicy nameIDPolicy = + SamlpRequestComponentBuilder.createNameIDPolicy( + true, "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", issuerId + ); + + AuthnContextClassRef authnCtxClassRef = + SamlpRequestComponentBuilder.createAuthnCtxClassRef( + "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" + ); + RequestedAuthnContext authnCtx = + SamlpRequestComponentBuilder.createRequestedAuthnCtxPolicy( + AuthnContextComparisonTypeEnumeration.EXACT, + Collections.singletonList(authnCtxClassRef), null + ); + + //CHECKSTYLE:OFF + return SamlpRequestComponentBuilder.createAuthnRequest( + assertionConsumerServiceAddress, + forceAuthn, + isPassive, + protocolBinding, + SAMLVersion.VERSION_20, + issuer, + nameIDPolicy, + authnCtx + ); + + } + + public boolean isForceAuthn() { + return forceAuthn; + } + + public void setForceAuthn(boolean forceAuthn) { + this.forceAuthn = forceAuthn; + } + + public boolean isPassive() { + return isPassive; + } + + public void setPassive(boolean isPassive) { + this.isPassive = isPassive; + } + + public String getProtocolBinding() { + return protocolBinding; + } + + public void setProtocolBinding(String protocolBinding) { + this.protocolBinding = protocolBinding; + } + + @Override + public LogoutRequest createLogoutRequest( + String issuerId, + String reason, + SamlAssertionWrapper authenticatedAssertion + ) throws Exception { + Issuer issuer = + SamlpRequestComponentBuilder.createIssuer(issuerId); + + NameID nameID = null; + List<String> sessionIndices = new ArrayList<String>(); + + if (authenticatedAssertion != null) { + if (authenticatedAssertion.getSaml2() != null) { + org.opensaml.saml2.core.Subject subject = + authenticatedAssertion.getSaml2().getSubject(); + if (subject != null && subject.getNameID() != null) { + nameID = subject.getNameID(); + } + } + List<AuthnStatement> authnStatements = + authenticatedAssertion.getSaml2().getAuthnStatements(); + if (authnStatements != null && !authnStatements.isEmpty()) { + for (AuthnStatement authnStatement : authnStatements) { + if (authnStatement.getSessionIndex() != null) { + sessionIndices.add(authnStatement.getSessionIndex()); + } + } + } + } + + //CHECKSTYLE:OFF + return SamlpRequestComponentBuilder.createLogoutRequest( + issuer, + reason, + nameID, + sessionIndices + ); + } + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SAMLPRequestBuilder.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SAMLPRequestBuilder.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SAMLPRequestBuilder.java new file mode 100644 index 0000000..ba7efba --- /dev/null +++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SAMLPRequestBuilder.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.fediz.core.samlsso; + +import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.LogoutRequest; + +/** + * This interface defines a methods to create a SAML 2.0 Protocol AuthnRequest and LogoutRequest. + */ +public interface SAMLPRequestBuilder { + + /** + * Create a SAML 2.0 Protocol AuthnRequest + */ + AuthnRequest createAuthnRequest( + String issuerId, + String assertionConsumerServiceAddress + ) throws Exception; + + /** + * Create a SAML 2.0 Protocol LogoutRequest + */ + LogoutRequest createLogoutRequest( + String issuerId, + String reason, + SamlAssertionWrapper authenticatedAssertion + ) throws Exception; +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SamlpRequestComponentBuilder.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SamlpRequestComponentBuilder.java b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SamlpRequestComponentBuilder.java index 426dc33..12bec45 100644 --- a/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SamlpRequestComponentBuilder.java +++ b/plugins/core/src/main/java/org/apache/cxf/fediz/core/samlsso/SamlpRequestComponentBuilder.java @@ -32,8 +32,11 @@ import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration; import org.opensaml.saml2.core.AuthnContextDeclRef; import org.opensaml.saml2.core.AuthnRequest; import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.LogoutRequest; +import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.NameIDPolicy; import org.opensaml.saml2.core.RequestedAuthnContext; +import org.opensaml.saml2.core.SessionIndex; import org.opensaml.xml.XMLObjectBuilderFactory; /** @@ -43,6 +46,10 @@ public final class SamlpRequestComponentBuilder { private static volatile SAMLObjectBuilder<AuthnRequest> authnRequestBuilder; + private static volatile SAMLObjectBuilder<LogoutRequest> logoutRequestBuilder; + + private static volatile SAMLObjectBuilder<SessionIndex> sessionIndexBuilder; + private static volatile SAMLObjectBuilder<Issuer> issuerBuilder; private static volatile SAMLObjectBuilder<NameIDPolicy> nameIDBuilder; @@ -90,6 +97,47 @@ public final class SamlpRequestComponentBuilder { } @SuppressWarnings("unchecked") + public static LogoutRequest createLogoutRequest( + Issuer issuer, + String reason, + NameID nameId, + List<String> sessionIndices + ) { + if (logoutRequestBuilder == null) { + logoutRequestBuilder = (SAMLObjectBuilder<LogoutRequest>) + builderFactory.getBuilder(LogoutRequest.DEFAULT_ELEMENT_NAME); + } + if (sessionIndexBuilder == null) { + sessionIndexBuilder = (SAMLObjectBuilder<SessionIndex>) + builderFactory.getBuilder(SessionIndex.DEFAULT_ELEMENT_NAME); + } + + LogoutRequest logoutRequest = logoutRequestBuilder.buildObject(); + + logoutRequest.setID(UUID.randomUUID().toString()); + logoutRequest.setIssueInstant(new DateTime()); + + if (reason != null) { + logoutRequest.setReason(reason); + } + if (nameId != null) { + logoutRequest.setNameID(nameId); + } + + if (sessionIndices != null && !sessionIndices.isEmpty()) { + for (String sessionIndex : sessionIndices) { + SessionIndex sessionIndexObj = sessionIndexBuilder.buildObject(); + sessionIndexObj.setSessionIndex(sessionIndex); + logoutRequest.getSessionIndexes().add(sessionIndexObj); + } + } + + logoutRequest.setIssuer(issuer); + + return logoutRequest; + } + + @SuppressWarnings("unchecked") public static Issuer createIssuer( String issuerValue ) { http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/f3887c20/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java ---------------------------------------------------------------------- diff --git a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java index 4293565..f14d80e 100644 --- a/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java +++ b/plugins/core/src/test/java/org/apache/cxf/fediz/core/samlsso/SAMLRequestTest.java @@ -30,7 +30,6 @@ import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; - import org.apache.cxf.fediz.common.SecurityTestUtil; import org.apache.cxf.fediz.core.RequestState; import org.apache.cxf.fediz.core.config.FedizConfigurator; @@ -46,6 +45,7 @@ import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.LogoutRequest; /** * Some tests for creating SAMLRequests using the SAMLProcessorImpl @@ -214,8 +214,18 @@ public class SAMLRequestTest { RedirectionResponse response = wfProc.createSignOutRequest(req, config); String redirectionURL = response.getRedirectionURL(); - Assert.assertTrue(redirectionURL.startsWith(TEST_IDP_ISSUER)); - Assert.assertTrue(redirectionURL.endsWith("wa=wsignout1.0")); + String samlRequest = + redirectionURL.substring(redirectionURL.indexOf("SAMLRequest=") + "SAMLRequest=".length(), + redirectionURL.indexOf("RelayState=") - 1); + + byte[] deflatedToken = Base64.decode(URLDecoder.decode(samlRequest, "UTF-8")); + InputStream tokenStream = CompressionUtils.inflate(deflatedToken); + + Document requestDoc = DOMUtils.readXml(new InputStreamReader(tokenStream, "UTF-8")); + LogoutRequest request = + (LogoutRequest)OpenSAMLUtil.fromDom(requestDoc.getDocumentElement()); + + Assert.assertEquals(TEST_REQUEST_URL, request.getIssuer().getValue()); } } \ No newline at end of file
