cleanup, add checks to arguments and responses, add log statements
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / ShibBinding.java
index ef7abe5..f95ca89 100644 (file)
@@ -1,4 +1,20 @@
 /*
+ * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed 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.
+ */
+
+/*
  * ShibBinding.java
  * 
  * Corresponds to ShibBinding.cpp
  * that identify the target of the request from the Metadata, and
  * the caller passes an implementation of Trust so that signatures
  * can be validated.
- * 
- * --------------------
- * Copyright 2002, 2004 
- * University Corporation for Advanced Internet Development, Inc. 
- * All rights reserved
- * [Thats all we have to say to protect ourselves]
- * Your permission to use this code is governed by "The Shibboleth License".
- * A copy may be found at http://shibboleth.internet2.edu/license.html
- * [Nothing in copyright law requires license text in every file.]
  */
 package edu.internet2.middleware.shibboleth.serviceprovider;
 
 import java.util.Iterator;
 
 import org.apache.log4j.Logger;
-import org.opensaml.QName;
+import org.opensaml.NoSuchProviderException;
 import org.opensaml.SAMLAssertion;
 import org.opensaml.SAMLAuthorityBinding;
 import org.opensaml.SAMLBinding;
+import org.opensaml.SAMLBindingFactory;
+import org.opensaml.SAMLCondition;
 import org.opensaml.SAMLException;
 import org.opensaml.SAMLRequest;
 import org.opensaml.SAMLResponse;
-import org.opensaml.SAMLSOAPBinding;
+import org.opensaml.SAMLSOAPHTTPBinding;
 import org.opensaml.TrustException;
-import org.opensaml.XML;
-
-import edu.internet2.middleware.shibboleth.metadata.AttributeAuthorityRole;
+import edu.internet2.middleware.shibboleth.common.Trust;
+import edu.internet2.middleware.shibboleth.metadata.AttributeAuthorityDescriptor;
 import edu.internet2.middleware.shibboleth.metadata.Endpoint;
 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
 
@@ -65,7 +73,6 @@ public class ShibBinding {
        private static ServiceProviderContext context = ServiceProviderContext.getInstance();
        
        private String applicationId = null;
-       private SAMLBinding sbinding = new SAMLSOAPBinding();
        
        /**
         * While the C++ constructor takes iterators over the Trust and 
@@ -73,10 +80,11 @@ public class ShibBinding {
         * that contains them.
         * 
         * @param applicationId
+        * @throws NoSuchProviderException
         */
        public 
        ShibBinding(
-                       String applicationId) {
+                       String applicationId)  {
                this.applicationId=applicationId;
        }
 
@@ -99,9 +107,10 @@ public class ShibBinding {
                        SAMLResponse 
        send (
                        SAMLRequest req,
-                       AttributeAuthorityRole role,
+                       AttributeAuthorityDescriptor role,
                        String[] audiences,
-                       SAMLAuthorityBinding[] bindings) 
+                       SAMLAuthorityBinding[] bindings,
+            Trust trust) 
        throws SAMLException {
                
                // For the duration of the request, get local references to
@@ -109,74 +118,61 @@ public class ShibBinding {
                ServiceProviderConfig config = context.getServiceProviderConfig();
                ApplicationInfo appinfo = config.getApplication(applicationId);
                
+        SAMLBinding sbinding = null;
                SAMLResponse resp = null;
                String prevBinding = null;
        
                /*
-                * I seriously considered commenting this block out. It makes
-                * no particular sense for the caller to know about or provide
-                * SAMLAuthorityBinding objects. In any rational world, 
-                * a caller inside Shibboleth is going to represent the 
-                * AA from the Metadata. 
+                * Try any inline bindings provided by 1.0/1.1 IdPs. 
                 */
                if (bindings!=null) {
                        for (int ibinding=0;ibinding<bindings.length;ibinding++) {
                                try {
                                        SAMLAuthorityBinding binding = bindings[ibinding];
-                                       String bindingString = binding.getBinding();
-                                       if (!bindingString.equals(prevBinding)) {
-                                               prevBinding = bindingString;
-                                               resp=sbinding.send(binding,req);
-                                       }
+                                       if (!binding.getBinding().equals(prevBinding)) {
+                                               prevBinding = binding.getBinding();
+                        sbinding = SAMLBindingFactory.getInstance(binding.getBinding());
+                    }
+                                       resp=sbinding.send(binding.getLocation(),req);
                                        validateResponseSignatures(role, appinfo, resp);
                                        return resp;
-                               } catch (SAMLException e) {
-                                       continue;
-                               }
+                } catch (TrustException e) {
+                    log.error("Unable to validate signatures on attribute response: " + e);
+                    continue;
+                } catch (SAMLException e) {
+                    log.error("Unable to query attributes: " + e);
+                    continue;
+                }
                        }
                }
                
                /*
-                * In concept, a Role can have a collection of Endpoints.
-                * The theory is that SAML 2.0 Metadata might have different
-                * entries for different protocols (or different versions of
-                * the same protocol).
-                * The current Shibboleth configuration file doesn't allow this.
-                * Later on, when support for SAML 2.0 metadata is added, it is
-                * just as likely that the Endpoint array would be filtered by
-                * the configuration construction/parse process to leave only
-                * relevant entries.
-                * So for now, the C++ code to run the array and filter entries
-                * is replaced by logic that "knows" there is exactly one 
-                * Endpoint per Role (built into the XMLProviderRoleImpl).
+                * Try each metadata endpoint...
                 */
-               Endpoint[] ends = role.getAttributeServices();
-               Endpoint endpoint = ends[0];
-               
-               log.debug("AA is at "+endpoint.getLocation());
-               
-               /*
-                * The "address" of the request is a location URL embedded in
-                * a SAMLAuthorityBinding object. Send the request and get the
-                * response.
-                */
-               try {
-                       SAMLAuthorityBinding authbind = 
-                               new SAMLAuthorityBinding(
-                                               endpoint.getBinding(),
-                                               endpoint.getLocation(),
-                                               new QName(XML.SAMLP_NS,"AttributeQuery"));
-                       resp=sbinding.send(authbind,req);
-                       log.debug("AA returned Attribute Assertion");
-                       validateResponseSignatures(role, appinfo, resp);
-                       return resp;
-               } catch (TrustException e) {
-                       log.error("Unable to validate signatures on attribute request",e);
-                       throw e;
-               } catch (SAMLException e) {
-                       log.error("Unable to query attributes.",e);
-                       throw e;
-               }
+               Iterator ends = role.getAttributeServiceManager().getEndpoints();
+        while (ends.hasNext()) {
+            Endpoint endpoint = (Endpoint)ends.next();
+            try {
+                if (!endpoint.getBinding().equals(prevBinding)) {
+                    prevBinding = endpoint.getBinding();
+                    sbinding = SAMLBindingFactory.getInstance(endpoint.getBinding());
+                }
+                if (sbinding instanceof SAMLSOAPHTTPBinding) {
+                    SAMLSOAPHTTPBinding httpbind = (SAMLSOAPHTTPBinding)sbinding;
+                    httpbind.addHook(new ShibHttpHook(role,trust));
+                }
+                resp=sbinding.send(endpoint.getLocation(),req);
+                validateResponseSignatures(role, appinfo, resp);
+                return resp;
+            } catch (TrustException e) {
+                log.error("Unable to validate signatures on attribute response: " + e);
+                continue;
+            } catch (SAMLException e) {
+                log.error("Unable to query attributes: " + e);
+                continue;
+            }
+        }
+        return null;
        }
 
        /**
@@ -189,12 +185,12 @@ public class ShibBinding {
         */
        private void 
        validateResponseSignatures(
-                       AttributeAuthorityRole role, 
+                       AttributeAuthorityDescriptor role, 
                        ApplicationInfo appinfo, 
                        SAMLResponse resp) 
        throws TrustException {
                
-               if (resp.isSigned()&& !appinfo.validate(role,resp)) {
+               if (resp.isSigned()&& !appinfo.validate(resp,role)) {
                        throw new TrustException("Unable to validate signature of response");
                }
                
@@ -202,10 +198,14 @@ public class ShibBinding {
                while (assertions.hasNext()) {
                        SAMLAssertion assertion = (SAMLAssertion) assertions.next();
                        
-                       // TODO Dropped some logic validating conditions
+                       Iterator conditions = assertion.getConditions();
+                       while (conditions.hasNext()) {
+                               SAMLCondition condition = (SAMLCondition) conditions.next();
+                               // TODO C++ only seems to validate that the audience string is present
+                       }
                        
                        if (assertion.isSigned() && 
-                               !appinfo.validate(role,assertion)) {
+                               !appinfo.validate(assertion,role)) {
                                throw new TrustException("Unable to validate signature of assertion in response");
                        }
                }