cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject svn commit: r1338769 - in /cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso: RequestAssertionConsumerService.java SAMLSSOResponseValidator.java
Date Tue, 15 May 2012 16:05:40 GMT
Author: coheigea
Date: Tue May 15 16:05:40 2012
New Revision: 1338769

URL: http://svn.apache.org/viewvc?rev=1338769&view=rev
Log:
Adding signature verification support to the RACS

Modified:
    cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java
    cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java

Modified: cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java?rev=1338769&r1=1338768&r2=1338769&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java
(original)
+++ cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/RequestAssertionConsumerService.java
Tue May 15 16:05:40 2012
@@ -19,11 +19,14 @@
 package org.apache.cxf.rs.security.saml.sso;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
+import java.net.URL;
 import java.net.URLDecoder;
+import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.UUID;
 import java.util.logging.Logger;
@@ -42,6 +45,7 @@ import javax.ws.rs.core.Response;
 
 import org.w3c.dom.Document;
 
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
 import org.apache.cxf.common.i18n.BundleUtils;
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.common.util.Base64Exception;
@@ -54,6 +58,8 @@ import org.apache.cxf.rs.security.saml.D
 import org.apache.cxf.rs.security.saml.sso.state.RequestState;
 import org.apache.cxf.rs.security.saml.sso.state.ResponseState;
 import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
 import org.apache.ws.security.saml.ext.OpenSAMLUtil;
 import org.opensaml.xml.XMLObject;
 
@@ -66,6 +72,8 @@ public class RequestAssertionConsumerSer
     
     private boolean supportDeflateEncoding = true;
     private boolean supportBase64Encoding = true;
+    private Crypto signatureCrypto;
+    private String signaturePropertiesFile;
 
     @Context 
     private MessageContext jaxrsContext;
@@ -84,34 +92,30 @@ public class RequestAssertionConsumerSer
         return supportBase64Encoding;
     }
     
+    public void setSignatureCrypto(Crypto crypto) {
+        signatureCrypto = crypto;
+    }
+    
+    /**
+     * Set the String corresponding to the signature Properties class
+     * @param signaturePropertiesFile the String corresponding to the signature properties
file
+     */
+    public void setSignaturePropertiesFile(String signaturePropertiesFile) {
+        this.signaturePropertiesFile = signaturePropertiesFile;
+        LOG.fine("Setting signature properties: " + signaturePropertiesFile);
+    }
+    
     @POST
     @Produces(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processSamlResponse(@FormParam(SSOConstants.SAML_RESPONSE) String encodedSamlResponse,
                                         @FormParam(SSOConstants.RELAY_STATE) String relayState)
{
-        if (relayState == null) {
-            reportError("MISSING_RELAY_STATE");
-            throw new WebApplicationException(400);
-        }
-        if (relayState.getBytes().length < 0 || relayState.getBytes().length > 80)
{
-            reportError("INVALID_RELAY_STATE");
-            throw new WebApplicationException(400);
-        }
-        RequestState requestState = getStateProvider().removeRequestState(relayState);
-        if (requestState == null) {
-            reportError("MISSING_REQUEST_STATE");
-            throw new WebApplicationException(400);
-        }
-        if (isStateExpired(requestState.getCreatedAt())) {
-            reportError("EXPIRED_REQUEST_STATE");
-            throw new WebApplicationException(400);
-        }
-        
+        RequestState requestState = processRelayState(relayState);
         URI targetURI = getTargetURI(requestState.getTargetAddress());
         
         org.opensaml.saml2.core.Response samlResponse = 
-            readSAMLResponse(encodedSamlResponse);
+            readSAMLResponse(true, encodedSamlResponse);
 
-        validateSamlResponse(samlResponse, requestState);
+        validateSamlResponse(true, samlResponse, requestState);
         
         // Set the security context
         String securityContextKey = UUID.randomUUID().toString();
@@ -131,22 +135,70 @@ public class RequestAssertionConsumerSer
     }
     
     @GET
-    public Response getSamlResponse(@QueryParam(SSOConstants.SAML_RESPONSE) String samlResponse,
+    public Response getSamlResponse(@QueryParam(SSOConstants.SAML_RESPONSE) String encodedSamlResponse,
                                     @QueryParam(SSOConstants.RELAY_STATE) String relayState)
{
-        return processSamlResponse(relayState, samlResponse);       
+        RequestState requestState = processRelayState(relayState);
+        URI targetURI = getTargetURI(requestState.getTargetAddress());
+        
+        org.opensaml.saml2.core.Response samlResponse = 
+            readSAMLResponse(false, encodedSamlResponse);
+
+        validateSamlResponse(false, samlResponse, requestState);
+        
+        // Set the security context
+        String securityContextKey = UUID.randomUUID().toString();
+        
+        long currentTime = System.currentTimeMillis();
+        ResponseState responseState = new ResponseState(relayState, currentTime);
+        getStateProvider().setResponseState(securityContextKey, responseState);
+        
+        String contextCookie = createCookie(SSOConstants.SECURITY_CONTEXT_TOKEN,
+                                            securityContextKey,
+                                            requestState.getWebAppContext(),
+                                            requestState.getWebAppDomain());
+        
+        // Finally, redirect to the service provider endpoint
+        return Response.seeOther(targetURI).header("Set-Cookie", contextCookie).build();
     }
     
-    private org.opensaml.saml2.core.Response readSAMLResponse(String samlResponse) {
+    private RequestState processRelayState(String relayState) {
+        if (relayState == null) {
+            reportError("MISSING_RELAY_STATE");
+            throw new WebApplicationException(400);
+        }
+        if (relayState.getBytes().length < 0 || relayState.getBytes().length > 80)
{
+            reportError("INVALID_RELAY_STATE");
+            throw new WebApplicationException(400);
+        }
+        RequestState requestState = getStateProvider().removeRequestState(relayState);
+        if (requestState == null) {
+            reportError("MISSING_REQUEST_STATE");
+            throw new WebApplicationException(400);
+        }
+        if (isStateExpired(requestState.getCreatedAt())) {
+            reportError("EXPIRED_REQUEST_STATE");
+            throw new WebApplicationException(400);
+        }
+        return requestState;
+    }
+    
+    private org.opensaml.saml2.core.Response readSAMLResponse(
+        boolean postBinding,
+        String samlResponse
+    ) {
         if (StringUtils.isEmpty(samlResponse)) {
             reportError("MISSING_SAML_RESPONSE");
             throw new WebApplicationException(400);
         }
         
-        String samlResponseDecoded = null;
-        try {
-            samlResponseDecoded = URLDecoder.decode(samlResponse, "UTF-8");
-        } catch (UnsupportedEncodingException e) {
-            throw new WebApplicationException(400);
+        String samlResponseDecoded = samlResponse;
+        // URL Decoding only applies for the re-direct binding
+        if (!postBinding) {
+            try {
+                samlResponseDecoded = URLDecoder.decode(samlResponse, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new WebApplicationException(400);
+            }
         }
         InputStream tokenStream = null;
         if (isSupportBase64Encoding()) {
@@ -174,6 +226,7 @@ public class RequestAssertionConsumerSer
         } catch (Exception ex) {
             throw new WebApplicationException(400);
         }
+        
         XMLObject responseObject = null;
         try {
             responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
@@ -186,12 +239,14 @@ public class RequestAssertionConsumerSer
         return (org.opensaml.saml2.core.Response)responseObject;
     }
     
-    protected void validateSamlResponse(org.opensaml.saml2.core.Response samlResponse,
-                                        RequestState requestState) {
+    protected void validateSamlResponse(
+        boolean postBinding,
+        org.opensaml.saml2.core.Response samlResponse,
+        RequestState requestState
+    ) {
         try {
             SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator();
-            // TODO Configure Crypto & CallbackHandler object here to validate signatures
-            protocolValidator.validateSamlResponse(samlResponse, null, null);
+            protocolValidator.validateSamlResponse(samlResponse, getSignatureCrypto(), null);
             
             SAMLSSOResponseValidator ssoResponseValidator = new SAMLSSOResponseValidator();
             ssoResponseValidator.setAssertionConsumerURL((String)jaxrsContext.get(Message.REQUEST_URL));
@@ -227,4 +282,56 @@ public class RequestAssertionConsumerSer
         LOG.warning(errorMsg.toString());
     }
     
+    private Crypto getSignatureCrypto() {
+        if (signatureCrypto == null && signaturePropertiesFile != null) {
+            Properties sigProperties = getProps(signaturePropertiesFile);
+            if (sigProperties == null) {
+                LOG.fine("Cannot load signature properties using: " + signaturePropertiesFile);
+                return null;
+            }
+            try {
+                signatureCrypto = CryptoFactory.getInstance(sigProperties);
+            } catch (WSSecurityException ex) {
+                LOG.fine("Error in loading the signature Crypto object: " + ex.getMessage());
+                return null;
+            }
+        }
+        return signatureCrypto;
+    }
+    
+    private static Properties getProps(Object o) {
+        Properties properties = null;
+        if (o instanceof Properties) {
+            properties = (Properties)o;
+        } else if (o instanceof String) {
+            URL url = null;
+            try {
+                url = ClassLoaderUtils.getResource((String)o, RequestAssertionConsumerService.class);
+                if (url == null) {
+                    url = new URL((String)o);
+                }
+                if (url != null) {
+                    properties = new Properties();
+                    InputStream ins = url.openStream();
+                    properties.load(ins);
+                    ins.close();
+                }
+            } catch (IOException e) {
+                LOG.fine(e.getMessage());
+                properties = null;
+            }
+        } else if (o instanceof URL) {
+            properties = new Properties();
+            try {
+                InputStream ins = ((URL)o).openStream();
+                properties.load(ins);
+                ins.close();
+            } catch (IOException e) {
+                LOG.fine(e.getMessage());
+                properties = null;
+            }            
+        }
+        return properties;
+    }
+    
 }

Modified: cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java?rev=1338769&r1=1338768&r2=1338769&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
(original)
+++ cxf/trunk/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java
Tue May 15 16:05:40 2012
@@ -68,9 +68,9 @@ public class SAMLSSOResponseValidator {
         }
         
         // The Response must contain a Destination that matches the assertionConsumerURL
if it is
-        // signed and received over the POST Binding.
+        // signed and received over the redirect Binding.
         String destination = samlResponse.getDestination();
-        if (postBinding && samlResponse.isSigned()
+        if (!postBinding && samlResponse.isSigned()
             && (destination == null || !destination.equals(assertionConsumerURL)))
{
             LOG.fine("The Response must contain a destination that matches the assertion
consumer URL");
             throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");



Mime
View raw message