1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kim.dao.impl;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.kuali.rice.coreservice.framework.parameter.ParameterService;
20 import org.kuali.rice.kim.api.identity.entity.Entity;
21 import org.kuali.rice.kim.api.identity.entity.EntityDefault;
22 import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
23 import org.kuali.rice.kim.api.identity.principal.Principal;
24 import org.kuali.rice.kim.api.identity.privacy.EntityPrivacyPreferences;
25 import org.kuali.rice.kim.dao.LdapPrincipalDao;
26 import org.kuali.rice.kim.impl.identity.PersonImpl;
27 import org.kuali.rice.kim.ldap.InvalidLdapEntityException;
28 import org.kuali.rice.kim.util.Constants;
29 import org.springframework.ldap.SizeLimitExceededException;
30 import org.springframework.ldap.core.ContextMapper;
31 import org.springframework.ldap.core.ContextMapperCallbackHandler;
32 import org.springframework.ldap.core.DistinguishedName;
33 import org.springframework.ldap.core.LdapTemplate;
34 import org.springframework.ldap.filter.AndFilter;
35 import org.springframework.ldap.filter.LikeFilter;
36 import org.springframework.ldap.filter.NotFilter;
37 import org.springframework.ldap.filter.OrFilter;
38
39 import javax.naming.NameClassPair;
40 import javax.naming.directory.SearchControls;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.regex.Matcher;
47 import java.util.regex.Pattern;
48
49 import static org.kuali.rice.core.util.BufferedLogger.*;
50 import static org.kuali.rice.kns.lookup.LookupUtils.getSearchResultsLimit;
51
52
53
54
55
56
57
58 public class LdapPrincipalDaoImpl implements LdapPrincipalDao {
59 private Constants kimConstants;
60 private LdapTemplate template;
61 private ParameterService parameterService;
62
63
64 private Map<String, ContextMapper> contextMappers;
65
66 public LdapPrincipalDaoImpl() {
67 }
68
69
70
71
72 public Principal getPrincipal(String principalId) {
73 if (principalId == null) {
74 return null;
75 }
76 Map<String, Object> criteria = new HashMap();
77 criteria.put(getKimConstants().getKimLdapIdProperty(), principalId);
78 List<Principal> results = search(Principal.class, criteria);
79
80 if (results.size() > 0) {
81 return results.get(0);
82 }
83
84 return null;
85 }
86
87
88
89
90 public Principal getPrincipalByName(String principalName) {
91 if (principalName == null) {
92 return null;
93 }
94 Map<String, Object> criteria = new HashMap();
95 criteria.put(getKimConstants().getKimLdapNameProperty(), principalName);
96 List<Principal> results = search(Principal.class, criteria);
97
98 if (results.size() > 0) {
99 return results.get(0);
100 }
101
102 return null;
103 }
104
105 public <T> List<T> search(Class<T> type, Map<String, Object> criteria) {
106 AndFilter filter = new AndFilter();
107
108 for (Map.Entry<String, Object> entry : criteria.entrySet()) {
109
110 if (entry.getValue() == null) {
111 entry.setValue("null");
112 }
113 if (entry.getValue() instanceof Iterable) {
114 OrFilter orFilter = new OrFilter();
115 for (String value : (Iterable<String>) entry.getValue()) {
116 if (value.startsWith("!")) {
117 orFilter.or(new NotFilter(new LikeFilter(entry.getKey(), value.substring(1))));
118 } else {
119 orFilter.or(new LikeFilter(entry.getKey(), value));
120 }
121 }
122 filter.and(orFilter);
123 }
124 else {
125 if (((String)entry.getValue()).startsWith("!")) {
126 filter.and(new NotFilter(new LikeFilter(entry.getKey(), ((String)entry.getValue()).substring(1))));
127 } else {
128 filter.and(new LikeFilter(entry.getKey(), (String) entry.getValue()));
129 }
130 }
131 };
132
133 info("Using filter ", filter);
134
135 debug("Looking up mapper for ", type.getSimpleName());
136 final ContextMapper customMapper = contextMappers.get(type.getSimpleName());
137
138 ContextMapperCallbackHandler callbackHandler = new CustomContextMapperCallbackHandler(customMapper);
139
140 try {
141 getLdapTemplate().search(DistinguishedName.EMPTY_PATH,
142 filter.encode(),
143 getSearchControls(), callbackHandler);
144 }
145 catch (SizeLimitExceededException e) {
146
147 }
148
149 return callbackHandler.getList();
150 }
151
152 protected SearchControls getSearchControls() {
153 SearchControls retval = new SearchControls();
154 retval.setCountLimit(getSearchResultsLimit(PersonImpl.class).longValue());
155 retval.setSearchScope(SearchControls.SUBTREE_SCOPE);
156 return retval;
157 }
158
159
160
161
162
163
164
165 public Entity getEntity(String entityId) {
166 if (entityId == null) {
167 return null;
168 }
169 Map<String, Object> criteria = new HashMap();
170 criteria.put(getKimConstants().getKimLdapIdProperty(), entityId);
171
172 List<Entity> results = search(Entity.class, criteria);
173
174 debug("Got results from info lookup ", results, " with size ", results.size());
175
176 if (results.size() > 0) {
177 return results.get(0);
178 }
179
180 return null;
181 }
182
183
184
185
186
187
188 public Entity getEntityByPrincipalId(String principalId) {
189 if (principalId == null) {
190 return null;
191 }
192 final Principal principal = getPrincipal(principalId);
193 if (principal != null && !StringUtils.isBlank(principal.getEntityId())) {
194 return getEntity(principal.getEntityId());
195 }
196 return null;
197 }
198
199 public EntityDefault getEntityDefault(String entityId) {
200 if (entityId == null) {
201 return null;
202 }
203 Map<String, Object> criteria = new HashMap();
204 criteria.put(getKimConstants().getKimLdapIdProperty(), entityId);
205
206 List<EntityDefault> results = search(EntityDefault.class, criteria);
207
208 debug("Got results from info lookup ", results, " with size ", results.size());
209
210 if (results.size() > 0) {
211 return results.get(0);
212 }
213
214 return null;
215 }
216
217
218
219
220
221
222 public EntityDefault getEntityDefaultByPrincipalId(String principalId) {
223 return getEntityDefault(principalId);
224 }
225
226 public EntityDefault getEntityDefaultByPrincipalName(String principalName) {
227 Map<String, Object> criteria = new HashMap();
228 criteria.put(getKimConstants().getKimLdapNameProperty(), principalName);
229
230 List<EntityDefault> results = search(EntityDefault.class, criteria);
231 if (results.size() > 0) {
232 return results.get(0);
233 }
234
235 return null;
236 }
237
238 public Entity getEntityByPrincipalName(String principalName) {
239 Map<String, Object> criteria = new HashMap();
240 criteria.put(getKimConstants().getKimLdapNameProperty(), principalName);
241
242 List<Entity> results = search(Entity.class, criteria);
243 if (results.size() > 0) {
244 return results.get(0);
245 }
246
247 return null;
248 }
249
250 public List<EntityDefault> lookupEntityDefault(Map<String,String> searchCriteria, boolean unbounded) {
251 List<EntityDefault> results = new ArrayList();
252 Map<String, Object> criteria = getLdapLookupCriteria(searchCriteria);
253
254 results = search(EntityDefault.class, criteria);
255
256 return results;
257 }
258
259 public List<String> lookupEntityIds(Map<String,String> searchCriteria) {
260 final List<String> results = new ArrayList<String>();
261 final Map<String, Object> criteria = getLdapLookupCriteria(searchCriteria);
262
263 for (final Entity entity : search(Entity.class, criteria)) {
264 results.add(entity.getId());
265 }
266
267 return results;
268 }
269
270
271
272
273
274
275 protected Map<String, Object> getLdapLookupCriteria(Map<String, String> searchCriteria) {
276 Map<String, Object> criteria = new HashMap();
277 boolean hasTaxId = false;
278
279 for (Map.Entry<String, String> criteriaEntry : searchCriteria.entrySet()) {
280 debug(String.format("Searching with criteria %s = %s", criteriaEntry.getKey(), criteriaEntry.getValue()));
281 String valueName = criteriaEntry.getKey();
282 Object value = criteriaEntry.getValue();
283 if (!criteriaEntry.getValue().equals("*")) {
284 valueName = String.format("%s.%s", criteriaEntry.getKey(), criteriaEntry.getValue());
285 }
286
287 if (!value.equals("*") && isMapped(valueName)) {
288 value = getLdapValue(valueName);
289 debug(value, " mapped to valueName ", valueName);
290 }
291
292 if (isMapped(criteriaEntry.getKey())) {
293 debug(String.format("Setting attribute to (%s, %s)",
294 getLdapAttribute(criteriaEntry.getKey()),
295 value));
296 final String key = getLdapAttribute(criteriaEntry.getKey());
297 if (!criteria.containsKey(key)) {
298 criteria.put(key, value);
299 }
300 }
301 else if (criteriaEntry.getKey().equalsIgnoreCase(getKimConstants().getExternalIdProperty())) {
302 criteria.put(getKimConstants().getKimLdapIdProperty(), value);
303 }
304 else if (criteriaEntry.getKey().equalsIgnoreCase(getKimConstants().getExternalIdTypeProperty())
305 && value.toString().equals(getKimConstants().getTaxExternalIdTypeCode())) {
306 hasTaxId = true;
307 }
308 }
309 return criteria;
310 }
311
312 public EntityPrivacyPreferences getEntityPrivacyPreferences(String entityId) {
313 if (entityId == null) {
314 return null;
315 }
316 Map<String, Object> criteria = new HashMap();
317 criteria.put(getKimConstants().getKimLdapIdProperty(), entityId);
318
319 List<EntityPrivacyPreferences> results = search(EntityPrivacyPreferences.class, criteria);
320 if (results.size() > 0) {
321 return results.get(0);
322 }
323
324 return null;
325 }
326
327 public Map<String, EntityNamePrincipalName> getDefaultNamesForPrincipalIds(List<String> principalIds) {
328 Map<String, Object> criteria = new HashMap();
329 Map<String, EntityNamePrincipalName> retval = new HashMap();
330 criteria.put(getKimConstants().getKimLdapIdProperty(), principalIds);
331
332 List<EntityNamePrincipalName> results = search(EntityNamePrincipalName.class, criteria);
333
334 for (EntityNamePrincipalName nameInfo : results) {
335 retval.put(nameInfo.getPrincipalName(), nameInfo);
336 }
337 return retval;
338 }
339
340 public Map<String, EntityNamePrincipalName> getDefaultNamesForEntityIds(List<String> entityIds) {
341 return getDefaultNamesForPrincipalIds(entityIds);
342 }
343
344 protected Matcher getKimAttributeMatcher(String kimAttribute) {
345 String mappedParamValue = getParameterService().getParameterValueAsString(getKimConstants().getParameterNamespaceCode(),
346 getKimConstants().getParameterDetailTypeCode(),
347 getKimConstants().getMappedParameterName());
348
349 String regexStr = String.format("(%s|.*;%s)=([^=;]*).*", kimAttribute, kimAttribute);
350 debug("Matching KIM attribute with regex ", regexStr);
351 Matcher retval = Pattern.compile(regexStr).matcher(mappedParamValue);
352
353 if (!retval.matches()) {
354 mappedParamValue = getParameterService().getParameterValueAsString(getKimConstants().getParameterNamespaceCode(),
355 getKimConstants().getParameterDetailTypeCode(),
356 getKimConstants().getMappedValuesName());
357 retval = Pattern.compile(regexStr).matcher(mappedParamValue);
358 }
359
360 return retval;
361 }
362
363 protected boolean isMapped(String kimAttribute) {
364 debug("Matching " + kimAttribute);
365 debug("Does ", kimAttribute, " match? ", getKimAttributeMatcher(kimAttribute).matches());
366 return getKimAttributeMatcher(kimAttribute).matches();
367 }
368
369 protected String getLdapAttribute(String kimAttribute) {
370 Matcher matcher = getKimAttributeMatcher(kimAttribute);
371 debug("Does ", kimAttribute, " match? ", matcher.matches());
372 if (matcher.matches()) {
373 return matcher.group(2);
374 } else {
375 return null;
376 }
377 }
378
379 protected Object getLdapValue(String kimAttribute) {
380 Matcher matcher = getKimAttributeMatcher(kimAttribute);
381 debug("Does ", kimAttribute, " match? ", matcher.matches());
382 if (!matcher.matches()) {
383 return null;
384 }
385 String value = matcher.group(2);
386
387
388 if (value.contains(",")) {
389 return Arrays.asList(value.split(","));
390 }
391
392 return value;
393 }
394
395 public void setKimConstants(Constants constants) {
396 this.kimConstants = constants;
397 }
398
399 public Constants getKimConstants() {
400 return kimConstants;
401 }
402
403 public ParameterService getParameterService() {
404 return this.parameterService;
405 }
406
407 public void setParameterService(ParameterService service) {
408 this.parameterService = service;
409 }
410
411 public LdapTemplate getLdapTemplate() {
412 return template;
413 }
414
415 public void setLdapTemplate(LdapTemplate template) {
416 this.template = template;
417 }
418
419 public Map<String, ContextMapper> getContextMappers() {
420 return this.contextMappers;
421 }
422
423 public void setContextMappers(final Map<String, ContextMapper> contextMappers) {
424 this.contextMappers = contextMappers;
425 }
426
427
428
429
430
431
432
433 private static final class CustomContextMapperCallbackHandler extends ContextMapperCallbackHandler {
434 public CustomContextMapperCallbackHandler(ContextMapper mapper) {
435 super(mapper);
436 }
437
438 public void handleNameClassPair(NameClassPair nameClassPair) {
439 try {
440 super.handleNameClassPair(nameClassPair);
441 }
442 catch (InvalidLdapEntityException ieee) {
443 warn("LDAP Search Results yielded an invalid result from ", nameClassPair);
444 }
445 }
446 }
447 }