Set the Form "action" from the AuthnRequest AssertionConsumerURL
Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/feae9f28 Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/feae9f28 Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/feae9f28 Branch: refs/heads/master Commit: feae9f28afb273b21dc5dcf8ce9f064137fea664 Parents: 6abaf27 Author: Colm O hEigeartaigh <[email protected]> Authored: Tue Mar 15 17:02:10 2016 +0000 Committer: Colm O hEigeartaigh <[email protected]> Committed: Tue Mar 15 17:02:10 2016 +0000 ---------------------------------------------------------------------- .../cxf/fediz/service/idp/IdpConstants.java | 5 + .../idp/beans/samlsso/AuthnRequestParser.java | 104 ++++++ .../beans/samlsso/AuthnRequestRealmParser.java | 81 ----- .../WEB-INF/flows/saml-validate-request.xml | 41 +-- systests/samlsso/out.txt | 325 ------------------- .../apache/cxf/fediz/systests/idp/IdpTest.java | 12 +- 6 files changed, 131 insertions(+), 437 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/feae9f28/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java index c754a38..2b007dd 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java @@ -28,6 +28,11 @@ public final class IdpConstants { */ public static final String TRUSTED_IDP_CONTEXT = "trusted_idp_context"; + /** + * A key used to store a parsed SAMLRequest as an OpenSAML AuthnRequest Object + */ + public static final String SAML_AUTHN_REQUEST = "saml_authn_request"; + private IdpConstants() { // complete } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/feae9f28/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java new file mode 100644 index 0000000..52bfecd --- /dev/null +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java @@ -0,0 +1,104 @@ +/** + * 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.service.idp.beans.samlsso; + +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.w3c.dom.Document; + +import org.apache.cxf.common.util.Base64Utility; +import org.apache.cxf.fediz.service.idp.IdpConstants; +import org.apache.cxf.fediz.service.idp.util.WebUtils; +import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; +import org.apache.cxf.rs.security.saml.sso.SSOConstants; +import org.apache.cxf.staxutils.StaxUtils; +import org.apache.wss4j.common.saml.OpenSAMLUtil; +import org.apache.wss4j.common.util.DOM2Writer; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.webflow.execution.RequestContext; + +/** + * Parse the received SAMLRequest into an OpenSAML AuthnRequest + */ +@Component +public class AuthnRequestParser { + + private static final Logger LOG = LoggerFactory.getLogger(AuthnRequestParser.class); + + public void parseSAMLRequest(RequestContext context) { + String samlRequest = context.getFlowScope().getString(SSOConstants.SAML_REQUEST); + LOG.debug("Received SAML Request: {}", samlRequest); + + if (samlRequest == null) { + WebUtils.removeAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST); + } else { + try { + AuthnRequest parsedRequest = extractRequest(samlRequest); + WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST, parsedRequest); + LOG.debug("SAML Request with id '{}' successfully parsed", parsedRequest.getID()); + } catch (Exception ex) { + LOG.warn("Error parsing request: {}", ex.getMessage()); + } + } + } + + public String retrieveRealm(RequestContext context) { + AuthnRequest authnRequest = + (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST); + if (authnRequest != null && authnRequest.getIssuer() != null) { + String issuer = authnRequest.getIssuer().getValue(); + LOG.debug("Parsed SAML AuthnRequest Issuer: {}", issuer); + return issuer; + } + + LOG.debug("No AuthnRequest available to be parsed"); + return null; + } + + public String retrieveConsumerURL(RequestContext context) { + AuthnRequest authnRequest = + (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST); + + if (authnRequest != null && authnRequest.getAssertionConsumerServiceURL() != null) { + String consumerURL = authnRequest.getAssertionConsumerServiceURL(); + LOG.debug("Parsed SAML AuthnRequest Consumer URL: {}", consumerURL); + return consumerURL; + } + + LOG.debug("No AuthnRequest available to be parsed"); + return null; + } + + private AuthnRequest extractRequest(String samlRequest) throws Exception { + byte[] deflatedToken = Base64Utility.decode(samlRequest); + InputStream tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); + + Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); + AuthnRequest request = + (AuthnRequest)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); + if (LOG.isDebugEnabled()) { + LOG.debug(DOM2Writer.nodeToString(responseDoc)); + } + return request; + } +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/feae9f28/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestRealmParser.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestRealmParser.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestRealmParser.java deleted file mode 100644 index 8319821..0000000 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestRealmParser.java +++ /dev/null @@ -1,81 +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.service.idp.beans.samlsso; - -import java.io.InputStream; -import java.io.InputStreamReader; - -import org.w3c.dom.Document; - -import org.apache.cxf.common.util.Base64Utility; -import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; -import org.apache.cxf.rs.security.saml.sso.SSOConstants; -import org.apache.cxf.staxutils.StaxUtils; -import org.apache.wss4j.common.saml.OpenSAMLUtil; -import org.apache.wss4j.common.util.DOM2Writer; -import org.opensaml.saml.saml2.core.AuthnRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.webflow.execution.RequestContext; - -/** - * Parse the received AuthnRequest and extract the home realm of the request from the Issuer - * value. - */ -@Component -public class AuthnRequestRealmParser { - - private static final Logger LOG = LoggerFactory.getLogger(AuthnRequestRealmParser.class); - - public String retrieveRealm(RequestContext context) { - String samlRequest = context.getFlowScope().getString(SSOConstants.SAML_REQUEST); - LOG.debug("Received SAML Request: {}", samlRequest); - - if (samlRequest != null) { - try { - AuthnRequest parsedRequest = extractRequest(samlRequest); - if (parsedRequest.getIssuer() != null) { - String issuer = parsedRequest.getIssuer().getValue(); - LOG.debug("Parsed SAML AuthnRequest Issuer: {}", issuer); - return issuer; - } - } catch (Exception ex) { - LOG.warn("Error parsing request: {}", ex.getMessage()); - return null; - } - } - - LOG.debug("No SamlRequest available to be parsed"); - return null; - } - - private AuthnRequest extractRequest(String samlRequest) throws Exception { - byte[] deflatedToken = Base64Utility.decode(samlRequest); - InputStream tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); - - Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); - AuthnRequest request = - (AuthnRequest)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); - if (LOG.isDebugEnabled()) { - LOG.debug(DOM2Writer.nodeToString(responseDoc)); - } - return request; - } -} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/feae9f28/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml ---------------------------------------------------------------------- diff --git a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml index f5734b6..55f97d6 100644 --- a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml +++ b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml @@ -32,8 +32,13 @@ <if test="requestParameters.RelayState == null or requestParameters.RelayState.length() == 0" then="viewBadRequest" /> <if test="requestParameters.SAMLRequest != null and !requestParameters.SAMLRequest.isEmpty()" - then="signinSAMLRequest" else="viewBadRequest" /> + then="parseSAMLRequest" else="viewBadRequest" /> </decision-state> + + <action-state id="parseSAMLRequest"> + <evaluate expression="authnRequestParser.parseSAMLRequest(flowRequestContext)" /> + <transition to="signinSAMLRequest"/> + </action-state> <subflow-state id="signinSAMLRequest" subflow="signinSAMLRequest"> <input name="idpConfig" value="flowScope.idpConfig" /> @@ -58,15 +63,15 @@ <!-- produce RP security token (as String type) --> <action-state id="requestRpToken"> <on-entry> - <evaluate expression="authnRequestRealmParser.retrieveRealm(flowRequestContext)" + <evaluate expression="authnRequestParser.retrieveRealm(flowRequestContext)" result="flowScope.realm"/> <evaluate expression="stsClientForRpAction.submit(flowScope.realm, flowRequestContext)" result="flowScope.rpToken"/> + <evaluate expression="authnRequestParser.retrieveConsumerURL(flowRequestContext)" + result="flowScope.consumerURL"/> </on-entry> <evaluate expression="signinParametersCacheAction.storeRPConfigInSession(flowRequestContext)"/> - <transition to="formResponseView" > - <set name="flowScope.signinResponseUrl" value="flowScope.wreply" /> - </transition> + <transition to="formResponseView" /> <transition on-exception="org.apache.cxf.fediz.core.exception.ProcessingException" to="viewBadRequest" /> <transition on-exception="java.lang.Throwable" to="scInternalServerError" /> </action-state> @@ -75,7 +80,7 @@ <!-- browser redirection (self-submitted form 'samlsigninresponseform.jsp') --> <end-state id="formResponseView" view="samlsigninresponseform"> <on-entry> - <evaluate expression="flowScope.signinResponseUrl" result="requestScope.samlAction" /> + <evaluate expression="flowScope.consumerURL" result="requestScope.samlAction" /> <evaluate expression="flowScope.RelayState" result="requestScope.relayState" /> <evaluate expression="flowScope.rpToken" result="requestScope.samlResponse" /> </on-entry> @@ -104,30 +109,6 @@ </on-entry> </end-state> - <!-- normal exit point for logout --> - <view-state id="viewSignoutConfirmation" view="signoutconfirmationresponse"> - <transition on="submit" to="invalidateSessionAction"/> - <transition on="cancel" to="redirect" /> - </view-state> - - <view-state id="redirect" view="externalRedirect:${flowScope.wreply}" /> - - <!-- normal exit point for logout --> - <end-state id="invalidateSessionAction" view="signoutresponse"> - <on-entry> - <!-- store the realmConfigMap in the request map before we invalidate the session below. - Its needed in the signoutresponse.jsp page --> - <set name="externalContext.requestMap.realmConfigMap" - value="externalContext.sessionMap.realmConfigMap"/> - <set name="externalContext.requestMap.wreply" value="flowScope.wreply"/> - <!-- there is no Saml token canceller in cxf STS... - <evaluate expression="stsClientForRpAction.cancelTokens(flowRequestContext)" /> - --> - <evaluate expression="homeRealmReminder.removeCookie(flowRequestContext)" /> - <evaluate expression="logoutAction.submit(flowRequestContext)" /> - </on-entry> - </end-state> - <end-state id="redirectToLocalIDP" view="externalRedirect:${flowScope.localIdpUrl}"> <on-entry> <set name="flowScope.localIdpUrl"
