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