Recognize Attribute Push
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / AssertionConsumerServlet.java
index 6fe474d..750de42 100644 (file)
@@ -49,6 +49,7 @@
 package edu.internet2.middleware.shibboleth.serviceprovider;
 
 import java.io.IOException;
+import java.util.Iterator;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
@@ -59,33 +60,35 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.log4j.Logger;
-import org.opensaml.SAMLBrowserProfile;
+import org.opensaml.SAMLAssertion;
+import org.opensaml.SAMLAttributeStatement;
+import org.opensaml.SAMLAudienceRestrictionCondition;
+import org.opensaml.SAMLCondition;
 import org.opensaml.SAMLException;
 import org.opensaml.SAMLResponse;
+import org.opensaml.SAMLStatement;
 import org.opensaml.SAMLBrowserProfile.BrowserProfileResponse;
-import org.w3c.dom.Element;
 
+import x0.maceShibbolethTargetConfig1.ApplicationDocument.Application;
 import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
-import edu.internet2.middleware.shibboleth.common.Credentials;
 import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
 import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
 
 /**
- * AuthenticatonAssertionConsumerServlet
+ * Process the Authentication Assertion POST data to create a Session.
  * 
  * @author Howard Gilbert
  */
 public class AssertionConsumerServlet extends HttpServlet {
 
+       private static final String SESSIONCOOKIE = "ShibbolethSPSession";
+
        private static Logger log = Logger.getLogger(AssertionConsumerServlet.class.getName());
        
        private static ServiceProviderContext context = ServiceProviderContext.getInstance();
        
-       private Element                 configuration = null;
-       private Credentials             credentials = null;
-       
        public static final String SESSIONPARM =
            "ShibbolethSessionId";
        
@@ -108,13 +111,10 @@ public class AssertionConsumerServlet extends HttpServlet {
         * 
         * @param request the request send by the client to the server
         * @param response the response send by the server to the client
-        * @throws ServletException if an error occurred
-        * @throws IOException if an error occurred
         */
        public void doPost(
                HttpServletRequest request,
                HttpServletResponse response)
-               // throws ServletException, IOException 
                {
            ServletContextInitializer.beginService(request,response);
                try {
@@ -138,9 +138,9 @@ public class AssertionConsumerServlet extends HttpServlet {
            
             if (appSessionValues.getShireSSL()&& // Requires SSL
                        !request.isSecure()) {       // isn't SSL
-               log.error("Authentication Assersion not posted over SSL.");
+               log.error("Authentication Assertion not posted over SSL.");
                try {
-                    response.sendRedirect("/shibboleth/shireError.html");
+                    response.sendRedirect("shireError.html");
                 } catch (IOException e1) {
                 }
                return;
@@ -151,9 +151,14 @@ public class AssertionConsumerServlet extends HttpServlet {
 
             String sessionId = createSessionFromPost(ipaddr, request, applicationId, shireURL, providerId, null);
             
-            Cookie cookie = new Cookie("ShibbolethSPSession",sessionId);
+            Cookie cookie = new Cookie(SESSIONCOOKIE,sessionId);
             response.addCookie(cookie);
             
+            /**
+             * This is included as a diagnostic, although it was suggested by a user
+             * as a future API for someone holding an Authentication and wishing to
+             * know what it means.
+             */
             try {
                                if (target.equals("SendAttributesBackToMe")) {
                                        ServletOutputStream outputStream = response.getOutputStream();
@@ -171,7 +176,7 @@ public class AssertionConsumerServlet extends HttpServlet {
         catch (MetadataException e) {
             log.error("Authentication Assertion source not found in Metadata.");
             try {
-                response.sendRedirect("/shibboleth/shireError.html");
+                response.sendRedirect("shireError.html");
             }
             catch (IOException e1) {
             }
@@ -179,7 +184,7 @@ public class AssertionConsumerServlet extends HttpServlet {
         catch (SAMLException e) {
             log.error("Authentication Assertion had invalid format.");
             try {
-                response.sendRedirect("/shibboleth/shireError.html");
+                response.sendRedirect("shireError.html");
             }
             catch (IOException e1) {
             }
@@ -192,6 +197,10 @@ public class AssertionConsumerServlet extends HttpServlet {
     /**
      * Create a Session object from SHIRE POST data
      * 
+     * <p>Used within this class to handle POSTs to the SP itself, but
+     * also by the FilterSupport logic when the POST is to the RM 
+     * context.</p>
+     * 
      * @param ipaddr IP Address of Browser
      * @param bin64Assertion Authentication assertion from POST
      * @param applicationId from RequestMap
@@ -213,21 +222,69 @@ public class AssertionConsumerServlet extends HttpServlet {
         String sessionid=null;
         StringBuffer pproviderId = // Get back IdP Entity name from SAML
             new StringBuffer();
-        String[] audiences = new String[1];
-        audiences[0]=providerId;
+        ServiceProviderConfig config = context.getServiceProviderConfig();
+        ApplicationInfo application = config.getApplication(applicationId);
+        Application applicationConfig = application.getApplicationConfig();
         
         ShibBrowserProfile profile = new ShibBrowserProfile(applicationId);
+        SPArtifactMapper mapper = new SPArtifactMapper(application,config);
         BrowserProfileResponse samldata = profile.receive(
                 pproviderId,
                 req,
                 shireURL,   // My URL (Why??) To prevent attackers from redirecting messages. 
-                SAMLBrowserProfile.PROFILE_POST,    // TODO: support both profiles 
                 context.getReplayCache(),
-                null,
+                mapper,
                 1
         );
         
-        // TODO: Audience/condition checking is now the profile caller's job.
+        String[] audienceArray = applicationConfig.getAudienceArray();
+        
+        
+        Iterator conditions = samldata.assertion.getConditions();
+        while (conditions.hasNext()) {
+            SAMLCondition cond =
+                (SAMLCondition)conditions.next();
+            
+            if (cond instanceof SAMLAudienceRestrictionCondition) {
+                SAMLAudienceRestrictionCondition audienceCondition =
+                    (SAMLAudienceRestrictionCondition) cond;
+                Iterator audiences = audienceCondition.getAudiences();
+                if (audiences==null)
+                    continue; // probably invalid
+                boolean matched = false;
+                StringBuffer audienceTests = new StringBuffer();
+                while (!matched && audiences.hasNext()) {
+                    String audienceString = (String) audiences.next();
+                    audienceTests.append(audienceString);
+                    audienceTests.append(' ');
+                    if (audienceString.equals(providerId)) {
+                        matched=true;
+                    }
+                    if (audienceArray!=null) {
+                        for (int i=0;i<audienceArray.length;i++) {
+                            if (audienceString.equals(audienceArray[i])) {
+                                matched=true;
+                                break;
+                            }
+                        }
+                    }
+                }
+                if (!matched) {
+                    log.error("Assertion restricted to "+audienceTests.toString());
+                    StringBuffer audienceBuffer = new StringBuffer("Did not match ");
+                    audienceBuffer.append(providerId);
+                    if (audienceArray!=null && audienceArray.length>0) {
+                        audienceBuffer.append(" or ");
+                        for (int i=0;i<audienceArray.length;i++) {
+                            audienceBuffer.append(audienceArray[i]);
+                            audienceBuffer.append(' ');
+                        }
+                    }
+                    log.error(audienceBuffer.toString());
+                    throw new SAMLException("Assertion failed audience restriction test.");
+                }
+            }
+        }
         
         // The Authentication Assertion gets placed in a newly created
         // Session object. Later, someone will get an Attribute Assertion
@@ -245,16 +302,50 @@ public class AssertionConsumerServlet extends HttpServlet {
         // Very agressive attribute fetch rule 
         // Get the Attributes immediately! [good for debugging]
         Session session = sessionManager.findSession(sessionid, applicationId);
+        
+        checkForAttributePush(samldata, session);
+        
         AttributeRequestor.fetchAttributes(session);
 
         return sessionid;
     }
 
 
+    /**
+     * Scan the POST data for Attribute Assertions. If any are found,
+     * then attributes have been pushed and we don't need to go to 
+     * the AA to get them. 
+     * @param samldata The BrowserProfileResponse containing the SAMLResponse
+     * @param session The Session just created
+     */
+    private static void checkForAttributePush(BrowserProfileResponse samldata, Session session) {
+        SAMLResponse samlresponse = samldata.response;
+        Iterator assertions = samlresponse.getAssertions();
+        while (assertions.hasNext()) {
+            SAMLAssertion assertion = (SAMLAssertion) assertions.next();
+            Iterator statements = assertion.getStatements();
+            while (statements.hasNext()) {
+                SAMLStatement statement = (SAMLStatement) statements.next();
+                if (statement instanceof SAMLAttributeStatement) {
+                    log.info("Found Attributes with Authenticaiton data (Attribute Push).");
+                    session.setAttributeResponse(samlresponse);
+                    // Note, the Attribute Statements have not been checked for 
+                    // AAP or Signatures. AttributeRequestor will bypass calling
+                    // the AA and will reprocess the POST Response for Attributes.
+                    return;
+                }
+            }
+        }
+    }
+
 
+    /**
+     * Artifact comes as a GET
+     */
     protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
        throws ServletException, IOException {
-       // Currently the Assertion Consumer does not receive a GET
+        log.debug("Received GET: "+ arg0.getQueryString());
+       doPost(arg0,arg1);
     }