View Javadoc

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