Clone SAMLSubject when building Query
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / AttributeRequestor.java
1 /*
2  * AttributeRequestor.java
3  * 
4  * Generate a SAMLRequest to the AA for Attributes, then process the
5  * reply. In C++, this logic was InternalCCacheEntry::getNewResponse() 
6  * in shib-ccache.cpp.
7  * 
8  * The User Authentication was previously processed by ShibPOSTProfile
9  * and was stored by SessionManager in a Session object. Although the
10  * attributes might be fetched immediately, that is a strategy issue.
11  * In Java, we isolate the Attribute fetch in this separate module.
12  * 
13  * The Session block retains a copy of the original SAMLStatement from
14  * the POST, and it contains information about the remote Entity.
15  * However, the POST came from the HS (or IIDPProvider if you want to
16  * use SAML 2 terms), and this transaction has to go to the AA. So
17  * Metadata must be used to obtain the AttributeAuthorityRole and its
18  * associated Endpoint (URL).
19  * 
20  * The ApplicationInfo object for the configured Application presents
21  * a getAttributeDesignators method that can return a list of attributes
22  * to specify in the request. However, I can find no configuration element
23  * that corresponds to this, and no example logic. For now that method
24  * returns an empty collection and no particular attributes are requested.
25  * 
26  * The actual SSL session and exchange of data is performed by OpenSAML.
27  * Our interface to SAML is through the separate ShibBind module. The
28  * layers of processing and responsibilites need to be understood.
29  * 
30  * ShibBind uses the Metadata for the User's ID Providing Entity to
31  * locate the AttributeAuthorityRole and therefore the AA URL. This
32  * is passed to OpenSAML along with the request. Upon return, if any
33  * statements are signed it is the responsiblilty of ShibBind to call
34  * the Trust implementations to validate the signatures.
35  * 
36  * This module then checks, by calling the isSigned() property of
37  * SAMLObjects, to make sure that everything that is supposed to be
38  * signed actually was signed. ShibBind knows if a signature is valid,
39  * but this module knows if a signature was requred. This module also
40  * applies AAP to examine attributes and values and discard those that
41  * the policy doesn't accept.
42  * 
43  * Recovery Context: All exceptions handled and logged internally.
44  * --------------------
45  * Copyright 2002, 2004 
46  * University Corporation for Advanced Internet Development, Inc. 
47  * All rights reserved
48  * [Thats all we have to say to protect ourselves]
49  * Your permission to use this code is governed by "The Shibboleth License".
50  * A copy may be found at http://shibboleth.internet2.edu/license.html
51  * [Nothing in copyright law requires license text in every file.]
52  */
53 package edu.internet2.middleware.shibboleth.serviceprovider;
54
55 import java.util.Collection;
56 import java.util.Iterator;
57
58 import org.apache.log4j.Logger;
59 import org.opensaml.SAMLAssertion;
60 import org.opensaml.SAMLAttributeQuery;
61 import org.opensaml.SAMLAuthenticationStatement;
62 import org.opensaml.SAMLException;
63 import org.opensaml.SAMLRequest;
64 import org.opensaml.SAMLResponse;
65 import org.opensaml.SAMLSubject;
66 import org.opensaml.XML;
67
68 import x0.maceShibbolethTargetConfig1.ApplicationDocument.Application;
69
70 import edu.internet2.middleware.shibboleth.metadata.AttributeAuthorityDescriptor;
71 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
72 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
73 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
74
75 /**
76  * A static class with a static method. No objects are created
77  * 
78  * @author Howard Gilbert
79  */
80 public class AttributeRequestor {
81         
82         private static Logger log = Logger.getLogger(AttributeRequestor.class);
83         private static ServiceProviderContext context   = ServiceProviderContext.getInstance();
84         
85         private AttributeRequestor() {} // Prevent instantiation
86         
87         /**
88          * Request SAML Attribute response from AA
89          * 
90          * @param session Session object (from authentication POST)
91          * @return true if Attributes successfully stored in the Session
92          * @throws MetadataException If Origin has no configured AA
93          * @throws SAMLException If there is a problem with the reply
94          */
95         static 
96                         boolean    // return false if attributes are not fetched
97         fetchAttributes(
98                         Session session){
99                 
100             log.debug("Fetching attributes for session "+session.getKey()+
101                     " from "+session.getEntityId());
102             
103                 // Get local references to configuration objects
104                 ServiceProviderConfig config = context.getServiceProviderConfig();
105                 ApplicationInfo appinfo = config.getApplication(session.getApplicationId());
106                 
107                 // The Entity name was fed by by ShibPOSTProfile.accept(). Look it up now.
108                 EntityDescriptor entity = appinfo.lookup(session.getEntityId());
109                 if (entity==null) {
110                         log.error("Entity(Site) deleted from Metadata since authentication POST received: "+session.getEntityId());
111                         return false;
112                 }
113                 Application applicationConfig = appinfo.getApplicationConfig();
114                 
115                 SAMLRequest request = null;
116                 
117                 AttributeAuthorityDescriptor aa = 
118                     entity.getAttributeAuthorityDescriptor(XML.SAML11_PROTOCOL_ENUM); // throws MetadataException
119                 if (aa==null) {
120                     log.error("No Attribute Authority in Metadata for ID="+entity.getId());
121                     return false;
122                 }
123                 
124                 // Build the Attribute Query 
125                 SAMLAttributeQuery query = null;
126                 SAMLSubject subject;
127                 try {
128                         SAMLAuthenticationStatement authenticationStatement = session.getAuthenticationStatement();
129                         if (authenticationStatement==null) {
130                             log.error("Session contains no Authentication Statement." );
131                             return false;
132                         }
133                         SAMLSubject subject2 = authenticationStatement.getSubject();
134                         if (subject2==null) {
135                             log.error("Session Authentication Statement contains no Subject." );
136                             return false;
137                         }
138                         subject = (SAMLSubject) subject2.clone();
139                 } catch (Exception e) {
140                     log.error("Unable to generate the query SAMLSubject from the Authenticaiton." );
141                     return false;
142                 }
143                 log.debug("Subject (Handle) is "+subject.getName());
144                 Collection attributeDesignators = appinfo.getAttributeDesignators();
145                 try {
146             query = 
147                 new SAMLAttributeQuery(
148                         subject,                 // Subject (i.e. Handle) from authentication
149                         entity.getId(),      // ID of user's Entity (i.e. Origin Site)
150                         attributeDesignators // Attributes to request, null for everything
151                         );
152
153             // Wrap the Query in a request
154             request = new SAMLRequest(query);
155         } catch (SAMLException e) {
156             log.error("AttributeRequestor unable to build SAML Query for Session "+session.getKey());
157             return false;
158         }
159                 
160                 
161                 // ShibBinding will extract URLs from the Metadata and build
162                 // parameters so SAML can create the session. It also interfaces
163                 // to Trust to verify that any signed objects have trusted signatures.
164                 SAMLResponse response = null;
165                 try {
166             ShibBinding binding = new ShibBinding(session.getApplicationId());
167                         response = binding.send(request,aa,null,null);
168                 } catch (SAMLException e) {;} // response will be null
169                 if (response==null) {
170                         log.error("AttributeRequestor Query to remote AA returned no response from "+session.getEntityId());
171                         return false;
172                 }
173                 
174                 
175                 // Check each assertion in the response.
176         int acount = 0;
177                 Iterator assertions = response.getAssertions();
178                 while (assertions.hasNext()) {
179                         SAMLAssertion assertion = (SAMLAssertion) assertions.next();
180 //                      if (signedAssertions && !assertion.isSigned()) {
181 //                              log.warn("AttributeRequestor has removed unsigned assertion from response from "+session.getEntityId());
182 //                              response.removeAssertion(acount);
183 //                              continue;
184 //                      }
185                         
186             try {
187                 appinfo.applyAAP(assertion,aa); // apply each AAP to this assertion
188                 acount++;
189             }
190                         catch (SAMLException ex) {
191                 response.removeAssertion(acount); // AAP rejected all statements for this assertion
192             }
193                 }
194
195                 // A response may end up with no attributes, but that is not an error.
196                 // Maybe there is just nothing important to say about this user.
197                 
198                 session.setAttributeResponse(response); // Save response in Session object
199                 return true;
200         }
201
202 }