1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector;
18
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.StringTokenizer;
24
25 import javax.naming.NamingException;
26 import javax.naming.directory.SearchResult;
27
28 import net.sf.ehcache.Cache;
29 import net.sf.ehcache.Element;
30
31 import org.opensaml.xml.util.DatatypeHelper;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
36 import edu.internet2.middleware.shibboleth.common.attribute.provider.BasicAttribute;
37 import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
38 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethResolutionContext;
39 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.TemplateEngine.CharacterEscapingStrategy;
40 import edu.vt.middleware.ldap.Ldap;
41 import edu.vt.middleware.ldap.SearchFilter;
42 import edu.vt.middleware.ldap.bean.LdapAttribute;
43 import edu.vt.middleware.ldap.bean.LdapAttributes;
44 import edu.vt.middleware.ldap.bean.LdapBeanProvider;
45
46
47
48
49 public class LdapDataConnector extends BaseDataConnector {
50
51
52 public static enum AUTHENTICATION_TYPE {
53
54 ANONYMOUS("none"),
55
56 SIMPLE("simple"),
57
58 STRONG("strong"),
59
60 EXTERNAL("EXTERNAL"),
61
62 DIGEST_MD5("DIGEST-MD5"),
63
64 CRAM_MD5("CRAM-MD5"),
65
66 GSSAPI("GSSAPI");
67
68
69 private String authTypeName;
70
71
72
73
74
75
76 private AUTHENTICATION_TYPE(String s) {
77 authTypeName = s;
78 }
79
80
81
82
83
84
85 public String getAuthTypeName() {
86 return authTypeName;
87 }
88
89
90
91
92
93
94
95 public static AUTHENTICATION_TYPE getAuthenticationTypeByName(String s) {
96 AUTHENTICATION_TYPE type = null;
97 if (AUTHENTICATION_TYPE.ANONYMOUS.getAuthTypeName().equals(s)) {
98 type = AUTHENTICATION_TYPE.ANONYMOUS;
99 } else if (AUTHENTICATION_TYPE.SIMPLE.getAuthTypeName().equals(s)) {
100 type = AUTHENTICATION_TYPE.SIMPLE;
101 } else if (AUTHENTICATION_TYPE.STRONG.getAuthTypeName().equals(s)) {
102 type = AUTHENTICATION_TYPE.STRONG;
103 } else if (AUTHENTICATION_TYPE.EXTERNAL.getAuthTypeName().equals(s)) {
104 type = AUTHENTICATION_TYPE.EXTERNAL;
105 } else if (AUTHENTICATION_TYPE.DIGEST_MD5.getAuthTypeName().equals(s)) {
106 type = AUTHENTICATION_TYPE.DIGEST_MD5;
107 } else if (AUTHENTICATION_TYPE.CRAM_MD5.getAuthTypeName().equals(s)) {
108 type = AUTHENTICATION_TYPE.CRAM_MD5;
109 } else if (AUTHENTICATION_TYPE.GSSAPI.getAuthTypeName().equals(s)) {
110 type = AUTHENTICATION_TYPE.GSSAPI;
111 }
112 return type;
113 }
114 };
115
116
117 private static Logger log = LoggerFactory.getLogger(LdapDataConnector.class);
118
119
120 private LdapPoolStrategy ldapPool;
121
122
123 private TemplateEngine filterCreator;
124
125
126 private String filterTemplateName;
127
128
129 private String filterTemplate;
130
131
132 private String[] returnAttributes;
133
134
135 private boolean noResultsIsError;
136
137
138 private Cache resultsCache;
139
140
141 private final LDAPValueEscapingStrategy escapingStrategy;
142
143
144
145
146
147
148
149 public LdapDataConnector(LdapPoolStrategy pool, Cache cache) {
150 super();
151 ldapPool = pool;
152
153 resultsCache = cache;
154
155 escapingStrategy = new LDAPValueEscapingStrategy();
156 }
157
158
159
160
161
162
163
164 public void registerTemplate(TemplateEngine engine, String template) {
165 if (getId() == null) {
166 throw new IllegalStateException("Template cannot be registered until plugin id has been set");
167 }
168 filterCreator = engine;
169 filterTemplate = template;
170 filterTemplateName = "shibboleth.resolver.dc." + getId();
171 filterCreator.registerTemplate(filterTemplateName, filterTemplate);
172 }
173
174
175 protected void clearCache() {
176 if (isCacheResults()) {
177 resultsCache.removeAll();
178 }
179 }
180
181
182
183
184
185
186 public boolean isCacheResults() {
187 return resultsCache != null;
188 }
189
190
191
192
193
194
195 public boolean isNoResultsIsError() {
196 return noResultsIsError;
197 }
198
199
200
201
202
203
204 public void setNoResultsIsError(boolean isError) {
205 noResultsIsError = isError;
206 }
207
208
209
210
211
212
213 public TemplateEngine getTemplateEngine() {
214 return filterCreator;
215 }
216
217
218
219
220
221
222 public String getFilterTemplate() {
223 return filterTemplate;
224 }
225
226
227
228
229
230
231 public LdapPoolStrategy getLdapPool() {
232 return ldapPool;
233 }
234
235
236
237
238
239
240 public String[] getReturnAttributes() {
241 return returnAttributes;
242 }
243
244
245
246
247
248
249
250
251
252 public void setReturnAttributes(String[] attributes) {
253 returnAttributes = attributes;
254 }
255
256
257
258
259
260
261 public void setReturnAttributes(String s) {
262 StringTokenizer st = new StringTokenizer(s, ",");
263 String[] ra = new String[st.countTokens()];
264 for (int count = 0; count < st.countTokens(); count++) {
265 ra[count] = st.nextToken();
266 }
267 setReturnAttributes(ra);
268 }
269
270
271 public void validate() throws AttributeResolutionException {
272 Ldap ldap = null;
273 try {
274 ldap = ldapPool.checkOut();
275 if(ldap == null){
276 log.error("Unable to retrieve an LDAP connection");
277 throw new AttributeResolutionException("Unable to retrieve LDAP connection");
278 }
279 if (!ldap.connect()) {
280 throw new NamingException();
281 }
282 } catch (NamingException e) {
283 log.error("An error occured when attempting to search the LDAP: " + ldap.getLdapConfig().getEnvironment(),
284 e);
285 throw new AttributeResolutionException("An error occurred when attempting to search the LDAP", e);
286 } catch (Exception e) {
287 log.error("Could not retrieve Ldap object from pool", e);
288 throw new AttributeResolutionException(
289 "An error occurred when attempting to retrieve a LDAP connection from the pool", e);
290 } finally {
291 if (ldap != null) {
292 try {
293 ldapPool.checkIn(ldap);
294 } catch (Exception e) {
295 log.error("Could not return Ldap object back to pool", e);
296 }
297 }
298 }
299 }
300
301
302 public Map<String, BaseAttribute> resolve(ShibbolethResolutionContext resolutionContext)
303 throws AttributeResolutionException {
304 String searchFilter = filterCreator.createStatement(filterTemplateName, resolutionContext, getDependencyIds(),
305 escapingStrategy);
306 searchFilter = searchFilter.trim();
307 log.debug("Search filter: {}", searchFilter);
308
309
310 Map<String, BaseAttribute> attributes = retrieveAttributesFromCache(searchFilter);
311
312
313 if (attributes == null) {
314 Iterator<SearchResult> results = searchLdap(searchFilter);
315
316 if (noResultsIsError && !results.hasNext()) {
317 log.debug("LDAP data connector " + getId()
318 + " - No result returned and connector configured to treat this as an error.");
319 throw new AttributeResolutionException("No LDAP entry found for "
320 + resolutionContext.getAttributeRequestContext().getPrincipalName());
321 }
322
323
324 attributes = buildBaseAttributes(results);
325 cacheResult(searchFilter, attributes);
326 }
327
328 return attributes;
329 }
330
331
332
333
334
335
336
337
338 protected Map<String, BaseAttribute> retrieveAttributesFromCache(String searchFilter) {
339 if (!isCacheResults()) {
340 return null;
341 }
342
343 log.debug("LDAP data connector {} - Checking cache for search results", getId());
344 Element cachedResult = resultsCache.get(searchFilter);
345 if (cachedResult != null && !cachedResult.isExpired()) {
346 log.debug("LDAP data connector {} - Returning attributes from cache", getId());
347 return (Map<String, BaseAttribute>) cachedResult.getObjectValue();
348 }
349
350 log.debug("LDAP data connector {} - No results cached for search filter '{}'", getId(), searchFilter);
351 return null;
352 }
353
354
355
356
357
358
359
360
361 protected Iterator<SearchResult> searchLdap(String searchFilter) throws AttributeResolutionException {
362 log.debug("LDAP data connector {} - Retrieving attributes from LDAP", getId());
363
364 Ldap ldap = null;
365 try {
366 ldap = ldapPool.checkOut();
367 return ldap.search(new SearchFilter(searchFilter), returnAttributes);
368 } catch (NamingException e) {
369 log.debug("LDAP data connector " + getId() + " - An error occured when attempting to search the LDAP: "
370 + ldap.getLdapConfig().getEnvironment(), e);
371 throw new AttributeResolutionException("An error occurred when attempting to search the LDAP");
372 } catch (Exception e) {
373 log.debug("LDAP data connector " + getId() + " - Could not perform ldap search", e);
374 throw new AttributeResolutionException("An error occurred when attempting to perform a LDAP search");
375 } finally {
376 if (ldap != null) {
377 try {
378 ldapPool.checkIn(ldap);
379 } catch (Exception e) {
380 log.error("LDAP data connector " + getId() + " - Could not return Ldap object back to pool", e);
381 }
382 }
383 }
384 }
385
386
387
388
389
390
391
392
393 protected Map<String, BaseAttribute> buildBaseAttributes(Iterator<SearchResult> results)
394 throws AttributeResolutionException {
395
396 Map<String, BaseAttribute> attributes = new HashMap<String, BaseAttribute>();
397
398 if (!results.hasNext()) {
399 return attributes;
400 }
401
402 SearchResult sr = results.next();
403 LdapAttributes ldapAttrs = null;
404 try {
405 ldapAttrs = LdapBeanProvider.getLdapBeanFactory().newLdapAttributes();
406 ldapAttrs.addAttributes(sr.getAttributes());
407 } catch (NamingException e) {
408 log.debug("LDAP data connector " + getId() + " - Error parsing LDAP attributes", e);
409 throw new AttributeResolutionException("Error parsing LDAP attributes", e);
410 }
411
412 for (LdapAttribute ldapAttr : ldapAttrs.getAttributes()) {
413 log.debug("LDAP data connector {} - Found the following attribute: {}", getId(), ldapAttr);
414 BaseAttribute attribute = attributes.get(ldapAttr.getName());
415 if (attribute == null) {
416 attribute = new BasicAttribute<String>(ldapAttr.getName());
417 attributes.put(ldapAttr.getName(), attribute);
418 }
419
420 Set<Object> values = ldapAttr.getValues();
421 if (values != null && !values.isEmpty()) {
422 for (Object value : values) {
423 if (value instanceof String) {
424 String s = (String) value;
425 if (!DatatypeHelper.isEmpty(s)) {
426 attribute.getValues().add(DatatypeHelper.safeTrimOrNullString(s));
427 }
428 } else {
429 log.debug("LDAP data connector {} - Attribute {} contained a value that is not of type String",
430 getId(), ldapAttr.getName());
431 attribute.getValues().add(value);
432 }
433 }
434 }
435 }
436
437 return attributes;
438 }
439
440
441
442
443
444
445
446 protected void cacheResult(String searchFilter, Map<String, BaseAttribute> attributes) {
447 if (!isCacheResults()) {
448 return;
449 }
450
451 log.debug("LDAP data connector {} - Caching attributes from search '{}'", getId(), searchFilter);
452 resultsCache.put(new Element(searchFilter, attributes));
453 }
454
455
456
457
458 protected class LDAPValueEscapingStrategy implements CharacterEscapingStrategy {
459
460
461 public String escape(String value) {
462 return value.replace("*", "\\*").replace("(", "\\(").replace(")", "\\)").replace("\\", "\\");
463 }
464 }
465 }