You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2018/07/09 13:49:15 UTC
[cxf-fediz] branch master updated: FEDIZ-221 - Reworked SAML
Authentication parsing in the IdP to make it more generic.
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git
The following commit(s) were added to refs/heads/master by this push:
new ee93df4 FEDIZ-221 - Reworked SAML Authentication parsing in the IdP to make it more generic.
ee93df4 is described below
commit ee93df45e5c517adcb4c87dd65b658f84c2714e7
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Mon Jul 9 14:48:47 2018 +0100
FEDIZ-221 - Reworked SAML Authentication parsing in the IdP to make it more generic.
---
pom.xml | 2 +-
.../apache/cxf/fediz/service/idp/IdpConstants.java | 5 ++
.../idp/beans/samlsso/AuthnRequestParser.java | 58 +++++++++++++---------
...LAuthnRequest.java => SAMLAbstractRequest.java} | 33 +++---------
.../service/idp/samlsso/SAMLAuthnRequest.java | 19 +------
.../service/idp/samlsso/SAMLLogoutRequest.java | 47 ++++++++++++++++++
.../webapp/WEB-INF/flows/saml-validate-request.xml | 42 ++++++++++++++--
7 files changed, 135 insertions(+), 71 deletions(-)
diff --git a/pom.xml b/pom.xml
index 1838ca6..4ff27cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,7 +43,7 @@
<commons.logging.version>1.2</commons.logging.version>
<commons.io.version>2.5</commons.io.version>
<commons.validator.version>1.6</commons.validator.version>
- <cxf.version>3.2.5</cxf.version>
+ <cxf.version>3.2.6-SNAPSHOT</cxf.version>
<cxf.build-utils.version>3.4.0</cxf.build-utils.version>
<dbcp.version>2.1.1</dbcp.version>
<easymock.version>3.4</easymock.version>
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
index 1e2969b..691cc41 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/IdpConstants.java
@@ -42,6 +42,11 @@ public final class IdpConstants {
* The SAML Authn Request
*/
public static final String SAML_AUTHN_REQUEST = "saml_authn_request";
+
+ /**
+ * The SAML Logout Request
+ */
+ public static final String SAML_LOGOUT_REQUEST = "saml_logout_request";
/**
* A Context variable associated with the request (independent of protocol)
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
index 99959bb..74fac7f 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
@@ -39,6 +39,7 @@ import org.apache.cxf.fediz.service.idp.IdpConstants;
import org.apache.cxf.fediz.service.idp.domain.Application;
import org.apache.cxf.fediz.service.idp.domain.Idp;
import org.apache.cxf.fediz.service.idp.samlsso.SAMLAuthnRequest;
+import org.apache.cxf.fediz.service.idp.samlsso.SAMLLogoutRequest;
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;
@@ -59,6 +60,8 @@ import org.apache.wss4j.dom.validate.SignatureTrustValidator;
import org.apache.wss4j.dom.validate.Validator;
import org.apache.xml.security.algorithms.JCEMapper;
import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.LogoutRequest;
+import org.opensaml.saml.saml2.core.RequestAbstractType;
import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
import org.opensaml.security.credential.BasicCredential;
import org.opensaml.security.x509.BasicX509Credential;
@@ -72,7 +75,7 @@ import org.springframework.stereotype.Component;
import org.springframework.webflow.execution.RequestContext;
/**
- * Parse the received SAMLRequest into an OpenSAML AuthnRequest
+ * Parse the received SAMLRequest into an OpenSAML AuthnRequest or LogoutRequest
*/
@Component
public class AuthnRequestParser {
@@ -103,17 +106,24 @@ public class AuthnRequestParser {
WebUtils.removeAttribute(context, IdpConstants.SAML_AUTHN_REQUEST);
throw new ProcessingException(TYPE.BAD_REQUEST);
} else {
- AuthnRequest parsedRequest = null;
+ RequestAbstractType parsedRequest = null;
try {
parsedRequest = extractRequest(context, samlRequest);
} catch (Exception ex) {
LOG.warn("Error parsing request: {}", ex.getMessage());
throw new ProcessingException(TYPE.BAD_REQUEST);
}
-
- // Store various attributes from the AuthnRequest
- SAMLAuthnRequest authnRequest = new SAMLAuthnRequest(parsedRequest);
- WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST, authnRequest);
+
+ // Store various attributes from the AuthnRequest/LogoutRequest
+ if (parsedRequest instanceof AuthnRequest) {
+ SAMLAuthnRequest authnRequest = new SAMLAuthnRequest((AuthnRequest)parsedRequest);
+ WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST, authnRequest);
+ } else if (parsedRequest instanceof LogoutRequest) {
+ SAMLLogoutRequest logoutRequest = new SAMLLogoutRequest((LogoutRequest)parsedRequest);
+ WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_LOGOUT_REQUEST, logoutRequest);
+ }
+
+ validateRequest(parsedRequest);
// Check the signature
try {
@@ -122,7 +132,8 @@ public class AuthnRequestParser {
checkDestination(context, parsedRequest);
// Check signature
- X509Certificate validatingCert = getValidatingCertificate(idp, authnRequest.getIssuer());
+ X509Certificate validatingCert =
+ getValidatingCertificate(idp, parsedRequest.getIssuer().getValue());
Crypto issuerCrypto = new CertificateStore(new X509Certificate[] {validatingCert});
validateAuthnRequestSignature(parsedRequest.getSignature(), issuerCrypto);
} else if (signature != null) {
@@ -131,7 +142,7 @@ public class AuthnRequestParser {
// Check signature
validateSeparateSignature(idp, sigAlg, signature, relayState,
- samlRequest, authnRequest.getIssuer());
+ samlRequest, parsedRequest.getIssuer().getValue());
} else if (requireSignature) {
LOG.debug("No signature is present, therefore the request is rejected");
throw new ProcessingException(TYPE.BAD_REQUEST);
@@ -143,8 +154,6 @@ public class AuthnRequestParser {
throw new ProcessingException(TYPE.BAD_REQUEST);
}
- validateRequest(parsedRequest);
-
LOG.debug("SAML Request with id '{}' successfully parsed", parsedRequest.getID());
}
}
@@ -226,7 +235,7 @@ public class AuthnRequestParser {
return false;
}
- protected AuthnRequest extractRequest(RequestContext context, String samlRequest) throws Exception {
+ protected RequestAbstractType extractRequest(RequestContext context, String samlRequest) throws Exception {
byte[] deflatedToken = Base64Utility.decode(samlRequest);
String httpMethod = WebUtils.getHttpServletRequest(context).getMethod();
@@ -235,12 +244,10 @@ public class AuthnRequestParser {
: new ByteArrayInputStream(deflatedToken);
Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, StandardCharsets.UTF_8));
- AuthnRequest request =
- (AuthnRequest)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
if (LOG.isDebugEnabled()) {
LOG.debug(DOM2Writer.nodeToString(responseDoc));
}
- return request;
+ return (RequestAbstractType)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
}
public boolean isSupportDeflateEncoding() {
@@ -251,9 +258,9 @@ public class AuthnRequestParser {
this.supportDeflateEncoding = supportDeflateEncoding;
}
- private void validateRequest(AuthnRequest parsedRequest) throws ProcessingException {
+ private void validateRequest(RequestAbstractType parsedRequest) throws ProcessingException {
if (parsedRequest.getIssuer() == null) {
- LOG.debug("No Issuer is present in the AuthnRequest");
+ LOG.debug("No Issuer is present in the AuthnRequest/LogoutRequest");
throw new ProcessingException(TYPE.BAD_REQUEST);
}
@@ -264,12 +271,15 @@ public class AuthnRequestParser {
throw new ProcessingException(TYPE.BAD_REQUEST);
}
- // No SubjectConfirmation Elements are allowed
- if (parsedRequest.getSubject() != null
- && parsedRequest.getSubject().getSubjectConfirmations() != null
- && !parsedRequest.getSubject().getSubjectConfirmations().isEmpty()) {
- LOG.debug("An invalid SubjectConfirmation Element was received");
- throw new ProcessingException(TYPE.BAD_REQUEST);
+ if (parsedRequest instanceof AuthnRequest) {
+ // No SubjectConfirmation Elements are allowed
+ AuthnRequest authnRequest = (AuthnRequest)parsedRequest;
+ if (authnRequest.getSubject() != null
+ && authnRequest.getSubject().getSubjectConfirmations() != null
+ && !authnRequest.getSubject().getSubjectConfirmations().isEmpty()) {
+ LOG.debug("An invalid SubjectConfirmation Element was received");
+ throw new ProcessingException(TYPE.BAD_REQUEST);
+ }
}
}
@@ -316,9 +326,9 @@ public class AuthnRequestParser {
return CertsUtils.parseX509Certificate(serviceConfig.getValidatingCertificate());
}
- private void checkDestination(RequestContext context, AuthnRequest authnRequest) throws ProcessingException {
+ private void checkDestination(RequestContext context, RequestAbstractType request) throws ProcessingException {
// Check destination
- String destination = authnRequest.getDestination();
+ String destination = request.getDestination();
LOG.debug("Validating destination: {}", destination);
String localAddr = WebUtils.getHttpServletRequest(context).getRequestURL().toString();
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAbstractRequest.java
similarity index 52%
copy from services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
copy to services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAbstractRequest.java
index d1606ac..55ed6d3 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAbstractRequest.java
@@ -21,54 +21,35 @@ package org.apache.cxf.fediz.service.idp.samlsso;
import java.io.Serializable;
-import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.RequestAbstractType;
/**
- * This class encapsulates a (parsed) SAML AuthnRequest Object. The OpenSAML AuthnRequest Object is not
+ * This class encapsulates a (parsed) SAML RequestAbstractType Object. The OpenSAML RequestAbstractType Object is not
* serializable.
*/
-public class SAMLAuthnRequest implements Serializable {
+public abstract class SAMLAbstractRequest implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4353024755428346545L;
private String issuer;
- private String consumerServiceURL;
private String requestId;
- private boolean forceAuthn;
- private String subjectNameId;
- public SAMLAuthnRequest(AuthnRequest authnRequest) {
- if (authnRequest.getIssuer() != null) {
- issuer = authnRequest.getIssuer().getValue();
+ public SAMLAbstractRequest(RequestAbstractType request) {
+ if (request.getIssuer() != null) {
+ issuer = request.getIssuer().getValue();
}
- consumerServiceURL = authnRequest.getAssertionConsumerServiceURL();
- requestId = authnRequest.getID();
- forceAuthn = authnRequest.isForceAuthn().booleanValue();
- if (authnRequest.getSubject() != null && authnRequest.getSubject().getNameID() != null) {
- subjectNameId = authnRequest.getSubject().getNameID().getValue();
- }
+ requestId = request.getID();
}
public String getIssuer() {
return issuer;
}
- public String getConsumerServiceURL() {
- return consumerServiceURL;
- }
-
public String getRequestId() {
return requestId;
}
- public boolean isForceAuthn() {
- return forceAuthn;
- }
-
- public String getSubjectNameId() {
- return subjectNameId;
- }
}
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
index d1606ac..ba43db8 100644
--- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLAuthnRequest.java
@@ -19,51 +19,36 @@
package org.apache.cxf.fediz.service.idp.samlsso;
-import java.io.Serializable;
-
import org.opensaml.saml.saml2.core.AuthnRequest;
/**
* This class encapsulates a (parsed) SAML AuthnRequest Object. The OpenSAML AuthnRequest Object is not
* serializable.
*/
-public class SAMLAuthnRequest implements Serializable {
+public class SAMLAuthnRequest extends SAMLAbstractRequest {
/**
*
*/
private static final long serialVersionUID = 4353024755428346545L;
- private String issuer;
private String consumerServiceURL;
- private String requestId;
private boolean forceAuthn;
private String subjectNameId;
public SAMLAuthnRequest(AuthnRequest authnRequest) {
- if (authnRequest.getIssuer() != null) {
- issuer = authnRequest.getIssuer().getValue();
- }
+ super(authnRequest);
consumerServiceURL = authnRequest.getAssertionConsumerServiceURL();
- requestId = authnRequest.getID();
forceAuthn = authnRequest.isForceAuthn().booleanValue();
if (authnRequest.getSubject() != null && authnRequest.getSubject().getNameID() != null) {
subjectNameId = authnRequest.getSubject().getNameID().getValue();
}
}
- public String getIssuer() {
- return issuer;
- }
-
public String getConsumerServiceURL() {
return consumerServiceURL;
}
- public String getRequestId() {
- return requestId;
- }
-
public boolean isForceAuthn() {
return forceAuthn;
}
diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLLogoutRequest.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLLogoutRequest.java
new file mode 100644
index 0000000..9e47764
--- /dev/null
+++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAMLLogoutRequest.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.service.idp.samlsso;
+
+import org.opensaml.saml.saml2.core.LogoutRequest;
+
+/**
+ * This class encapsulates a (parsed) SAML LogoutRequest Object. The OpenSAML LogoutRequest Object is not
+ * serializable.
+ */
+public class SAMLLogoutRequest extends SAMLAbstractRequest {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4353024755428346545L;
+
+ private String subjectNameId;
+
+ public SAMLLogoutRequest(LogoutRequest logoutRequest) {
+ super(logoutRequest);
+
+ if (logoutRequest.getNameID() != null) {
+ subjectNameId = logoutRequest.getNameID().getValue();
+ }
+ }
+
+ public String getSubjectNameId() {
+ return subjectNameId;
+ }
+}
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 0f76a57..2319ee6 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
@@ -68,7 +68,7 @@
<if test="requestParameters.RelayState == null or requestParameters.RelayState.isEmpty()"
then="viewBadRequest" />
<if test="requestParameters.SAMLRequest != null and !requestParameters.SAMLRequest.isEmpty()"
- then="parseSAMLAuthnRequest" />
+ then="parseSAMLRequest" />
<if test="requestParameters.SAMLResponse == null or requestParameters.SAMLResponse.isEmpty()"
then="viewBadRequest" else="signinResponse" />
</decision-state>
@@ -85,14 +85,19 @@
then="viewBadRequest" else="signinResponse" />
</decision-state>
- <action-state id="parseSAMLAuthnRequest">
+ <action-state id="parseSAMLRequest">
<evaluate expression="authnRequestParser.parseSAMLRequest(flowRequestContext, flowScope.idpConfig,
flowScope.SAMLRequest, flowScope.SigAlg,
flowScope.Signature, flowScope.RelayState)" />
- <transition to="retrieveConsumerURL"/>
+ <transition to="determineRequestType"/>
<transition on-exception="org.apache.cxf.fediz.core.exception.ProcessingException" to="viewBadRequest" />
</action-state>
+ <decision-state id="determineRequestType">
+ <if test="flowScope.saml_authn_request == null"
+ then="selectSignOutProcess" else="retrieveConsumerURL" />
+ </decision-state>
+
<action-state id="retrieveConsumerURL">
<evaluate expression="authnRequestParser.retrieveConsumerURL(flowRequestContext)"
result="flowScope.consumerURL"/>
@@ -209,6 +214,37 @@
<transition to="redirectToTrustedIDP" />
<transition on-exception="java.lang.Throwable" to="scInternalServerError" />
</action-state>
+
+ <decision-state id="selectSignOutProcess">
+ <if test="flowScope.idpConfig.rpSingleSignOutConfirmation == true
+ or flowScope.idpConfig.rpSingleSignOutCleanupConfirmation == true"
+ then="viewSignoutConfirmation" else="invalidateSessionAction" />
+ </decision-state>
+
+ <!-- normal exit point for logout -->
+ <view-state id="viewSignoutConfirmation" view="signoutconfirmationresponse">
+ <transition on="submit" to="invalidateSessionAction"/>
+ <transition on="cancel" to="redirect" />
+ </view-state>
+
+ <!-- normal exit point for logout -->
+ <decision-state id="invalidateSessionAction">
+ <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"/>
+ <evaluate expression="homeRealmReminder.removeCookie(flowRequestContext)" />
+ <evaluate expression="logoutAction.submit(flowRequestContext)" />
+ </on-entry>
+ <if test="flowScope.idpConfig.isAutomaticRedirectToRpAfterLogout()"
+ then="redirectToRPLogoutPage" else="showLogoutResponsePage" />
+ </decision-state>
+
+ <end-state id="showLogoutResponsePage" view="signoutresponse" />
+
+ <end-state id="redirectToRPLogoutPage" view="externalRedirect:#{flowScope.wreply}" />
<!-- abnormal exit point -->
<decision-state id="viewBadRequest">