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