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