View Javadoc

1   /**
2    * Copyright 2005-2015 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.impl.role;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import javax.xml.namespace.QName;
28  
29  import org.apache.commons.collections.CollectionUtils;
30  import org.apache.commons.lang.StringUtils;
31  import org.apache.log4j.Logger;
32  import org.kuali.rice.core.api.CoreApiServiceLocator;
33  import org.kuali.rice.core.api.CoreConstants;
34  import org.kuali.rice.core.api.criteria.CriteriaLookupService;
35  import org.kuali.rice.core.api.delegation.DelegationType;
36  import org.kuali.rice.core.api.membership.MemberType;
37  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
38  import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator;
39  import org.kuali.rice.core.api.config.property.ConfigurationService;
40  import org.kuali.rice.coreservice.api.namespace.Namespace;
41  import org.kuali.rice.coreservice.api.namespace.NamespaceService;
42  import org.kuali.rice.kim.api.KimConstants;
43  import org.kuali.rice.kim.api.group.Group;
44  import org.kuali.rice.kim.api.group.GroupService;
45  import org.kuali.rice.kim.api.identity.IdentityService;
46  import org.kuali.rice.kim.api.identity.principal.Principal;
47  import org.kuali.rice.kim.api.role.Role;
48  import org.kuali.rice.kim.api.role.RoleMember;
49  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
50  import org.kuali.rice.kim.api.type.KimType;
51  import org.kuali.rice.kim.api.type.KimTypeAttribute;
52  import org.kuali.rice.kim.api.type.KimTypeInfoService;
53  import org.kuali.rice.kim.framework.role.RoleEbo;
54  import org.kuali.rice.kim.framework.role.RoleTypeService;
55  import org.kuali.rice.kim.framework.type.KimTypeService;
56  import org.kuali.rice.kim.impl.common.attribute.KimAttributeBo;
57  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
58  import org.kuali.rice.kim.impl.common.delegate.DelegateTypeBo;
59  import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService;
60  import org.kuali.rice.kim.impl.role.RoleBoLite;
61  import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
62  import org.kuali.rice.kim.impl.type.KimTypeAttributeBo;
63  import org.kuali.rice.kim.impl.type.KimTypeBo;
64  import org.kuali.rice.krad.service.BusinessObjectService;
65  import org.kuali.rice.krad.service.KRADServiceLocator;
66  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
67  import org.kuali.rice.krad.service.LookupService;
68  import org.kuali.rice.krad.util.KRADPropertyConstants;
69  import org.kuali.rice.krad.util.ObjectUtils;
70  
71  abstract class RoleServiceBase {
72      private static final Logger LOG = Logger.getLogger( RoleServiceBase.class );
73  
74      private BusinessObjectService businessObjectService;
75      private LookupService lookupService;
76      private IdentityService identityService;
77      private NamespaceService namespaceService;
78      private KimTypeInfoService kimTypeInfoService;
79      private GroupService groupService;
80      private ResponsibilityInternalService responsibilityInternalService;
81      private RoleDao roleDao;
82      protected CriteriaLookupService criteriaLookupService;
83  
84      /**
85       * A helper enumeration for indicating which KimRoleDao method to use when attempting to get role/delegation-related lists that are not in the cache.
86       *
87       * @author Kuali Rice Team (rice.collab@kuali.org)
88       */
89      protected static enum RoleDaoAction {
90          ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS,
91          ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS,
92          ROLE_MEMBERS_FOR_ROLE_IDS,
93          ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS,
94          ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS,
95          DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS,
96          DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS,
97          DELEGATION_MEMBERS_FOR_DELEGATION_IDS
98      }
99  
100     /**
101      * Explicitly sets the BusinessObjectService to use. For testability.
102      * @param bos the BusinessObjectService to use
103      */
104     void setBusinessObjectService(BusinessObjectService bos) {
105         businessObjectService = bos;
106     }
107 
108     /**
109      * Converts the Qualifier Name/Value Role qualification set into Qualifier AttributeID/Value set
110      *
111      * @param qualification The original role qualification attribute set
112      * @param validAttributeIds The mapping of attribute names to their matching attribute ids
113      * @return Converted Map<String, String> containing ID/value pairs
114      */
115     private Map<String, String> convertQualifierKeys(Map<String, String> qualification, Map<String, String> validAttributeIds) {
116         Map<String, String> convertedQualification = new HashMap<String, String>();
117         if (qualification != null && CollectionUtils.isNotEmpty(qualification.entrySet())) {
118             for (Map.Entry<String, String> entry : qualification.entrySet()) {
119                 String attributeId = validAttributeIds.get(entry.getKey());
120                 if (StringUtils.isNotEmpty(attributeId)) {
121                     convertedQualification.put(attributeId, entry.getValue());
122                 }
123             }
124         }
125         return convertedQualification;
126     }
127 
128     protected void getNestedRoleTypeMemberIds(String roleId, Set<String> members) {
129         ArrayList<String> roleList = new ArrayList<String>(1);
130         roleList.add(roleId);
131         List<RoleMemberBo> firstLevelMembers = getStoredRoleMembersForRoleIds(roleList, MemberType.ROLE.getCode(), Collections.<String, String>emptyMap());
132         for (RoleMemberBo member : firstLevelMembers) {
133             if (MemberType.ROLE.equals(member.getType())) {
134                 if (!members.contains(member.getMemberId())) {
135                     members.add(member.getMemberId());
136                     getNestedRoleTypeMemberIds(member.getMemberId(), members);
137                 }
138             }
139         }
140     }
141 
142     protected List<RoleMemberBo> getRoleMembersForPrincipalId(String roleId, String principalId) {
143         return roleDao.getRolePrincipalsForPrincipalIdAndRoleIds(Collections.singletonList(roleId), principalId, null);
144     }
145 
146     protected List<RoleMemberBo> getRoleMembersForGroupIds(String roleId, List<String> groupIds) {
147         if (CollectionUtils.isEmpty(groupIds)) {
148             return new ArrayList<RoleMemberBo>();
149         }
150         return roleDao.getRoleMembersForGroupIds(roleId, groupIds);
151     }
152 
153     /**
154      * Retrieves a list of RoleMemberBo instances from the KimRoleDao.
155      *
156      * @param daoActionToTake An indicator for which KimRoleDao method should be used to get the results if the desired RoleMemberBos are not cached.
157      * @param roleIds         The role IDs to filter by; may get used as the IDs for members that are also roles, depending on the daoActionToTake value.
158      * @param principalId     The principal ID to filter by; may get ignored depending on the daoActionToTake value.
159      * @param groupIds        The group IDs to filter by; may get ignored depending on the daoActionToTake value.
160      * @param memberTypeCode  The member type code to filter by; may get overridden depending on the daoActionToTake value.
161      * @param qualification   The original role qualification attribute set
162      * @return A list of RoleMemberBo instances based on the provided parameters.
163      * @throws IllegalArgumentException if daoActionToTake refers to an enumeration constant that is not role-member-related.
164      */
165     protected List<RoleMemberBo> getRoleMemberBoList(RoleDaoAction daoActionToTake, Collection<String> roleIds, String principalId,
166                                                      Collection<String> groupIds, String memberTypeCode, Map<String, String> qualification) {
167         if (roleIds == null || roleIds.isEmpty()) {
168             roleIds = Collections.emptyList();
169         }
170         if (groupIds == null || groupIds.isEmpty()) {
171             groupIds = Collections.emptyList();
172         }
173 
174         Map<String, String> validAttributeIds = new HashMap<String, String>();
175         HashSet <String> kimTypeIds = new HashSet<String>();
176         
177         //Getting unique kim types
178         for (String roleId : roleIds) {
179             RoleBoLite role = getRoleBoLite(roleId);
180             kimTypeIds.add(role.getKimTypeId());
181         }
182 
183         if (qualification != null && CollectionUtils.isNotEmpty(qualification.entrySet())) {
184             for (String kimTypeId : kimTypeIds) {
185                 for (Map.Entry<String, String> entry : qualification.entrySet()) {
186                     validAttributeIds.put(entry.getKey(), getKimAttributeId(kimTypeId, entry.getKey()));
187                 }
188             }
189         }
190 
191         Map<String, String> convertedQualification = convertQualifierKeys(qualification, validAttributeIds);
192 
193         switch (daoActionToTake) {
194             case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS: // Search for principal role members only.
195                 return roleDao.getRolePrincipalsForPrincipalIdAndRoleIds(roleIds, principalId, convertedQualification);
196             case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS: // Search for group role members only.
197                return roleDao.getRoleGroupsForGroupIdsAndRoleIds(roleIds, groupIds, convertedQualification);
198             case ROLE_MEMBERS_FOR_ROLE_IDS: // Search for role members with the given member type code.
199                return roleDao.getRoleMembersForRoleIds(roleIds, memberTypeCode, convertedQualification);
200             case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS: // Search for role members who are also roles.
201                 return roleDao.getRoleMembershipsForRoleIdsAsMembers(roleIds, convertedQualification);
202             case ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS: // Search for role members that might be roles, principals, or groups.
203                 return roleDao.getRoleMembersForRoleIdsWithFilters(roleIds, principalId, groupIds, convertedQualification);
204             default: // This should never happen, since the previous switch block should handle this case appropriately.
205                 throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
206         }
207     }
208 
209     /**
210      * Calls the KimRoleDao's "getRolePrincipalsForPrincipalIdAndRoleIds" method and/or retrieves any corresponding members from the cache.
211      */
212     protected List<RoleMemberBo> getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collection<String> roleIds, String principalId, Map<String, String> qualification) {
213         return getRoleMemberBoList(RoleDaoAction.ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS, roleIds, principalId, Collections.<String>emptyList(), null, qualification);
214     }
215 
216     /**
217      * Calls the KimRoleDao's "getRoleGroupsForGroupIdsAndRoleIds" method and/or retrieves any corresponding members from the cache.
218      */
219     protected List<RoleMemberBo> getStoredRoleGroupsForGroupIdsAndRoleIds(Collection<String> roleIds, Collection<String> groupIds, Map<String, String> qualification) {
220         return getRoleMemberBoList(RoleDaoAction.ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS, roleIds, null, groupIds, null, qualification);
221     }
222 
223     /**
224      * Calls the KimRoleDao's "getRoleMembersForRoleIds" method and/or retrieves any corresponding members from the cache.
225      */
226     protected List<RoleMemberBo> getStoredRoleMembersForRoleIds(Collection<String> roleIds, String memberTypeCode, Map<String, String> qualification) {
227         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS, roleIds, null, Collections.<String>emptyList(), memberTypeCode, qualification);
228     }
229 
230     /**
231      * Calls the KimRoleDao's "getRoleMembershipsForRoleIdsAsMembers" method and/or retrieves any corresponding members from the cache.
232      */
233     protected List<RoleMemberBo> getStoredRoleMembershipsForRoleIdsAsMembers(Collection<String> roleIds, Map<String, String> qualification) {
234         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS, roleIds, null, Collections.<String>emptyList(), null, qualification);
235     }
236 
237     /**
238      * Calls the KimRoleDao's "getRoleMembersForRoleIdsWithFilters" method and/or retrieves any corresponding members from the cache.
239      */
240     protected List<RoleMemberBo> getStoredRoleMembersForRoleIdsWithFilters(Collection<String> roleIds, String principalId, List<String> groupIds, Map<String, String> qualification) {
241         return getRoleMemberBoList(RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS, roleIds, principalId, groupIds, null, qualification);
242     }
243 
244     /**
245      * Retrieves a RoleMemberBo object by its ID. If the role member already exists in the cache, this method will return the cached
246      * version; otherwise, it will retrieve the uncached version from the database and then cache it (if it belongs to a role that allows
247      * its members to be cached) before returning it.
248      */
249     protected RoleMemberBo getRoleMemberBo(String roleMemberId) {
250         if (StringUtils.isBlank(roleMemberId)) {
251             return null;
252         }
253 
254         return getBusinessObjectService().findByPrimaryKey(RoleMemberBo.class, Collections.singletonMap(
255                KimConstants.PrimaryKeyConstants.ID, roleMemberId));
256     }
257 
258     /**
259      * Retrieves a RoleResponsibilityActionBo object by its ID.
260      */
261     protected RoleResponsibilityActionBo getRoleResponsibilityActionBo(String roleResponsibilityActionId) {
262         if (StringUtils.isBlank(roleResponsibilityActionId)) {
263             return null;
264         }
265 
266         return getBusinessObjectService().findByPrimaryKey(RoleResponsibilityActionBo.class, Collections.singletonMap(
267                 KimConstants.PrimaryKeyConstants.ID, roleResponsibilityActionId));
268     }
269 
270     /**
271      * Calls the KimRoleDao's "getDelegationImplMapFromRoleIds" method and/or retrieves any corresponding delegations from the cache.
272      */
273     protected Map<String, DelegateTypeBo> getStoredDelegationImplMapFromRoleIds(Collection<String> roleIds) {
274         if (roleIds != null && !roleIds.isEmpty()) {
275             return roleDao.getDelegationImplMapFromRoleIds(roleIds);
276         }
277 
278         return Collections.emptyMap();
279     }
280 
281     /**
282      * Calls the KimRoleDao's "getDelegationBosForRoleIds" method and/or retrieves any corresponding delegations from the cache.
283      */
284     protected List<DelegateTypeBo> getStoredDelegationImplsForRoleIds(Collection<String> roleIds) {
285         if (roleIds != null && !roleIds.isEmpty()) {
286             return roleDao.getDelegationBosForRoleIds(roleIds);
287         }
288         return Collections.emptyList();
289     }
290 
291     /**
292      * Retrieves a List of delegation members from the KimRoleDao as appropriate.
293      *
294      * @param daoActionToTake An indicator for which KimRoleDao method to use for retrieving results.
295      * @param delegationIds   The IDs of the delegations that the members belong to.
296      * @param principalId     The principal ID of the principal delegation members; may get ignored depending on the RoleDaoAction value.
297      * @param groupIds        The group IDs of the group delegation members; may get ignored depending on the RoleDaoAction value.
298      * @return A List of DelegateMemberBo objects based on the provided parameters.
299      * @throws IllegalArgumentException if daoActionToTake does not represent a delegation-member-list-related enumeration value.
300      */
301     protected List<DelegateMemberBo> getDelegationMemberBoList(RoleDaoAction daoActionToTake, Collection<String> delegationIds,
302                                                                String principalId, List<String> groupIds) {
303         if (delegationIds == null || delegationIds.isEmpty()) {
304             delegationIds = Collections.emptyList();
305         }
306         if (groupIds == null || groupIds.isEmpty()) {
307             groupIds = Collections.emptyList();
308         }
309 
310         switch (daoActionToTake) {
311             case DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS: // Search for principal delegation members.
312                 return roleDao.getDelegationPrincipalsForPrincipalIdAndDelegationIds(delegationIds, principalId);
313             case DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS: // Search for group delegation members.
314                 return roleDao.getDelegationGroupsForGroupIdsAndDelegationIds(delegationIds, groupIds);
315             default: // This should never happen since the previous switch block should handle this case appropriately.
316                 throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-delegation-member-list-related value!");
317         }
318     }
319 
320     /**
321      * Calls the KimRoleDao's "getDelegationPrincipalsForPrincipalIdAndDelegationIds" method and/or retrieves any corresponding members from the cache.
322      */
323     protected List<DelegateMemberBo> getStoredDelegationPrincipalsForPrincipalIdAndDelegationIds(Collection<String> delegationIds, String principalId) {
324         return getDelegationMemberBoList(RoleDaoAction.DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS,
325                 delegationIds, principalId, null);
326     }
327 
328     /**
329      * Retrieves a DelegateMemberBo object by its ID. If the delegation member already exists in the cache, this method will return the cached
330      * version; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
331      */
332     protected DelegateMemberBo getDelegateMemberBo(String delegationMemberId) {
333         if (StringUtils.isBlank(delegationMemberId)) {
334             return null;
335         }
336 
337         return getBusinessObjectService().findByPrimaryKey(DelegateMemberBo.class,
338                 Collections.singletonMap(KimConstants.PrimaryKeyConstants.DELEGATION_MEMBER_ID, delegationMemberId));
339     }
340 
341     /**
342      * Retrieves a DelegateMemberBo List by (principal/group/role) member ID and delegation ID. If the List already exists in the cache,
343      * this method will return the cached one; otherwise, it will retrieve the uncached version from the database and then cache it before returning it.
344      */
345     protected List<DelegateMemberBo> getDelegationMemberBoListByMemberAndDelegationId(String memberId, String delegationId) {
346 
347         Map<String, String> searchCriteria = new HashMap<String, String>();
348         searchCriteria.put(KimConstants.PrimaryKeyConstants.MEMBER_ID, memberId);
349         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_ID, delegationId);
350         return new ArrayList<DelegateMemberBo>(getBusinessObjectService().findMatching(DelegateMemberBo.class, searchCriteria));
351     }
352 
353     protected Object getMember(String memberTypeCode, String memberId) {
354         if (StringUtils.isBlank(memberId)) {
355             return null;
356         }
357         if (MemberType.PRINCIPAL.getCode().equals(memberTypeCode)) {
358             return getIdentityService().getPrincipal(memberId);
359         } else if (MemberType.GROUP.getCode().equals(memberTypeCode)) {
360             return getGroupService().getGroup(memberId);
361         } else if (MemberType.ROLE.getCode().equals(memberTypeCode)) {
362             return getRoleBo(memberId);
363         }
364         return null;
365     }
366 
367     protected String getMemberName(Object member) {
368         if (member == null) {
369             return "";
370         }
371         if (member instanceof Principal) {
372             return ((Principal) member).getPrincipalName();
373         }
374         if (member instanceof Group) {
375             return ((Group) member).getName();
376         }
377         if (member instanceof Role) {
378             return ((Role) member).getName();
379         }
380         return member.toString();
381     }
382 
383     protected RoleBo getRoleBo(String roleId) {
384         if (StringUtils.isBlank(roleId)) {
385             return null;
386         }
387         return getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, roleId);
388     }
389     
390     protected RoleBoLite getRoleBoLite(String roleId) {
391         if (StringUtils.isBlank(roleId)) {
392             return null;
393         }
394         return getBusinessObjectService().findBySinglePrimaryKey(RoleBoLite.class, roleId);
395     }
396 
397     protected DelegateTypeBo getDelegationOfType(String roleId, DelegationType delegationType) {
398         List<DelegateTypeBo> roleDelegates = getRoleDelegations(roleId);
399         if (isDelegationPrimary(delegationType)) {
400             return getPrimaryDelegation(roleId, roleDelegates);
401         } else {
402             return getSecondaryDelegation(roleId, roleDelegates);
403         }
404     }
405 
406     private DelegateTypeBo getSecondaryDelegation(String roleId, List<DelegateTypeBo> roleDelegates) {
407         DelegateTypeBo secondaryDelegate = null;
408         RoleBoLite roleBo = getRoleBoLite(roleId);
409         for (DelegateTypeBo delegate : roleDelegates) {
410             if (isDelegationSecondary(delegate.getDelegationType())) {
411                 secondaryDelegate = delegate;
412             }
413         }
414         if (secondaryDelegate == null) {
415             secondaryDelegate = new DelegateTypeBo();
416             secondaryDelegate.setRoleId(roleId);
417             secondaryDelegate.setDelegationType(DelegationType.SECONDARY);
418             secondaryDelegate.setKimTypeId(roleBo.getKimTypeId());
419         }
420         return secondaryDelegate;
421     }
422 
423     protected DelegateTypeBo getPrimaryDelegation(String roleId, List<DelegateTypeBo> roleDelegates) {
424         DelegateTypeBo primaryDelegate = null;
425         RoleBoLite roleBo = getRoleBoLite(roleId);
426         for (DelegateTypeBo delegate : roleDelegates) {
427             if (isDelegationPrimary(delegate.getDelegationType())) {
428                 primaryDelegate = delegate;
429             }
430         }
431         if (primaryDelegate == null) {
432             primaryDelegate = new DelegateTypeBo();
433             primaryDelegate.setRoleId(roleId);
434             primaryDelegate.setDelegationType(DelegationType.PRIMARY);
435             primaryDelegate.setKimTypeId(roleBo.getKimTypeId());
436         }
437         return primaryDelegate;
438     }
439 
440     protected RoleMemberBo matchingMemberRecord(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
441         for (RoleMemberBo rm : roleMembers) {
442             if (doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
443                 return rm;
444             }
445         }
446         return null;
447     }
448 
449     protected boolean isDelegationPrimary(DelegationType delegationType) {
450         return DelegationType.PRIMARY.equals(delegationType);
451     }
452 
453     protected boolean isDelegationSecondary(DelegationType delegationType) {
454         return DelegationType.SECONDARY.equals(delegationType);
455     }
456 
457 
458     private List<DelegateTypeBo> getRoleDelegations(String roleId) {
459         if (roleId == null) {
460             return new ArrayList<DelegateTypeBo>();
461         }
462         return getStoredDelegationImplsForRoleIds(Collections.singletonList(roleId));
463 
464     }
465 
466     protected RoleBo getRoleBoByName(String namespaceCode, String roleName) {
467         if (StringUtils.isBlank(namespaceCode)
468                 || StringUtils.isBlank(roleName)) {
469             return null;
470         }
471         Map<String, String> criteria = new HashMap<String, String>();
472         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
473         criteria.put(KimConstants.UniqueKeyConstants.NAME, roleName);
474         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
475         // while this is not actually the primary key - there will be at most one row with these criteria
476         return getBusinessObjectService().findByPrimaryKey(RoleBo.class, criteria);
477     }
478     
479     protected RoleBoLite getRoleBoLiteByName(String namespaceCode, String roleName) {
480         if (StringUtils.isBlank(namespaceCode)
481                 || StringUtils.isBlank(roleName)) {
482             return null;
483         }
484         Map<String, String> criteria = new HashMap<String, String>();
485         criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode);
486         criteria.put(KimConstants.UniqueKeyConstants.NAME, roleName);
487         criteria.put(KRADPropertyConstants.ACTIVE, "Y");
488         // while this is not actually the primary key - there will be at most one row with these criteria
489         return getBusinessObjectService().findByPrimaryKey(RoleBoLite.class, criteria);
490     }
491 
492 	protected List<RoleMember> doAnyMemberRecordsMatchByExactQualifier( RoleEbo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier ) {
493 		List<RoleMemberBo> roleMemberBos = getRoleMembersByExactQualifierMatch(role, memberId, daoActionToTake, qualifier);
494         List<RoleMember> roleMembers = new ArrayList<RoleMember>();
495         if(CollectionUtils.isNotEmpty(roleMemberBos)) {
496             for (RoleMemberBo bo : roleMemberBos) {
497                 roleMembers.add(RoleMemberBo.to(bo));
498             }
499 			return roleMembers;
500 		}
501 
502 		return Collections.emptyList();
503 	}
504 	
505 	protected List<RoleMemberBo> getRoleMembersByExactQualifierMatch(RoleEbo role, String memberId, RoleDaoAction daoActionToTake, Map<String, String> qualifier) {
506 		List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
507 		RoleTypeService roleTypeService = getRoleTypeService( role.getId() );
508 		if(roleTypeService != null) {
509     		List<String> attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
510     		if(CollectionUtils.isNotEmpty(attributesForExactMatch)) {
511     			switch (daoActionToTake) {
512 	    			case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS : // Search for group role members only.
513 	        			rms = getStoredRoleGroupsForGroupIdsAndRoleIds(Collections.singletonList(role.getId()), Collections.singletonList(memberId), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
514 	    				break;
515 	    			case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS : // Search for principal role members only.
516 	        			rms = getStoredRolePrincipalsForPrincipalIdAndRoleIds(Collections.singletonList(role.getId()), memberId, populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
517 	    				break;
518 	    			case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS : // Search for roles as role members only.
519 	    				List<RoleMemberBo> allRoleMembers = getStoredRoleMembershipsForRoleIdsAsMembers(Collections.singletonList(role.getId()), populateQualifiersForExactMatch(qualifier, attributesForExactMatch));
520 	        			for(RoleMemberBo rm : allRoleMembers) {
521 	        				if ( rm.getMemberId().equals(memberId) ) { 
522 	        					rms.add(rm);
523 	        				}
524 	        			}
525                         break;
526 	    			default : // The daoActionToTake parameter is invalid; throw an exception.
527 	    				throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot refer to a non-role-member-related value!");
528     			}
529     			
530     		} 
531 		}
532 		return rms;
533 	}
534     
535     //return roleMemberId of match or null if no match
536     protected RoleMember doAnyMemberRecordsMatch(List<RoleMemberBo> roleMembers, String memberId, String memberTypeCode, Map<String, String> qualifier) {
537         for (RoleMemberBo rm : roleMembers) {
538             if (rm.isActive() && doesMemberMatch(rm, memberId, memberTypeCode, qualifier)) {
539                 return RoleMemberBo.to(rm);
540             }
541         }
542         return null;
543     }
544 
545     protected boolean doesMemberMatch(RoleMemberBo roleMember, String memberId, String memberTypeCode, Map<String, String> qualifier) {
546         if (roleMember.getMemberId().equals(memberId) && roleMember.getType().getCode().equals(memberTypeCode)) {
547             // member ID/type match
548             Map<String, String> roleQualifier = roleMember.getAttributes();
549             if ((qualifier == null || qualifier.isEmpty())
550                     && (roleQualifier == null || roleQualifier.isEmpty())) {
551                 return true; // blank qualifier match
552             } else {
553                 if (qualifier != null && roleQualifier != null && qualifier.equals(roleQualifier)) {
554                     return true; // qualifier match
555                 }
556             }
557         }
558         return false;
559     }
560     
561     /**
562      * Retrieves the role type service associated with the given role ID
563      *
564      * @param roleId the role ID to get the role type service for
565      * @return the Role Type Service
566      */
567     protected RoleTypeService getRoleTypeService(String roleId) {
568         RoleBoLite roleBo = getRoleBoLite(roleId);
569         if(roleBo != null){
570             KimType roleType = KimTypeBo.to(roleBo.getKimRoleType());
571             if (roleType != null) {
572                 return getRoleTypeService(roleType);
573             }
574         }
575         return KimImplServiceLocator.getDefaultRoleTypeService();
576     }
577 
578     /**
579      * Retrieves the role type service for the given service name.
580      *
581      * @param serviceName the name of the service to retrieve
582      * @return the Role Type Service
583      */
584     protected RoleTypeService getRoleTypeServiceByName(String serviceName) {
585         try {
586             KimTypeService service = (KimTypeService) GlobalResourceLoader.getService(QName.valueOf(serviceName));
587             if (service != null && service instanceof RoleTypeService) {
588                 return (RoleTypeService) service;
589             }
590             LOG.warn("Unable to find role type service by name: " + serviceName + ". Defaulting to: kimNoMembersRoleTypeService ");
591             return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
592         } catch (Exception ex) {
593             LOG.warn("Unable to find role type service by name: " + serviceName, ex);
594             return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
595         }
596     }
597 
598     protected RoleTypeService getRoleTypeService(KimType typeInfo) {
599         String serviceName = typeInfo.getServiceName();
600         if (serviceName != null) {
601             try {
602                 KimTypeService service = (KimTypeService) GlobalResourceLoader.getService(QName.valueOf(serviceName));
603                 if (service != null && service instanceof RoleTypeService) {
604                     return (RoleTypeService) service;
605                 }
606                 LOG.warn("Unable to find role type service with name: " + serviceName + ". Defaulting to: kimNoMembersRoleTypeService ");
607                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
608             } catch (Exception ex) {
609                 LOG.error("Unable to find role type service with name: " + serviceName, ex);
610                 return (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
611             }
612         }
613         return KimImplServiceLocator.getDefaultRoleTypeService();
614     }
615     
616     protected Map<String, String> populateQualifiersForExactMatch(Map<String, String> defaultQualification, List<String> attributes) {
617         Map<String,String> qualifiersForExactMatch = new HashMap<String,String>();
618         if (defaultQualification != null && CollectionUtils.isNotEmpty(defaultQualification.keySet())) {
619             for (String attributeName : attributes) {
620                 if (StringUtils.isNotEmpty(defaultQualification.get(attributeName))) {
621                     qualifiersForExactMatch.put(attributeName, defaultQualification.get(attributeName));
622                 }
623             }
624         }
625         return qualifiersForExactMatch;
626     }
627 
628     // TODO: pulling attribute IDs repeatedly is inefficient - consider caching the entire list as a map
629     /*
630      * search by attribute name, if none return null, if there is only one, return. If there are multiple, then
631      * search by kimType: if found return else 
632      *     search by appId of kimType : if found return else
633      *          search by rice app id : if found return else
634      *              search by kuali app id : if found return else
635      *                  return null.
636      */
637     protected String getKimAttributeId(String kimTypeId, String attributeName) {
638         Collection<KimAttributeBo> attributeData = getAttributeByName(attributeName);
639         String kimAttributeId = null;
640         
641         if (CollectionUtils.isNotEmpty(attributeData)) {
642             if (CollectionUtils.size(attributeData) == 1) {
643                 kimAttributeId = attributeData.iterator().next().getId();
644             } else {
645                 kimAttributeId = getCorrectAttributeId(kimTypeId, attributeName, attributeData);
646             }
647         }
648         
649         return kimAttributeId;
650     }
651     
652     /*
653      * Searches the KimAttributeBo for the attribute by name
654      */
655     protected Collection<KimAttributeBo> getAttributeByName(String attributeName) {
656         Map<String, Object> critieria = new HashMap<String, Object>(1);
657         critieria.put(KimConstants.AttributeConstants.ATTRIBUTE_NAME, attributeName);
658         Collection<KimAttributeBo> attributeData = getBusinessObjectService().findMatching(KimAttributeBo.class, critieria);
659         
660         return attributeData;
661     }
662     
663     /*
664      * Attempts to get the right attribute for the kimType. If it fails, then tries by namespace.
665      */
666     protected String getCorrectAttributeId(String kimTypeId, String attributeName, Collection<KimAttributeBo> attributeData) {
667         KimType kimType = getKimTypeInfoService().getKimType(kimTypeId);
668         String attribute = getAttributeFromKimType(kimType, attributeName);
669         
670         return ObjectUtils.isNotNull(attribute) ? attribute : getAttributeFromNamespace(kimType, attributeName, attributeData);
671     }
672     
673     protected String getAttributeFromKimType(KimType kimType, String attributeName) {
674         if (kimType != null) {
675             for (KimTypeAttribute attribute : kimType.getAttributeDefinitions()) {
676                 if (attribute.getKimAttribute() != null
677                         && StringUtils.equals(attributeName, attribute.getKimAttribute().getAttributeName())) {
678                     return attribute.getKimAttribute().getId();
679                 }
680             }
681         }
682         
683         return null;
684     }
685     
686     /*
687      * Gets the attribute based on the app namespace, if it cannot find then tries Rice namespace and then Kuali.
688      */
689     protected String getAttributeFromNamespace(KimType kimType, String attributeName, Collection<KimAttributeBo> attributes) {
690         String appId = getAppIdFromNamespace(kimType.getNamespaceCode());
691         String attributeId = getAttributeFromAppId(attributes, appId);
692 
693         if (ObjectUtils.isNull(attributeId)) {
694             attributeId = getAttributeFromAppId(attributes, KimConstants.KIM_TYPE_RICE_NAMESPACE);
695             if (ObjectUtils.isNull(attributeId)) {
696                 attributeId = getAttributeFromAppId(attributes, KimConstants.KIM_TYPE_DEFAULT_NAMESPACE);
697             }
698         }
699         
700         return attributeId;
701     }
702     
703     protected String getAppIdFromNamespace(String namespaceCode) {
704         Namespace appNamespace = getNamespaceService().getNamespace(namespaceCode);
705         if (appNamespace == null) {
706             throw new RuntimeException("Namespace " + namespaceCode + " not mapped in namespace table.");
707         }
708         
709         return appNamespace.getApplicationId();
710     }
711     
712     /*
713      * Compares the appId of the attribute with the given appId.
714      * Here we make the assumption that there are not multiple attributes with the same name
715      * for a given application.
716      */
717     protected String getAttributeFromAppId(Collection<KimAttributeBo> attributes, String appId) {
718         for (KimAttributeBo attribute : attributes) {
719             if (StringUtils.equalsIgnoreCase(getAppIdFromNamespace(attribute.getNamespaceCode()), appId)) {
720                 return attribute.getId();
721             }
722         }
723         
724         return null;
725     }
726     
727     protected KimTypeInfoService getKimTypeInfoService() {
728         if (kimTypeInfoService == null) {
729             kimTypeInfoService = KimApiServiceLocator.getKimTypeInfoService();
730         }
731         
732         return kimTypeInfoService;
733     }
734 
735     protected NamespaceService getNamespaceService() {
736         if (namespaceService == null) {
737             namespaceService = CoreServiceApiServiceLocator.getNamespaceService();
738         }
739         
740         return namespaceService;
741     }
742 
743     protected BusinessObjectService getBusinessObjectService() {
744         if (businessObjectService == null) {
745             businessObjectService = KRADServiceLocator.getBusinessObjectService();
746         }
747         return businessObjectService;
748     }
749 
750     /**
751      * @return the lookupService
752      */
753     protected LookupService getLookupService() {
754         if (lookupService == null) {
755             lookupService = KRADServiceLocatorWeb.getLookupService();
756         }
757         return lookupService;
758     }
759 
760     protected IdentityService getIdentityService() {
761         if (identityService == null) {
762             identityService = KimApiServiceLocator.getIdentityService();
763         }
764 
765         return identityService;
766     }
767     
768     protected GroupService getGroupService() {
769         if (groupService == null) {
770             groupService = KimApiServiceLocator.getGroupService();
771         }
772 
773         return groupService;
774     }
775 
776     protected ResponsibilityInternalService getResponsibilityInternalService() {
777         if (responsibilityInternalService == null) {
778             responsibilityInternalService = KimImplServiceLocator.getResponsibilityInternalService();
779         }
780         return responsibilityInternalService;
781     }
782 
783     /**
784      * @return the roleDao
785      */
786     protected RoleDao getRoleDao() {
787         return this.roleDao;
788     }
789 
790     /**
791      * @param roleDao the roleDao to set
792      */
793     public void setRoleDao(RoleDao roleDao) {
794         this.roleDao = roleDao;
795     }
796 
797     public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) {
798         this.criteriaLookupService = criteriaLookupService;
799     }
800 
801     public CriteriaLookupService getCriteriaLookupService() {
802         return criteriaLookupService;
803     }
804 
805 }