Support the IdP initiated SAML SSO flow

Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/cf461e4c
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/cf461e4c
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/cf461e4c

Branch: refs/heads/master-jaxrs-2.1
Commit: cf461e4c0805b187b43b5514fd08131fdd3fe601
Parents: 6583010
Author: Colm O hEigeartaigh <[email protected]>
Authored: Wed Aug 10 11:55:20 2016 +0100
Committer: Colm O hEigeartaigh <[email protected]>
Committed: Wed Aug 10 11:55:20 2016 +0100

----------------------------------------------------------------------
 ...AbstractRequestAssertionConsumerHandler.java | 54 ++++++++++++++++++++
 .../security/saml/sso/AbstractSSOSpHandler.java | 36 +++++++++++++
 .../saml/sso/AbstractServiceProviderFilter.java | 46 +++++------------
 .../saml/sso/SAMLSSOResponseValidator.java      |  3 ++
 4 files changed, 106 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/cf461e4c/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
----------------------------------------------------------------------
diff --git 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
index 3c110a1..0e801f3 100644
--- 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
+++ 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java
@@ -45,6 +45,7 @@ import org.apache.cxf.common.util.Base64Utility;
 import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.jaxrs.ext.MessageContext;
 import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
 import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
 import org.apache.cxf.rs.security.saml.sso.state.RequestState;
 import org.apache.cxf.rs.security.saml.sso.state.ResponseState;
@@ -69,6 +70,8 @@ public abstract class AbstractRequestAssertionConsumerHandler 
extends AbstractSS
     private TokenReplayCache<String> replayCache;
 
     private MessageContext messageContext;
+    private String applicationURL;
+    private boolean parseApplicationURLFromRelayState;
     
     @Context 
     public void setMessageContext(MessageContext mc) {
@@ -201,6 +204,31 @@ public abstract class 
AbstractRequestAssertionConsumerHandler extends AbstractSS
     }
     
     protected RequestState processRelayState(String relayState) {
+        if (isSupportUnsolicited()) {
+            String urlToForwardTo = applicationURL;
+            if (relayState != null && relayState.getBytes().length > 0 && 
relayState.getBytes().length < 80) {
+                // First see if we have a valid RequestState
+                RequestState requestState = 
getStateProvider().removeRequestState(relayState);
+                if (requestState != null && 
!isStateExpired(requestState.getCreatedAt(), 0)) {
+                    return requestState;
+                }
+                
+                // Otherwise get the application URL from the RelayState if 
supported
+                if (parseApplicationURLFromRelayState) {
+                    urlToForwardTo = relayState;
+                }
+            }
+            
+            // Otherwise create a new one for the IdP initiated case
+            return new RequestState(urlToForwardTo,
+                                    getIdpServiceAddress(),
+                                    null,
+                                    
getIssuerId(JAXRSUtils.getCurrentMessage()),
+                                    "/",
+                                    null,
+                                    new Date().getTime());
+        }
+        
         if (relayState == null) {
             reportError("MISSING_RELAY_STATE");
             throw ExceptionUtils.toBadRequestException(null, null);
@@ -218,6 +246,7 @@ public abstract class 
AbstractRequestAssertionConsumerHandler extends AbstractSS
             reportError("EXPIRED_REQUEST_STATE");
             throw ExceptionUtils.toBadRequestException(null, null);
         }
+
         return requestState;
     }
     
@@ -348,4 +377,29 @@ public abstract class 
AbstractRequestAssertionConsumerHandler extends AbstractSS
     public void setEnforceResponseSigned(boolean enforceResponseSigned) {
         this.enforceResponseSigned = enforceResponseSigned;
     }
+
+    public String getApplicationURL() {
+        return applicationURL;
+    }
+
+    /**
+     * Set the Application URL to forward to, for the unsolicited IdP case.
+     * @param applicationURL
+     */
+    public void setApplicationURL(String applicationURL) {
+        this.applicationURL = applicationURL;
+    }
+
+    public boolean isParseApplicationURLFromRelayState() {
+        return parseApplicationURLFromRelayState;
+    }
+
+    /**
+     * Whether to parse the application URL to forward to from the RelayState, 
for the unsolicted IdP case.
+     * @param parseApplicationURLFromRelayState
+     */
+    public void setParseApplicationURLFromRelayState(boolean 
parseApplicationURLFromRelayState) {
+        this.parseApplicationURLFromRelayState = 
parseApplicationURLFromRelayState;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/cf461e4c/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java
----------------------------------------------------------------------
diff --git 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java
 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java
index dd0a516..11a5b0c 100644
--- 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java
+++ 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractSSOSpHandler.java
@@ -28,7 +28,9 @@ import javax.annotation.PreDestroy;
 import javax.security.auth.callback.CallbackHandler;
 
 import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.jaxrs.impl.UriInfoImpl;
 import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.message.Message;
 import org.apache.cxf.rs.security.saml.sso.state.SPStateManager;
 import org.apache.cxf.rt.security.utils.SecurityUtils;
 import org.apache.wss4j.common.crypto.Crypto;
@@ -47,6 +49,9 @@ public class AbstractSSOSpHandler {
     private CallbackHandler callbackHandler;
     private String callbackHandlerClass;
     private String signatureUsername;
+    private String idpServiceAddress;
+    private String issuerId;
+    private boolean supportUnsolicited;
     
     static {
         OpenSAMLUtil.initSamlEngine();
@@ -203,4 +208,35 @@ public class AbstractSSOSpHandler {
         return signatureUsername;
     }
     
+    public void setIdpServiceAddress(String idpServiceAddress) {
+        this.idpServiceAddress = idpServiceAddress;
+    }
+
+    public String getIdpServiceAddress() {
+        return idpServiceAddress;
+    }
+    
+    public void setIssuerId(String issuerId) {
+        this.issuerId = issuerId;
+    }
+    
+    protected String getIssuerId(Message m) {
+        if (issuerId == null) {
+            return new UriInfoImpl(m).getBaseUri().toString();
+        } else {
+            return issuerId;
+        }
+    }
+    
+    public boolean isSupportUnsolicited() {
+        return supportUnsolicited;
+    }
+
+    /**
+     * Whether to support unsolicited IdP initiated login or not. The default
+     * is false.
+     */
+    public void setSupportUnsolicited(boolean supportUnsolicited) {
+        this.supportUnsolicited = supportUnsolicited;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/cf461e4c/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java
----------------------------------------------------------------------
diff --git 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java
 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java
index 3d88dc8..edb2a9a 100644
--- 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java
+++ 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java
@@ -75,8 +75,6 @@ public abstract class AbstractServiceProviderFilter extends 
AbstractSSOSpHandler
     protected static final ResourceBundle BUNDLE = 
         BundleUtils.getBundle(AbstractServiceProviderFilter.class);
     
-    private String idpServiceAddress;
-    private String issuerId;
     private String assertionConsumerServiceAddress;
     private AuthnRequestBuilder authnRequestBuilder = new 
DefaultAuthnRequestBuilder();
     private boolean signRequest;
@@ -106,31 +104,11 @@ public abstract class AbstractServiceProviderFilter 
extends AbstractSSOSpHandler
         this.assertionConsumerServiceAddress = assertionConsumerServiceAddress;
     }
 
-    public void setIssuerId(String issuerId) {
-        this.issuerId = issuerId;
-    }
-    
-    public void setIdpServiceAddress(String idpServiceAddress) {
-        this.idpServiceAddress = idpServiceAddress;
-    }
-
-    public String getIdpServiceAddress() {
-        return idpServiceAddress;
-    }
-    
     @PreDestroy
     public void close() {
         super.close();
     }
     
-    private String getIssuerId(Message m) {
-        if (issuerId == null) {
-            return new UriInfoImpl(m).getBaseUri().toString();
-        } else {
-            return issuerId;
-        }
-    }
-    
     protected boolean checkSecurityContext(Message m) {
         HttpHeaders headers = new HttpHeadersImpl(m);
         Map<String, Cookie> cookies = headers.getCookies();
@@ -142,16 +120,18 @@ public abstract class AbstractServiceProviderFilter 
extends AbstractSSOSpHandler
             return false;    
         }
         
-        Cookie relayStateCookie = cookies.get(SSOConstants.RELAY_STATE);
-        if (relayStateCookie == null) {
-            reportError("MISSING_RELAY_COOKIE");
-            return false;
-        }
-        String originalRelayState = responseState.getRelayState();
-        if (!originalRelayState.equals(relayStateCookie.getValue())) {
-            // perhaps the response state should also be removed
-            reportError("INVALID_RELAY_STATE");
-            return false;
+        if (!isSupportUnsolicited()) {
+            Cookie relayStateCookie = cookies.get(SSOConstants.RELAY_STATE);
+            if (relayStateCookie == null) {
+                reportError("MISSING_RELAY_COOKIE");
+                return false;
+            }
+            String originalRelayState = responseState.getRelayState();
+            if (!originalRelayState.equals(relayStateCookie.getValue())) {
+                // perhaps the response state should also be removed
+                reportError("INVALID_RELAY_STATE");
+                return false;
+            }
         }
         try {
             String assertion = responseState.getAssertion();
@@ -241,7 +221,7 @@ public abstract class AbstractServiceProviderFilter extends 
AbstractSSOSpHandler
                 m, getIssuerId(m), getAbsoluteAssertionServiceAddress(m)
             );
         if (isSignRequest()) {
-            authnRequest.setDestination(idpServiceAddress);
+            authnRequest.setDestination(getIdpServiceAddress());
             signAuthnRequest(authnRequest);
         }
         Element authnRequestElement = OpenSAMLUtil.toDom(authnRequest, doc);

http://git-wip-us.apache.org/repos/asf/cxf/blob/cf461e4c/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
----------------------------------------------------------------------
diff --git 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
index c95e773..25083c1 100644
--- 
a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
+++ 
b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
@@ -262,6 +262,9 @@ public class SAMLSSOResponseValidator {
         if (requestId != null && 
!requestId.equals(subjectConfData.getInResponseTo())) {
             LOG.fine("The InResponseTo String does match the original request 
id " + requestId);
             throw new 
WSSecurityException(WSSecurityException.ErrorCode.FAILURE, 
"invalidSAMLsecurity");
+        } else if (requestId == null && subjectConfData.getInResponseTo() != 
null) {
+            LOG.fine("No InResponseTo String is allowed for the unsolicted 
case");
+            throw new 
WSSecurityException(WSSecurityException.ErrorCode.FAILURE, 
"invalidSAMLsecurity");
         }
         
     }

Reply via email to