Coverage Report - org.kuali.rice.kim.dao.impl.LdapPrincipalDaoImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
LdapPrincipalDaoImpl
0%
0/166
0%
0/72
2.774
LdapPrincipalDaoImpl$CustomContextMapperCallbackHandler
0%
0/7
N/A
2.774
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 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  
 package org.kuali.rice.kim.dao.impl;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.kuali.rice.core.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  
  * Integrated Data Access via LDAP to EDS. Provides implementation to interface method
 54  
  * for using Spring-LDAP to communicate with EDS.
 55  
  *
 56  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 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  0
     public LdapPrincipalDaoImpl() {
 67  0
     }
 68  
                             
 69  
     /**
 70  
      * In EDS, the principalId, principalName, and entityId will all be the same.
 71  
      */
 72  
     public Principal getPrincipal(String principalId) {
 73  0
         if (principalId == null) {
 74  0
             return null;
 75  
         }
 76  0
         Map<String, Object> criteria = new HashMap();
 77  0
         criteria.put(getKimConstants().getKimLdapIdProperty(), principalId);
 78  0
         List<Principal> results = search(Principal.class, criteria);
 79  
 
 80  0
         if (results.size() > 0) {
 81  0
             return results.get(0);
 82  
         }
 83  
 
 84  0
         return null;
 85  
     }
 86  
 
 87  
     /**
 88  
      * Assuming he principalId, principalName, and entityId will all be the same.
 89  
      */
 90  
     public Principal getPrincipalByName(String principalName) {
 91  0
         if (principalName == null) {
 92  0
             return null;
 93  
         }
 94  0
         Map<String, Object> criteria = new HashMap();
 95  0
         criteria.put(getKimConstants().getKimLdapNameProperty(), principalName);
 96  0
         List<Principal> results = search(Principal.class, criteria);
 97  
 
 98  0
         if (results.size() > 0) {
 99  0
             return results.get(0);
 100  
         }
 101  
         
 102  0
         return null;
 103  
     }
 104  
 
 105  
     public <T> List<T> search(Class<T> type, Map<String, Object> criteria) {
 106  0
         AndFilter filter = new AndFilter();
 107  
         
 108  0
         for (Map.Entry<String, Object> entry : criteria.entrySet()) {
 109  
             //attempting to handle null values to prevent NPEs in this code.
 110  0
             if (entry.getValue() == null) {
 111  0
                 entry.setValue("null");
 112  
             }
 113  0
             if (entry.getValue() instanceof Iterable) {
 114  0
                 OrFilter orFilter = new OrFilter();
 115  0
                 for (String value : (Iterable<String>) entry.getValue()) {
 116  0
                     if (value.startsWith("!")) {
 117  0
                         orFilter.or(new NotFilter(new LikeFilter(entry.getKey(), value.substring(1))));
 118  
                     } else {
 119  0
                         orFilter.or(new LikeFilter(entry.getKey(), value));
 120  
                     }
 121  
                 }
 122  0
                 filter.and(orFilter);
 123  0
             }
 124  
             else {
 125  0
                 if (((String)entry.getValue()).startsWith("!")) {
 126  0
                     filter.and(new NotFilter(new LikeFilter(entry.getKey(), ((String)entry.getValue()).substring(1))));
 127  
                 } else {
 128  0
                     filter.and(new LikeFilter(entry.getKey(), (String) entry.getValue()));
 129  
                 }
 130  
             }
 131  
         };
 132  
         
 133  0
         info("Using filter ", filter);
 134  
 
 135  0
         debug("Looking up mapper for ", type.getSimpleName());
 136  0
         final ContextMapper customMapper = contextMappers.get(type.getSimpleName());
 137  
 
 138  0
         ContextMapperCallbackHandler callbackHandler = new CustomContextMapperCallbackHandler(customMapper);
 139  
         
 140  
         try {
 141  0
             getLdapTemplate().search(DistinguishedName.EMPTY_PATH, 
 142  
                                      filter.encode(), 
 143  
                                      getSearchControls(), callbackHandler);
 144  
         }
 145  0
         catch (SizeLimitExceededException e) {
 146  
             // Ignore this. We want to limit our results.
 147  0
         }
 148  
 
 149  0
         return callbackHandler.getList();
 150  
     }
 151  
 
 152  
     protected SearchControls getSearchControls() {
 153  0
         SearchControls retval = new SearchControls();
 154  0
         retval.setCountLimit(getSearchResultsLimit(PersonImpl.class).longValue());
 155  0
         retval.setSearchScope(SearchControls.SUBTREE_SCOPE);
 156  0
         return retval;
 157  
     }
 158  
 
 159  
         /**
 160  
      * FIND entity objects based on the given criteria. 
 161  
      * 
 162  
      * @param entityId of user/person to grab entity information for
 163  
      * @return {@link Entity}
 164  
      */
 165  
         public Entity getEntity(String entityId) {
 166  0
             if (entityId == null) {
 167  0
                 return null;
 168  
             }
 169  0
         Map<String, Object> criteria = new HashMap();
 170  0
         criteria.put(getKimConstants().getKimLdapIdProperty(), entityId);
 171  
 
 172  0
         List<Entity> results = search(Entity.class, criteria);
 173  
 
 174  0
         debug("Got results from info lookup ", results, " with size ", results.size());
 175  
 
 176  0
         if (results.size() > 0) {
 177  0
             return results.get(0);
 178  
         }
 179  
         
 180  0
         return null;
 181  
     }
 182  
         
 183  
         /**
 184  
          * Fetches full entity info, populated from EDS, based on the Entity's principal id
 185  
          * @param principalId the principal id to look the entity up for
 186  
          * @return the corresponding entity info
 187  
          */
 188  
         public Entity getEntityByPrincipalId(String principalId) {
 189  0
             if (principalId == null) {
 190  0
                 return null;
 191  
             }
 192  0
            final Principal principal = getPrincipal(principalId);
 193  0
            if (principal != null && !StringUtils.isBlank(principal.getEntityId())) {
 194  0
                return getEntity(principal.getEntityId());
 195  
            }
 196  0
            return null;
 197  
         }
 198  
 
 199  
         public EntityDefault getEntityDefault(String entityId) {
 200  0
             if (entityId == null) {
 201  0
                 return null;
 202  
             }
 203  0
         Map<String, Object> criteria = new HashMap();
 204  0
         criteria.put(getKimConstants().getKimLdapIdProperty(), entityId);
 205  
 
 206  0
         List<EntityDefault> results = search(EntityDefault.class, criteria);
 207  
 
 208  0
         debug("Got results from info lookup ", results, " with size ", results.size());
 209  
 
 210  0
         if (results.size() > 0) {
 211  0
             return results.get(0);
 212  
         }
 213  
         
 214  0
         return null;
 215  
     }
 216  
 
 217  
     /**
 218  
      * entityid and principalId are treated as the same.
 219  
      * 
 220  
      * @see #getEntityDefaultInfo(String)
 221  
      */
 222  
         public EntityDefault getEntityDefaultByPrincipalId(String principalId) {
 223  0
         return getEntityDefault(principalId);
 224  
     }
 225  
 
 226  
         public EntityDefault getEntityDefaultByPrincipalName(String principalName) {
 227  0
         Map<String, Object> criteria = new HashMap();
 228  0
         criteria.put(getKimConstants().getKimLdapNameProperty(), principalName);
 229  
 
 230  0
         List<EntityDefault> results = search(EntityDefault.class, criteria);
 231  0
         if (results.size() > 0) {
 232  0
             return results.get(0);
 233  
         }
 234  
         
 235  0
         return null;
 236  
     }
 237  
 
 238  
         public Entity getEntityByPrincipalName(String principalName) {
 239  0
         Map<String, Object> criteria = new HashMap();
 240  0
         criteria.put(getKimConstants().getKimLdapNameProperty(), principalName);
 241  
 
 242  0
         List<Entity> results = search(Entity.class, criteria);
 243  0
         if (results.size() > 0) {
 244  0
             return results.get(0);
 245  
         }
 246  
         
 247  0
         return null;
 248  
     }
 249  
 
 250  
         public List<EntityDefault> lookupEntityDefault(Map<String,String> searchCriteria, boolean unbounded) {
 251  0
         List<EntityDefault> results = new ArrayList();
 252  0
         Map<String, Object> criteria = getLdapLookupCriteria(searchCriteria);
 253  
         
 254  0
         results = search(EntityDefault.class, criteria);
 255  
 
 256  0
         return results;
 257  
     }
 258  
 
 259  
         public List<String> lookupEntityIds(Map<String,String> searchCriteria) {
 260  0
         final List<String> results = new ArrayList<String>();
 261  0
         final Map<String, Object> criteria = getLdapLookupCriteria(searchCriteria);
 262  
         
 263  0
         for (final Entity entity : search(Entity.class, criteria)) {
 264  0
             results.add(entity.getId());
 265  
         }
 266  
         
 267  0
         return results;
 268  
     }
 269  
     
 270  
     /**
 271  
      * Converts Kuali Lookup parameters into LDAP query parameters
 272  
      * @param searchCriteria kuali lookup info
 273  
      * @return {@link Map} of LDAP query info
 274  
      */
 275  
     protected Map<String, Object> getLdapLookupCriteria(Map<String, String> searchCriteria) {
 276  0
         Map<String, Object> criteria = new HashMap();
 277  0
         boolean hasTaxId = false;
 278  
         
 279  0
         for (Map.Entry<String, String> criteriaEntry : searchCriteria.entrySet()) {
 280  0
             debug(String.format("Searching with criteria %s = %s", criteriaEntry.getKey(), criteriaEntry.getValue()));
 281  0
             String valueName = criteriaEntry.getKey();            
 282  0
             Object value = criteriaEntry.getValue();
 283  0
             if (!criteriaEntry.getValue().equals("*")) {
 284  0
                 valueName = String.format("%s.%s", criteriaEntry.getKey(), criteriaEntry.getValue());
 285  
             }
 286  
 
 287  0
             if (!value.equals("*") && isMapped(valueName)) {
 288  0
                 value = getLdapValue(valueName);
 289  0
                 debug(value, " mapped to valueName ", valueName);
 290  
             }
 291  
         
 292  0
             if (isMapped(criteriaEntry.getKey())) {
 293  0
                 debug(String.format("Setting attribute to (%s, %s)", 
 294  
                                     getLdapAttribute(criteriaEntry.getKey()), 
 295  
                                     value));
 296  0
                 final String key = getLdapAttribute(criteriaEntry.getKey());
 297  0
                 if (!criteria.containsKey(key)) {
 298  0
                     criteria.put(key, value);
 299  
                 }
 300  0
             }
 301  0
             else if (criteriaEntry.getKey().equalsIgnoreCase(getKimConstants().getExternalIdProperty())) {
 302  0
                 criteria.put(getKimConstants().getKimLdapIdProperty(), value);
 303  
             }
 304  0
             else if (criteriaEntry.getKey().equalsIgnoreCase(getKimConstants().getExternalIdTypeProperty()) 
 305  
                      && value.toString().equals(getKimConstants().getTaxExternalIdTypeCode())) {
 306  0
                 hasTaxId = true;
 307  
             }
 308  0
         }
 309  0
         return criteria;
 310  
     }
 311  
 
 312  
         public EntityPrivacyPreferences getEntityPrivacyPreferences(String entityId) {
 313  0
             if (entityId == null) {
 314  0
                 return null;
 315  
             }
 316  0
         Map<String, Object> criteria = new HashMap();
 317  0
         criteria.put(getKimConstants().getKimLdapIdProperty(), entityId);
 318  
 
 319  0
         List<EntityPrivacyPreferences> results = search(EntityPrivacyPreferences.class, criteria);
 320  0
         if (results.size() > 0) {
 321  0
             return results.get(0);
 322  
         }
 323  
         
 324  0
         return null;
 325  
     }
 326  
         
 327  
     public Map<String, EntityNamePrincipalName> getDefaultNamesForPrincipalIds(List<String> principalIds) {
 328  0
         Map<String, Object> criteria = new HashMap();
 329  0
         Map<String, EntityNamePrincipalName> retval = new HashMap();
 330  0
         criteria.put(getKimConstants().getKimLdapIdProperty(), principalIds);
 331  
 
 332  0
         List<EntityNamePrincipalName> results = search(EntityNamePrincipalName.class, criteria);
 333  
 
 334  0
         for (EntityNamePrincipalName nameInfo : results) {
 335  0
             retval.put(nameInfo.getPrincipalName(), nameInfo);
 336  
         }
 337  0
         return retval;
 338  
     }
 339  
 
 340  
     public Map<String, EntityNamePrincipalName> getDefaultNamesForEntityIds(List<String> entityIds) {
 341  0
         return getDefaultNamesForPrincipalIds(entityIds);
 342  
     }
 343  
 
 344  
     protected Matcher getKimAttributeMatcher(String kimAttribute) {
 345  0
         String mappedParamValue = getParameterService().getParameterValueAsString(getKimConstants().getParameterNamespaceCode(),
 346  
                                                                                   getKimConstants().getParameterDetailTypeCode(),
 347  
                                                                                   getKimConstants().getMappedParameterName());
 348  
 
 349  0
         String regexStr = String.format("(%s|.*;%s)=([^=;]*).*", kimAttribute, kimAttribute);
 350  0
         debug("Matching KIM attribute with regex ", regexStr);
 351  0
         Matcher retval = Pattern.compile(regexStr).matcher(mappedParamValue);
 352  
         
 353  0
         if (!retval.matches()) {
 354  0
             mappedParamValue = getParameterService().getParameterValueAsString(getKimConstants().getParameterNamespaceCode(),
 355  
                                                                           getKimConstants().getParameterDetailTypeCode(),
 356  
                                                                           getKimConstants().getMappedValuesName());
 357  0
             retval = Pattern.compile(regexStr).matcher(mappedParamValue);
 358  
         }
 359  
 
 360  0
         return retval;
 361  
     }
 362  
 
 363  
     protected boolean isMapped(String kimAttribute) {
 364  0
         debug("Matching " + kimAttribute);
 365  0
         debug("Does ", kimAttribute, " match? ", getKimAttributeMatcher(kimAttribute).matches());
 366  0
         return getKimAttributeMatcher(kimAttribute).matches();
 367  
     }
 368  
 
 369  
     protected String getLdapAttribute(String kimAttribute) {
 370  0
         Matcher matcher = getKimAttributeMatcher(kimAttribute);
 371  0
         debug("Does ", kimAttribute, " match? ", matcher.matches());
 372  0
         if (matcher.matches()) { 
 373  0
             return matcher.group(2);
 374  
         } else {
 375  0
             return null;
 376  
         }
 377  
     }
 378  
 
 379  
     protected Object getLdapValue(String kimAttribute) {
 380  0
         Matcher matcher = getKimAttributeMatcher(kimAttribute);
 381  0
         debug("Does ", kimAttribute, " match? ", matcher.matches());
 382  0
         if (!matcher.matches()) {
 383  0
             return null;
 384  
         }
 385  0
         String value = matcher.group(2);
 386  
 
 387  
         // If it's actually a list. It can only be a list if there are commas
 388  0
         if (value.contains(",")) {
 389  0
             return Arrays.asList(value.split(","));
 390  
         }
 391  
 
 392  0
         return value;
 393  
     }
 394  
 
 395  
     public void setKimConstants(Constants constants) {
 396  0
         this.kimConstants = constants;
 397  0
     }
 398  
 
 399  
     public Constants getKimConstants() {
 400  0
         return kimConstants;
 401  
     }
 402  
 
 403  
     public ParameterService getParameterService() {
 404  0
         return this.parameterService;
 405  
     }
 406  
 
 407  
     public void setParameterService(ParameterService service) {
 408  0
         this.parameterService = service;
 409  0
     }
 410  
 
 411  
     public LdapTemplate getLdapTemplate() {
 412  0
         return template;
 413  
     }
 414  
 
 415  
     public void setLdapTemplate(LdapTemplate template) {
 416  0
         this.template = template;
 417  0
     }
 418  
     
 419  
     public Map<String, ContextMapper> getContextMappers() {
 420  0
         return this.contextMappers;
 421  
     }
 422  
 
 423  
     public void setContextMappers(final Map<String, ContextMapper> contextMappers) {
 424  0
         this.contextMappers = contextMappers;
 425  0
     }
 426  
 
 427  
     /**
 428  
      * Overrides the existing {@link ContextMapperCallbackHandler} because we want to 
 429  
      * intercede when there is invalid results from EDS.
 430  
      * 
 431  
      * @author Leo Przybylski (przybyls@arizona.edu)
 432  
      */
 433  
     private static final class CustomContextMapperCallbackHandler extends ContextMapperCallbackHandler {
 434  
         public CustomContextMapperCallbackHandler(ContextMapper mapper) {
 435  0
             super(mapper);
 436  0
         }
 437  
         
 438  
         public void handleNameClassPair(NameClassPair nameClassPair) {
 439  
             try {
 440  0
                 super.handleNameClassPair(nameClassPair);
 441  
             }
 442  0
             catch (InvalidLdapEntityException ieee) {
 443  0
                 warn("LDAP Search Results yielded an invalid result from ", nameClassPair);
 444  0
             }
 445  0
         }
 446  
     }
 447  
 }