If the responder cannot find an attribute class, it now responds with the attributes...
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / AAResponder.java
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution, if any, must include 
17  * the following acknowledgment: "This product includes software developed by 
18  * the University Corporation for Advanced Internet Development 
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
20  * may appear in the software itself, if and wherever such third-party 
21  * acknowledgments normally appear.
22  * 
23  * Neither the name of Shibboleth nor the names of its contributors, nor 
24  * Internet2, nor the University Corporation for Advanced Internet Development, 
25  * Inc., nor UCAID may be used to endorse or promote products derived from this 
26  * software without specific prior written permission. For written permission, 
27  * please contact shibboleth@shibboleth.org
28  * 
29  * Products derived from this software may not be called Shibboleth, Internet2, 
30  * UCAID, or the University Corporation for Advanced Internet Development, nor 
31  * may Shibboleth appear in their name, without prior written permission of the 
32  * University Corporation for Advanced Internet Development.
33  * 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 package edu.internet2.middleware.shibboleth.aa;
51
52 /**
53  *  Attribute Authority & Release Policy
54  *  Main logic that decides what to release 
55  *
56  * @author     Parviz Dousti (dousti@cmu.edu)
57  * @created    June, 2002
58  */
59
60
61 import java.io.*;
62 import java.util.*;
63 import java.lang.reflect.*;
64 import javax.servlet.*;
65 import javax.servlet.http.*;
66 import javax.naming.*;
67 import javax.naming.directory.*;
68 import edu.internet2.middleware.shibboleth.*;
69 import edu.internet2.middleware.shibboleth.hs.*;
70 import edu.internet2.middleware.eduPerson.*;
71 import org.w3c.dom.*;
72 import org.opensaml.*;
73 import org.apache.log4j.Logger;
74
75 public class AAResponder{
76
77     ArpRepository arpFactory;
78     Arp adminArp;
79     DirContext ctx;
80     String domain;
81     private static Logger log = Logger.getLogger(AAResponder.class.getName());    
82
83     public AAResponder(ArpRepository arpFactory, DirContext ctx, String domain)
84         throws AAException{
85
86         this.arpFactory = arpFactory;
87         try {
88                 adminArp = arpFactory.lookupArp("admin", true);
89         } catch (ArpRepositoryException e) {
90                 log.error("Error while searching Arp Repository (" + arpFactory + ") : " + e.getMessage());
91             throw new AAException("Unable to load admin ARP.");
92         }
93         if(adminArp ==  null) {
94                 log.error("Admin ARP not found in Arp Repository (" + arpFactory + ").");
95             throw new AAException("Unable to load admin ARP.");
96         }
97         this.ctx = ctx;
98         this.domain = domain;
99     }
100
101
102     public SAMLAttribute[] getReleaseAttributes(String userName, String searchFilter, String handle, String sharName, String url)
103         throws AAException{
104
105         DirContext userCtx = null;
106
107         try{
108             if(searchFilter == null)
109                 searchFilter = "";
110             int indx = searchFilter.indexOf("%s");
111             if(indx  <0){
112                 userCtx = (DirContext)ctx.lookup(searchFilter+userName);
113             }else{
114                 /* This is a search filter. Search after replacing %s with uid*/
115                 StringBuffer tmp = new StringBuffer(searchFilter);
116                 tmp.delete(indx, indx+2);
117                 tmp.insert(indx, userName);
118                 searchFilter = tmp.toString();
119                 NamingEnumeration en = ctx.search("", searchFilter, null);
120                 if(!en.hasMore())
121                     throw new AAException("No context found for "+userName+" as a result of searching "+searchFilter);
122                 userCtx = (DirContext)en.next();
123                 if(en.hasMore())
124                     throw new AAException("More than 1 context found for "+userName+" as a result of searching "+searchFilter);
125
126             }           
127         }catch(NamingException e){
128             throw new AAException("Cannot lookup context for "+userName+" :"+e);
129         }
130
131
132
133         Set s = getCombinedReleaseSet(adminArp, sharName, url, userName);
134         // go throu the set and find values for each attribute
135         try{
136             Vector sAttrs = new Vector();
137             Iterator it = s.iterator();
138             while(it.hasNext()){
139                 ArpAttribute aAttr = (ArpAttribute)it.next();
140                 Attribute dAttr = aAttr.getDirAttribute(userCtx, true);
141                 if(dAttr != null){
142                     SAMLAttribute sAttr = jndi2saml(dAttr);
143                     if (sAttr != null) {
144                         sAttrs.add(sAttr);
145                     }
146                 }
147             }
148             SAMLAttribute[] sa = new SAMLAttribute[sAttrs.size()];
149             return (SAMLAttribute[])sAttrs.toArray(sa);
150         }catch(NamingException e){
151             throw new AAException("Bad Contexted for getting Attribute Values: "+e);
152         }
153     }
154
155
156         private Set getCombinedReleaseSet(Arp admin, String sharName, String url, String userName)
157                 throws AAException {
158
159                 try {
160                         Set adminSet;
161                         Set userSet;
162                         Arp userArp;
163
164                         userArp = arpFactory.lookupArp(userName, false);
165
166                         if (userArp == null) {
167                                 // no user ARP just use the admin
168                                 // only go throu and drop the exclude ones
169                                 adminSet = getReleaseSet(adminArp, sharName, url, adminArp);
170                                 Iterator it = adminSet.iterator();
171                                 while (it.hasNext()) {
172                                         ArpAttribute attr = (ArpAttribute) it.next();
173                                         if (attr.mustExclude())
174                                                 adminSet.remove(attr);
175                                 }
176                                 return adminSet;
177                         }
178
179                         adminSet = getReleaseSet(adminArp, sharName, url, adminArp);
180                         userSet = getReleaseSet(userArp, sharName, url, adminArp);
181                         // combine the two
182                         Iterator it = adminSet.iterator();
183                         while (it.hasNext()) {
184                                 ArpAttribute aAttr = (ArpAttribute) it.next();
185                                 if (aAttr.mustExclude()) {
186                                         userSet.remove(aAttr); // ok if not there
187                                         adminSet.remove(aAttr);
188                                 }
189                                 if (userSet.contains(aAttr)) {
190                                         // in both. Combine filters
191                                         ArpFilter f = combineFilters(aAttr, getAttr(userSet, aAttr));
192                                         log.info(
193                                                 "Combining filters: "
194                                                         + aAttr.getFilter()
195                                                         + " AND "
196                                                         + getAttr(userSet, aAttr).getFilter()
197                                                         + " = "
198                                                         + f);
199                                         if (f != null)
200                                                 aAttr.setFilter(f, true); // force it
201                                         userSet.remove(aAttr);
202                                 }
203                         }
204                         adminSet.addAll(userSet);
205                         return adminSet;
206
207                 } catch (ArpRepositoryException e) {
208                         log.error("Error while searching Arp Repository (" + arpFactory + ") : " + e.getMessage());
209                         throw new AAException("Unable to load user ARP.");
210                 }
211
212         }                   
213                     
214
215     private Set getReleaseSet(Arp arp, String sharName, String url, Arp admin)
216         throws AAException{
217
218         boolean usingDefault = false;
219
220         log.info("using ARP: "+arp);
221
222         ArpShar shar = arp.getShar(sharName);
223         if(shar == null){
224             shar = admin.getDefaultShar();
225             usingDefault = true;
226         }
227         if(shar == null)
228             throw new AAException("No default SHAR.");
229
230         log.debug("\t using shar: "+shar+(usingDefault?"(default)":""));
231         log.debug("\t using url: "+url);
232
233         if(url == null || url.length() == 0)
234             throw new AAException("Given url to AA is null or blank");
235
236         ArpResource resource = shar.bestFit(url);
237         log.debug("\t\t best fit is: "+resource);
238         if(resource == null){
239             if(usingDefault)
240                 return new HashSet(); // empty set
241
242             shar = admin.getDefaultShar();
243             if(shar == null)
244                 throw new AAException("No default SHAR.");
245
246             resource = shar.bestFit(url);
247             if(resource == null)
248                 return new HashSet(); // empty set
249         }
250         Set s = new HashSet();
251         ArpAttribute[] attrs = resource.getAttributes();
252         for(int i=0; i<attrs.length; i++){
253             log.info("\t\t\t attribute: "+attrs[i]+" FILTER: "+attrs[i].getFilter());
254             s.add(attrs[i]);
255         }
256         return s;
257     }
258
259     private ArpFilter combineFilters(ArpAttribute attr1, ArpAttribute attr2){
260
261         ArpFilter filt1 = attr1.getFilter();
262         ArpFilter filt2 = attr2.getFilter();
263         
264         if(filt1 == null)
265             return filt2;
266
267         if(filt2 == null)
268             return filt1;
269
270         ArpFilterValue[]  fv1Array = filt1.getFilterValues();
271         
272         for(int i=0; i<fv1Array.length; i++){
273             ArpFilterValue afv = fv1Array[i];
274
275             if(afv.mustInclude()){  // cannot be filtered out
276                 filt2.removeFilterValue(afv); // ok if not there
277             }else{
278                 filt2.addAFilterValue(afv);
279             }
280         }
281
282         return filt2;
283     
284     }
285     
286
287     private ArpAttribute getAttr(Set s, ArpAttribute a){
288         Iterator it = s.iterator();
289         while(it.hasNext()){
290             ArpAttribute attr = (ArpAttribute)it.next();
291             if(attr.equals(a))
292                 return attr;
293         }
294         return null;
295     }
296     
297         private SAMLAttribute jndi2saml(Attribute jAttr) throws NamingException, AAException {
298
299                 if (jAttr == null) {
300                         return null;
301                 }
302
303                 log.debug("Converting Attribute (" + jAttr.getID() + ") to SAML.");
304                 Vector vals = new Vector();
305
306                 NamingEnumeration ne = jAttr.getAll();
307
308                 while (ne.hasMore()) {
309                         vals.add(ne.next());
310                 }
311
312                 try {
313                         Class attrClass = Class.forName("edu.internet2.middleware.shibboleth.aaLocal.attributes." + jAttr.getID());
314                         log.debug("Loaded the class for " + attrClass);
315                         ShibAttribute sa = (ShibAttribute) attrClass.newInstance();
316                         return sa.toSamlAttribute(this.domain, vals.toArray());
317                 } catch (Exception e) {
318                         log.error("Failed to load the class for attribute (" + jAttr.getID() + ") :" + e);
319                         return null;
320                 }
321
322         }
323 }