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 static org.kuali.rice.core.api.criteria.PredicateFactory.equal;
19  
20  import java.sql.Timestamp;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Date;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import javax.jws.WebParam;
34  import javax.sql.DataSource;
35  import javax.xml.namespace.QName;
36  
37  import org.apache.commons.collections.CollectionUtils;
38  import org.apache.commons.lang.StringUtils;
39  import org.apache.commons.lang.exception.ExceptionUtils;
40  import org.apache.log4j.Logger;
41  import org.joda.time.DateTime;
42  import org.kuali.rice.core.api.CoreConstants;
43  import org.kuali.rice.core.api.cache.CacheKeyUtils;
44  import org.kuali.rice.core.api.criteria.QueryByCriteria;
45  import org.kuali.rice.core.api.criteria.QueryResults;
46  import org.kuali.rice.core.api.delegation.DelegationType;
47  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
48  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
49  import org.kuali.rice.core.api.membership.MemberType;
50  import org.kuali.rice.core.api.mo.ModelObjectUtils;
51  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
52  import org.kuali.rice.core.api.util.VersionHelper;
53  import org.kuali.rice.kim.api.KimConstants;
54  import org.kuali.rice.kim.api.common.delegate.DelegateMember;
55  import org.kuali.rice.kim.api.common.delegate.DelegateType;
56  import org.kuali.rice.kim.api.identity.principal.Principal;
57  import org.kuali.rice.kim.api.role.DelegateMemberQueryResults;
58  import org.kuali.rice.kim.api.role.Role;
59  import org.kuali.rice.kim.api.role.RoleMember;
60  import org.kuali.rice.kim.api.role.RoleMemberQueryResults;
61  import org.kuali.rice.kim.api.role.RoleMembership;
62  import org.kuali.rice.kim.api.role.RoleMembershipQueryResults;
63  import org.kuali.rice.kim.api.role.RoleQueryResults;
64  import org.kuali.rice.kim.api.role.RoleResponsibility;
65  import org.kuali.rice.kim.api.role.RoleResponsibilityAction;
66  import org.kuali.rice.kim.api.role.RoleService;
67  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
68  import org.kuali.rice.kim.api.type.KimType;
69  import org.kuali.rice.kim.api.type.KimTypeUtils;
70  import org.kuali.rice.kim.framework.common.delegate.DelegationTypeService;
71  import org.kuali.rice.kim.framework.role.RoleTypeService;
72  import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
73  import org.kuali.rice.kim.framework.type.KimTypeService;
74  import org.kuali.rice.kim.impl.common.attribute.AttributeTransform;
75  import org.kuali.rice.kim.impl.common.attribute.KimAttributeDataBo;
76  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberAttributeDataBo;
77  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
78  import org.kuali.rice.kim.impl.common.delegate.DelegateTypeBo;
79  import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
80  import org.kuali.rice.kim.impl.type.KimTypeBo;
81  import org.kuali.rice.krad.data.platform.MaxValueIncrementerFactory;
82  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
83  import org.kuali.rice.ksb.api.bus.Endpoint;
84  import org.kuali.rice.ksb.api.bus.ServiceBus;
85  import org.springframework.cache.Cache;
86  import org.springframework.cache.CacheManager;
87  import org.springframework.cache.support.NoOpCacheManager;
88  import org.springframework.util.LinkedMultiValueMap;
89  import org.springframework.util.MultiValueMap;
90  
91  public class RoleServiceImpl extends RoleServiceBase implements RoleService {
92  
93      private static final Logger LOG = Logger.getLogger(RoleServiceImpl.class);
94  
95      private static final Map<String, RoleDaoAction> memberTypeToRoleDaoActionMap =
96              populateMemberTypeToRoleDaoActionMap();
97  
98      private static Map<String, RoleDaoAction> populateMemberTypeToRoleDaoActionMap() {
99          Map<String, RoleDaoAction> map = new HashMap<String, RoleDaoAction>();
100         map.put(MemberType.GROUP.getCode(), RoleDaoAction.ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS);
101         map.put(MemberType.PRINCIPAL.getCode(), RoleDaoAction.ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS);
102         map.put(MemberType.ROLE.getCode(), RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS);
103         return Collections.unmodifiableMap(map);
104     }
105 
106     private RoleService proxiedRoleService;
107     private CacheManager cacheManager;
108 
109     public RoleServiceImpl() {
110         this.cacheManager = new NoOpCacheManager();
111     }
112 
113     @Override
114     public Role createRole(final Role role) throws RiceIllegalArgumentException, RiceIllegalStateException {
115         incomingParamCheck(role, "role");
116 
117         if (StringUtils.isNotBlank(role.getId()) && getRole(role.getId()) != null) {
118             throw new RiceIllegalStateException("the role to create already exists: " + role);
119         }
120         RoleBo bo = RoleBo.from(role);
121         return RoleBo.to(getDataObjectService().save(bo));
122     }
123 
124     @Override
125     public Role updateRole(final Role role) throws RiceIllegalArgumentException, RiceIllegalStateException {
126         incomingParamCheck(role, "role");
127 
128         RoleBoLite originalRole = getRoleBoLite(role.getId());
129         if (StringUtils.isBlank(role.getId()) || originalRole == null) {
130             throw new RiceIllegalStateException("the role does not exist: " + role);
131         }
132 
133         RoleBo bo = RoleBo.from(role);
134 
135         RoleBo updatedRole = getDataObjectService().save(bo);
136         if (originalRole.isActive() && !updatedRole.isActive()) {
137             KimImplServiceLocator.getRoleInternalService().roleInactivated(updatedRole.getId());
138         }
139         return RoleBo.to(updatedRole);
140     }
141 
142     /**
143      * This method tests to see if assigning a roleBo to another roleBo will create a circular reference.
144      * The Role is checked to see if it is a member (direct or nested) of the roleBo to be assigned as a member.
145      *
146      * @return true  - assignment is allowed, no circular reference will be created.
147      * false - illegal assignment, it will create a circular membership
148      */
149     protected boolean checkForCircularRoleMembership(String newMemberId, RoleBo roleBo) {
150         // get all nested roleBo members that are of type roleBo
151         Set<String> newRoleMemberIds = getRoleTypeRoleMemberIds(newMemberId);
152         return !newRoleMemberIds.contains(roleBo.getId());
153     }
154 
155     protected RoleMember findRoleMember(String roleMemberId) {
156         final List<RoleMember> roleMembers = findRoleMembers(QueryByCriteria.Builder.fromPredicates(equal(
157                 KimConstants.PrimaryKeyConstants.ID, roleMemberId))).getResults();
158         if (roleMembers != null && !roleMembers.isEmpty()) {
159             return roleMembers.get(0);
160         }
161         return null;
162     }
163 
164     @Override
165     public RoleMemberQueryResults findRoleMembers(QueryByCriteria queryByCriteria) throws RiceIllegalStateException {
166         incomingParamCheck(queryByCriteria, "queryByCriteria");
167 
168         QueryResults<RoleMemberBo> results = getDataObjectService().findMatching(RoleMemberBo.class,
169                 AttributeTransform.getInstance().apply(queryByCriteria));
170 
171         RoleMemberQueryResults.Builder builder = RoleMemberQueryResults.Builder.create();
172         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
173         builder.setTotalRowCount(results.getTotalRowCount());
174 
175         final List<RoleMember.Builder> ims = new ArrayList<RoleMember.Builder>();
176         for (RoleMemberBo bo : results.getResults()) {
177             ims.add(RoleMember.Builder.create(bo));
178         }
179 
180         builder.setResults(ims);
181         return builder.build();
182     }
183 
184     @Override
185     public Set<String> getRoleTypeRoleMemberIds(String roleId) throws RiceIllegalArgumentException {
186         incomingParamCheck(roleId, "roleId");
187 
188         Set<String> results = new HashSet<String>();
189         getNestedRoleTypeMemberIds(roleId, results);
190         return Collections.unmodifiableSet(results);
191     }
192 
193     @Override
194     public List<String> getMemberParentRoleIds(String memberType, String memberId) throws RiceIllegalStateException {
195         incomingParamCheck(memberType, "memberType");
196         incomingParamCheck(memberId, "memberId");
197 
198         List<RoleMemberBo> parentRoleMembers = getRoleMembershipsForMemberId(memberType, memberId,
199                 Collections.<String, String>emptyMap());
200 
201         List<String> parentRoleIds = new ArrayList<String>(parentRoleMembers.size());
202         for (RoleMemberBo parentRoleMember : parentRoleMembers) {
203             parentRoleIds.add(parentRoleMember.getRoleId());
204         }
205 
206         return parentRoleIds;
207     }
208 
209     @Override
210     public List<RoleResponsibilityAction> getRoleMemberResponsibilityActions(String roleMemberId)
211             throws RiceIllegalStateException {
212         incomingParamCheck(roleMemberId, "roleMemberId");
213 
214         QueryResults<RoleResponsibilityActionBo> responsibilityActionBoList = getDataObjectService().findMatching(
215                 RoleResponsibilityActionBo.class, QueryByCriteria.Builder.forAttribute(
216                 KimConstants.PrimaryKeyConstants.ROLE_MEMBER_ID, roleMemberId).build());
217 
218         List<RoleResponsibilityAction> roleResponsibilityActionsList = new ArrayList<RoleResponsibilityAction>();
219         for (RoleResponsibilityActionBo roleResponsibilityActionBo : responsibilityActionBoList.getResults()) {
220             RoleResponsibilityAction roleResponsibility = RoleResponsibilityActionBo.to(roleResponsibilityActionBo);
221             roleResponsibilityActionsList.add(roleResponsibility);
222         }
223         return roleResponsibilityActionsList;
224     }
225 
226     @Override
227     public DelegateMemberQueryResults findDelegateMembers(QueryByCriteria queryByCriteria)
228             throws RiceIllegalStateException {
229         incomingParamCheck(queryByCriteria, "queryByCriteria");
230 
231         QueryResults<DelegateMemberBo> results = getDataObjectService().findMatching(DelegateMemberBo.class,
232                 AttributeTransform.getInstance().apply(queryByCriteria));
233 
234         DelegateMemberQueryResults.Builder builder = DelegateMemberQueryResults.Builder.create();
235         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
236         builder.setTotalRowCount(results.getTotalRowCount());
237 
238         final List<DelegateMember.Builder> ims = new ArrayList<DelegateMember.Builder>();
239         for (DelegateMemberBo bo : results.getResults()) {
240             ims.add(DelegateMember.Builder.create(bo));
241         }
242 
243         builder.setResults(ims);
244         return builder.build();
245     }
246 
247     @Override
248     public Role getRole(String roleId) throws RiceIllegalStateException {
249         incomingParamCheck(roleId, "roleId");
250         return loadRole(roleId);
251     }
252 
253     /**
254      * Loads the role with the given id, leveraging the cache where possible and querying the database
255      * if role not already in the cache. If the role is not in the cache, then it will be placed in
256      * the cache once it is loaded.
257      */
258     protected Role loadRole(String roleId) {
259         Role role = getRoleFromCache(roleId);
260         if (role == null) {
261             RoleBoLite roleBo = getRoleBoLite(roleId);
262             if (roleBo != null) {
263                 role = RoleBoLite.to(roleBo);
264                 putRoleInCache(role);
265             }
266         }
267         return role;
268     }
269 
270     protected Role getRoleFromCache(String id) {
271         Cache cache = cacheManager.getCache(Role.Cache.NAME);
272         Cache.ValueWrapper cachedValue = cache.get("id=" + id);
273         if (cachedValue != null) {
274             return (Role) cachedValue.get();
275         }
276         return null;
277     }
278 
279     protected Role getRoleFromCache(String namespaceCode, String name) {
280         Cache cache = cacheManager.getCache(Role.Cache.NAME);
281         Cache.ValueWrapper cachedValue = cache.get("namespaceCode=" + namespaceCode + "|name=" + name);
282         if (cachedValue != null) {
283             return (Role) cachedValue.get();
284         }
285         return null;
286     }
287 
288     protected void putRoleInCache(Role role) {
289         if (role != null) {
290             Cache cache = cacheManager.getCache(Role.Cache.NAME);
291             String idKey = "id=" + role.getId();
292             String nameKey = "namespaceCode=" + role.getNamespaceCode() + "|name=" + role.getName();
293             cache.put(idKey, role);
294             cache.put(nameKey, role);
295         }
296     }
297 
298     protected Map<String, RoleBoLite> getRoleBoLiteMap(Collection<String> roleIds) {
299         Map<String, RoleBoLite> result;
300         // check for a non-null result in the cache, return it if found
301         if (roleIds.size() == 1) {
302             String roleId = roleIds.iterator().next();
303             RoleBoLite bo = getRoleBoLite(roleId);
304             if (bo == null) {
305                 return Collections.<String, RoleBoLite>emptyMap();
306             }
307             result = bo.isActive() ? Collections.singletonMap(roleId, bo) : Collections.<String, RoleBoLite>emptyMap();
308         } else {
309             result = new HashMap<String, RoleBoLite>(roleIds.size());
310             for (String roleId : roleIds) {
311                 RoleBoLite bo = getRoleBoLite(roleId);
312                 if (bo != null && bo.isActive()) {
313                     result.put(roleId, bo);
314                 }
315             }
316         }
317         return result;
318     }
319 
320     @Override
321     public List<Role> getRoles(List<String> roleIds) throws RiceIllegalStateException {
322         if (CollectionUtils.isEmpty(roleIds)) {
323             throw new RiceIllegalArgumentException("roleIds is null or empty");
324         }
325         return Collections.unmodifiableList(loadRoles(roleIds));
326     }
327 
328     /**
329      * Loads the roles with the given ids, leveraging the cache where possible and querying the database
330      * for role ids not already in the cache. If the role is not in the cache, then it will be placed in
331      * the cache once it is loaded.
332      */
333     protected List<Role> loadRoles(List<String> roleIds) {
334         List<String> remainingRoleIds = new ArrayList<String>();
335         Map<String, Role> roleMap = new HashMap<String, Role>(roleIds.size());
336         for (String roleId : roleIds) {
337             Role role = getRoleFromCache(roleId);
338             if (role != null) {
339                 roleMap.put(roleId, role);
340             } else {
341                 remainingRoleIds.add(roleId);
342             }
343         }
344         if (!remainingRoleIds.isEmpty()) {
345             Map<String, RoleBoLite> roleBoMap = getRoleBoLiteMap(remainingRoleIds);
346             for (String roleId : roleBoMap.keySet()) {
347                 RoleBoLite roleBo = roleBoMap.get(roleId);
348                 if (roleBo != null) {
349                     Role role = RoleBoLite.to(roleBo);
350                     roleMap.put(roleId, role);
351                     putRoleInCache(role);
352                 }
353             }
354         }
355         List<Role> roles = new ArrayList<Role>(roleMap.values());
356         return roles;
357     }
358 
359     @Override
360     public Role getRoleByNamespaceCodeAndName(String namespaceCode, String roleName) throws RiceIllegalStateException {
361         incomingParamCheck(namespaceCode, "namespaceCode");
362         incomingParamCheck(roleName, "roleName");
363         return loadRoleByName(namespaceCode, roleName);
364     }
365 
366     /**
367      * Loads the role with the given name, leveraging the cache where possible and querying the database
368      * if role not already in the cache. If the role is not in the cache, then it will be placed in
369      * the cache once it is loaded.
370      */
371     protected Role loadRoleByName(String namespaceCode, String roleName) {
372         Role role = getRoleFromCache(namespaceCode, roleName);
373         if (role == null) {
374             RoleBoLite roleBo = getRoleBoLiteByName(namespaceCode, roleName);
375             if (roleBo != null) {
376                 role = getRoleFromCache(roleBo.getId());
377                 if (role == null) {
378                     role = RoleBoLite.to(roleBo);
379                 }
380                 putRoleInCache(role);
381             }
382         }
383         return role;
384     }
385 
386     @Override
387     public String getRoleIdByNamespaceCodeAndName(String namespaceCode, String roleName)
388             throws RiceIllegalStateException {
389         incomingParamCheck(namespaceCode, "namespaceCode");
390         incomingParamCheck(roleName, "roleName");
391 
392         Role role = getRoleByNamespaceCodeAndName(namespaceCode, roleName);
393         if (role != null) {
394             return role.getId();
395         } else {
396             return null;
397         }
398     }
399 
400     @Override
401     public boolean isRoleActive(String roleId) throws RiceIllegalStateException {
402         incomingParamCheck(roleId, "roleId");
403         Role role = getRole(roleId);
404         return role != null && role.isActive();
405     }
406 
407     @Override
408     public List<Map<String, String>> getRoleQualifersForPrincipalByRoleIds(String principalId, List<String> roleIds,
409             Map<String, String> qualification) throws RiceIllegalStateException {
410         incomingParamCheck(principalId, "principalId");
411         incomingParamCheck(roleIds, "roleIds");
412 
413         List<Map<String, String>> results = new ArrayList<Map<String, String>>();
414 
415         List<RoleMemberBo> roleMemberBoList = getStoredRoleMembersUsingExactMatchOnQualification(principalId, null,
416                 roleIds, qualification);
417 
418         Map<String, List<RoleMembership>> roleIdToMembershipMap = new HashMap<String, List<RoleMembership>>();
419         for (RoleMemberBo roleMemberBo : roleMemberBoList) {
420             // gather up the qualifier sets and the service they go with
421             if (MemberType.PRINCIPAL.equals(roleMemberBo.getType())) {
422                 RoleTypeService roleTypeService = getRoleTypeService(roleMemberBo.getRoleId());
423                 if (roleTypeService != null) {
424                     List<RoleMembership> las = roleIdToMembershipMap.get(roleMemberBo.getRoleId());
425                     if (las == null) {
426                         las = new ArrayList<RoleMembership>();
427                         roleIdToMembershipMap.put(roleMemberBo.getRoleId(), las);
428                     }
429                     RoleMembership mi = RoleMembership.Builder.create(roleMemberBo.getRoleId(), roleMemberBo.getId(),
430                             roleMemberBo.getMemberId(), roleMemberBo.getType(), roleMemberBo.getAttributes(),
431                             roleMemberBo.getActiveFromDate(), roleMemberBo.getActiveToDate()).build();
432 
433                     las.add(mi);
434                 } else {
435                     results.add(roleMemberBo.getAttributes());
436                 }
437             }
438         }
439         for (Map.Entry<String, List<RoleMembership>> entry : roleIdToMembershipMap.entrySet()) {
440             RoleTypeService roleTypeService = getRoleTypeService(entry.getKey());
441             //it is possible that the the roleTypeService is coming from a remote application
442             // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
443             try {
444                 List<RoleMembership> matchingMembers = roleTypeService.getMatchingRoleMemberships(qualification,
445                         entry.getValue());
446                 for (RoleMembership rmi : matchingMembers) {
447                     results.add(rmi.getQualifier());
448                 }
449             } catch (Exception ex) {
450                 LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + entry.getKey(), ex);
451             }
452         }
453         return Collections.unmodifiableList(results);
454     }
455 
456     @Override
457     public List<Map<String, String>> getRoleQualifersForPrincipalByNamespaceAndRolename(String principalId,
458             String namespaceCode, String roleName, Map<String, String> qualification) throws RiceIllegalStateException {
459         incomingParamCheck(principalId, "principalId");
460         incomingParamCheck(namespaceCode, "namespaceCode");
461         incomingParamCheck(roleName, "roleName");
462 
463         String roleId = getRoleIdByNamespaceCodeAndName(namespaceCode, roleName);
464         if (roleId == null) {
465             return Collections.emptyList();
466         }
467         return getNestedRoleQualifiersForPrincipalByRoleIds(principalId, Collections.singletonList(roleId),
468                 qualification);
469     }
470 
471     @Override
472     public List<Map<String, String>> getNestedRoleQualifersForPrincipalByNamespaceAndRolename(String principalId,
473             String namespaceCode, String roleName, Map<String, String> qualification) throws RiceIllegalStateException {
474         incomingParamCheck(principalId, "principalId");
475         incomingParamCheck(namespaceCode, "namespaceCode");
476         incomingParamCheck(roleName, "roleName");
477 
478         String roleId = getRoleIdByNamespaceCodeAndName(namespaceCode, roleName);
479         if (roleId == null) {
480             return new ArrayList<Map<String, String>>(0);
481         }
482         return getNestedRoleQualifiersForPrincipalByRoleIds(principalId, Collections.singletonList(roleId),
483                 qualification);
484     }
485 
486     @Override
487     public List<Map<String, String>> getNestedRoleQualifiersForPrincipalByRoleIds(String principalId,
488             List<String> roleIds, Map<String, String> qualification) throws RiceIllegalStateException {
489         incomingParamCheck(principalId, "principalId");
490         incomingParamCheck(roleIds, "roleIds");
491 
492         List<Map<String, String>> results = new ArrayList<Map<String, String>>();
493 
494         Map<String, RoleBoLite> roleBosById = getRoleBoLiteMap(roleIds);
495 
496         // get the person's groups
497         List<String> groupIds = getGroupService().getGroupIdsByPrincipalId(principalId);
498         List<RoleMemberBo> roleMemberBos = getStoredRoleMembersUsingExactMatchOnQualification(principalId, groupIds,
499                 roleIds, qualification);
500 
501         Map<String, List<RoleMembership>> roleIdToMembershipMap = new HashMap<String, List<RoleMembership>>();
502         for (RoleMemberBo roleMemberBo : roleMemberBos) {
503             RoleTypeService roleTypeService = getRoleTypeService(roleMemberBo.getRoleId());
504             // gather up the qualifier sets and the service they go with
505             if (MemberType.PRINCIPAL.equals(roleMemberBo.getType()) || MemberType.GROUP.equals(
506                     roleMemberBo.getType())) {
507                 if (roleTypeService != null) {
508                     List<RoleMembership> las = roleIdToMembershipMap.get(roleMemberBo.getRoleId());
509                     if (las == null) {
510                         las = new ArrayList<RoleMembership>();
511                         roleIdToMembershipMap.put(roleMemberBo.getRoleId(), las);
512                     }
513                     RoleMembership mi = RoleMembership.Builder.create(roleMemberBo.getRoleId(), roleMemberBo.getId(),
514                             roleMemberBo.getMemberId(), roleMemberBo.getType(), roleMemberBo.getAttributes(),
515                             roleMemberBo.getActiveFromDate(), roleMemberBo.getActiveToDate()).build();
516 
517                     las.add(mi);
518                 } else {
519                     results.add(roleMemberBo.getAttributes());
520                 }
521             } else if (MemberType.ROLE.equals(roleMemberBo.getType())) {
522                 // find out if the user has the role
523                 // need to convert qualification using this role's service
524                 Map<String, String> nestedQualification = qualification;
525                 if (roleTypeService != null) {
526                     RoleBoLite roleBo = roleBosById.get(roleMemberBo.getRoleId());
527                     // pulling from here as the nested roleBo is not necessarily (and likely is not)
528                     // in the roleBosById Map created earlier
529                     RoleBoLite nestedRole = getRoleBoLite(roleMemberBo.getMemberId());
530                     //it is possible that the the roleTypeService is coming from a remote application
531                     // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
532                     try {
533                         nestedQualification = getNestedQualification(nestedRole, roleBo.getNamespaceCode(),
534                                 roleBo.getName(), nestedRole.getNamespaceCode(), nestedRole.getName(), qualification,
535                                 roleMemberBo.getAttributes());
536                     } catch (Exception ex) {
537                         LOG.warn("Not able to retrieve RoleTypeService from remote system for roleBo Id: " + roleBo
538                                 .getId(), ex);
539                     }
540                 }
541                 List<String> nestedRoleId = new ArrayList<String>(1);
542                 nestedRoleId.add(roleMemberBo.getMemberId());
543                 // if the user has the given role, add the qualifier the *nested role* has with the
544                 // originally queries role
545                 if (this.getProxiedRoleService().principalHasRole(principalId, nestedRoleId, nestedQualification,
546                         false)) {
547                     results.add(roleMemberBo.getAttributes());
548                 }
549             }
550         }
551         for (Map.Entry<String, List<RoleMembership>> entry : roleIdToMembershipMap.entrySet()) {
552             RoleTypeService roleTypeService = getRoleTypeService(entry.getKey());
553             //it is possible that the the roleTypeService is coming from a remote
554             // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
555             try {
556                 List<RoleMembership> matchingMembers = roleTypeService.getMatchingRoleMemberships(qualification,
557                         entry.getValue());
558                 for (RoleMembership roleMembership : matchingMembers) {
559                     results.add(roleMembership.getQualifier());
560                 }
561             } catch (Exception ex) {
562                 LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + entry.getKey(), ex);
563             }
564         }
565         return Collections.unmodifiableList(results);
566     }
567 
568     @Override
569     public List<RoleMembership> getRoleMembers(List<String> roleIds, Map<String, String> qualification)
570             throws RiceIllegalStateException {
571         incomingParamCheck(roleIds, "roleIds");
572 
573         Set<String> foundRoleTypeMembers = new HashSet<String>();
574         List<RoleMembership> roleMembers = getRoleMembers(roleIds, qualification, true, foundRoleTypeMembers);
575 
576         return Collections.unmodifiableList(roleMembers);
577     }
578 
579     @Override
580     public Collection<String> getRoleMemberPrincipalIds(String namespaceCode, String roleName,
581             Map<String, String> qualification) throws RiceIllegalStateException {
582         incomingParamCheck(namespaceCode, "namespaceCode");
583         incomingParamCheck(roleName, "roleName");
584 
585         Set<String> principalIds = new HashSet<String>();
586         Set<String> foundRoleTypeMembers = new HashSet<String>();
587         List<String> roleIds = Collections.singletonList(getRoleIdByNamespaceCodeAndName(namespaceCode, roleName));
588         for (RoleMembership roleMembership : getRoleMembers(roleIds, qualification, false, foundRoleTypeMembers)) {
589             if (MemberType.GROUP.equals(roleMembership.getType())) {
590                 principalIds.addAll(getGroupService().getMemberPrincipalIds(roleMembership.getMemberId()));
591             } else {
592                 principalIds.add(roleMembership.getMemberId());
593             }
594         }
595 
596         return Collections.unmodifiableSet(principalIds);
597     }
598 
599     @Override
600     public boolean principalHasRole(String principalId, List<String> roleIds, Map<String, String> qualification)
601             throws RiceIllegalStateException {
602 
603         if (LOG.isDebugEnabled()) {
604             logPrincipalHasRoleCheck(principalId, roleIds, qualification);
605         }
606 
607         boolean hasRole = this.getProxiedRoleService().principalHasRole(principalId, roleIds, qualification, true);
608 
609         if (LOG.isDebugEnabled()) {
610             LOG.debug("Result: " + hasRole);
611         }
612 
613         return hasRole;
614     }
615 
616     @Override
617     public List<String> getPrincipalIdSubListWithRole(List<String> principalIds, String roleNamespaceCode,
618             String roleName, Map<String, String> qualification) throws RiceIllegalStateException {
619         incomingParamCheck(principalIds, "principalIds");
620         incomingParamCheck(roleNamespaceCode, "roleNamespaceCode");
621         incomingParamCheck(roleName, "roleName");
622 
623         List<String> subList = new ArrayList<String>();
624         RoleBoLite role = getRoleBoLiteByName(roleNamespaceCode, roleName);
625         for (String principalId : principalIds) {
626             if (this.getProxiedRoleService().principalHasRole(principalId, Collections.singletonList(role.getId()),
627                     qualification)) {
628                 subList.add(principalId);
629             }
630         }
631         return Collections.unmodifiableList(subList);
632     }
633 
634     @Override
635     public RoleQueryResults findRoles(QueryByCriteria queryByCriteria) throws RiceIllegalStateException {
636         incomingParamCheck(queryByCriteria, "queryByCriteria");
637 
638         QueryResults<RoleBoLite> results = getDataObjectService().findMatching(RoleBoLite.class, queryByCriteria);
639 
640         RoleQueryResults.Builder builder = RoleQueryResults.Builder.create();
641         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
642         builder.setTotalRowCount(results.getTotalRowCount());
643 
644         final List<Role.Builder> ims = new ArrayList<Role.Builder>();
645         for (RoleBoLite bo : results.getResults()) {
646             ims.add(Role.Builder.create(bo));
647         }
648 
649         builder.setResults(ims);
650         return builder.build();
651     }
652 
653     @Override
654     public List<RoleMembership> getFirstLevelRoleMembers(List<String> roleIds) throws RiceIllegalStateException {
655         incomingParamCheck(roleIds, "roleIds");
656         if (roleIds.isEmpty()) {
657             return Collections.emptyList();
658         }
659 
660         List<RoleMemberBo> roleMemberBoList = getStoredRoleMembersForRoleIds(roleIds, null, null);
661         List<RoleMembership> roleMemberships = new ArrayList<RoleMembership>();
662         for (RoleMemberBo roleMemberBo : roleMemberBoList) {
663             RoleMembership roleMembeship = RoleMembership.Builder.create(roleMemberBo.getRoleId(), roleMemberBo.getId(),
664                     roleMemberBo.getMemberId(), roleMemberBo.getType(), roleMemberBo.getAttributes(),
665                     roleMemberBo.getActiveFromDate(), roleMemberBo.getActiveToDate()).build();
666             roleMemberships.add(roleMembeship);
667         }
668         return Collections.unmodifiableList(roleMemberships);
669     }
670 
671     @Override
672     public RoleMembershipQueryResults findRoleMemberships(QueryByCriteria queryByCriteria)
673             throws RiceIllegalStateException {
674         incomingParamCheck(queryByCriteria, "queryByCriteria");
675 
676         QueryResults<RoleMemberBo> results = getDataObjectService().findMatching(RoleMemberBo.class,
677                 AttributeTransform.getInstance().apply(queryByCriteria));
678 
679         RoleMembershipQueryResults.Builder builder = RoleMembershipQueryResults.Builder.create();
680         builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
681         builder.setTotalRowCount(results.getTotalRowCount());
682 
683         final List<RoleMembership.Builder> ims = new ArrayList<RoleMembership.Builder>();
684         for (RoleMemberBo bo : results.getResults()) {
685             RoleMembership.Builder roleMembership = RoleMembership.Builder.create(bo.getRoleId(), bo.getId(),
686                     bo.getMemberId(), bo.getType(), bo.getAttributes(), bo.getActiveFromDate(), bo.getActiveToDate());
687             ims.add(roleMembership);
688         }
689 
690         builder.setResults(ims);
691         return builder.build();
692     }
693 
694     @Override
695     public List<DelegateMember> getDelegationMembersByDelegationId(String delegationId)
696             throws RiceIllegalStateException {
697         incomingParamCheck(delegationId, "delegationId");
698 
699         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
700         if (delegateBo == null) {
701             return Collections.emptyList();
702         }
703 
704         return getDelegateMembersForDelegation(delegateBo);
705     }
706 
707     @Override
708     public DelegateMember getDelegationMemberByDelegationAndMemberId(String delegationId, String memberId)
709             throws RiceIllegalStateException {
710         incomingParamCheck(delegationId, "delegationId");
711         incomingParamCheck(memberId, "memberId");
712 
713         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
714         DelegateMemberBo delegationMember = getKimDelegationMemberImplByDelegationAndId(delegationId, memberId);
715 
716         return getDelegateCompleteInfo(delegateBo, delegationMember);
717     }
718 
719     @Override
720     public DelegateMember getDelegationMemberById(String delegationMemberId) throws RiceIllegalStateException {
721         incomingParamCheck(delegationMemberId, "delegationMemberId");
722 
723         DelegateMemberBo delegateMemberBo = getDelegateMemberBo(delegationMemberId);
724         if (delegateMemberBo == null) {
725             return null;
726         }
727 
728         DelegateTypeBo delegateBo = getKimDelegationImpl(delegateMemberBo.getDelegationId());
729 
730         return getDelegateCompleteInfo(delegateBo, delegateMemberBo);
731     }
732 
733     @Override
734     public List<RoleResponsibility> getRoleResponsibilities(String roleId) throws RiceIllegalStateException {
735         incomingParamCheck(roleId, "roleId");
736 
737         List<RoleResponsibilityBo> roleResponsibilityBos = getDataObjectService().findMatching(
738                 RoleResponsibilityBo.class, QueryByCriteria.Builder.forAttribute(
739                 KimConstants.PrimaryKeyConstants.SUB_ROLE_ID, roleId).build()).getResults();
740         List<RoleResponsibility> roleResponsibilities = new ArrayList<RoleResponsibility>();
741 
742         for (RoleResponsibilityBo roleResponsibilityImpl : roleResponsibilityBos) {
743             roleResponsibilities.add(RoleResponsibilityBo.to(roleResponsibilityImpl));
744         }
745         return Collections.unmodifiableList(roleResponsibilities);
746     }
747 
748     @Override
749     public DelegateType getDelegateTypeByRoleIdAndDelegateTypeCode(String roleId, DelegationType delegationType)
750             throws RiceIllegalStateException {
751         incomingParamCheck(roleId, "roleId");
752         incomingParamCheck(delegationType, "delegationType");
753 
754         DelegateTypeBo delegateBo = getDelegationOfType(roleId, delegationType);
755         return DelegateTypeBo.to(delegateBo);
756     }
757 
758     @Override
759     public DelegateType getDelegateTypeByDelegationId(String delegationId) throws RiceIllegalStateException {
760         incomingParamCheck(delegationId, "delegationId");
761 
762         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
763         return DelegateTypeBo.to(delegateBo);
764     }
765 
766     protected List<RoleMembership> getRoleMembers(List<String> roleIds, Map<String, String> qualification,
767             boolean followDelegations, Set<String> foundRoleTypeMembers) {
768         List<RoleMembership> results = new ArrayList<RoleMembership>();
769         Set<String> allRoleIds = new HashSet<String>();
770         for (String roleId : roleIds) {
771             if (this.getProxiedRoleService().isRoleActive(roleId)) {
772                 allRoleIds.add(roleId);
773             }
774         }
775         // short-circuit if no roles match
776         if (allRoleIds.isEmpty()) {
777             return Collections.emptyList();
778         }
779         Set<String> matchingRoleIds = new HashSet<String>(allRoleIds.size());
780         // for efficiency, retrieve all roles and store in a map
781         Map<String, RoleBoLite> roles = getRoleBoLiteMap(allRoleIds);
782 
783         List<String> copyRoleIds = new ArrayList<String>(allRoleIds);
784         List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
785 
786         for (String roleId : allRoleIds) {
787             RoleTypeService roleTypeService = getRoleTypeService(roleId);
788             if (roleTypeService != null) {
789                 List<String> attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
790                 if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
791                     copyRoleIds.remove(roleId);
792                     rms.addAll(getStoredRoleMembersForRoleIds(Collections.singletonList(roleId), null,
793                             populateQualifiersForExactMatch(qualification, attributesForExactMatch)));
794                 }
795             }
796         }
797         if (CollectionUtils.isNotEmpty(copyRoleIds)) {
798             rms.addAll(getStoredRoleMembersForRoleIds(copyRoleIds, null, null));
799         }
800 
801         // build a map of role ID to membership information
802         // this will be used for later qualification checks
803         Map<String, List<RoleMembership>> roleIdToMembershipMap = new HashMap<String, List<RoleMembership>>();
804         for (RoleMemberBo roleMemberBo : rms) {
805             RoleMembership mi = RoleMembership.Builder.create(roleMemberBo.getRoleId(), roleMemberBo.getId(),
806                     roleMemberBo.getMemberId(), roleMemberBo.getType(), roleMemberBo.getAttributes(),
807                     roleMemberBo.getActiveFromDate(), roleMemberBo.getActiveToDate()).build();
808 
809             // if the qualification check does not need to be made, just add the result
810             if ((qualification == null || qualification.isEmpty())) {
811                 if (MemberType.ROLE.equals(roleMemberBo.getType())) {
812                     // if a role member type, do a non-recursive role member check
813                     // to obtain the group and principal members of that role
814                     // given the qualification
815                     Map<String, String> nestedRoleQualification = qualification;
816                     RoleTypeService roleTypeService = getRoleTypeService(roleMemberBo.getRoleId());
817                     if (roleTypeService != null) {
818                         // get the member role object
819                         RoleBoLite memberRole = getRoleBoLite(mi.getMemberId());
820                         nestedRoleQualification = getNestedQualification(memberRole, roles.get(roleMemberBo.getRoleId())
821                                 .getNamespaceCode(), roles.get(roleMemberBo.getRoleId()).getName(),
822                                 memberRole.getNamespaceCode(), memberRole.getName(), qualification,
823                                 roleMemberBo.getAttributes());
824                     }
825                     if (this.getProxiedRoleService().isRoleActive(roleMemberBo.getRoleId())) {
826                         Collection<RoleMembership> nestedRoleMembers = getNestedRoleMembers(nestedRoleQualification, mi,
827                                 foundRoleTypeMembers);
828                         if (!nestedRoleMembers.isEmpty()) {
829                             results.addAll(nestedRoleMembers);
830                             matchingRoleIds.add(roleMemberBo.getRoleId());
831                         }
832                     }
833                 } else { // not a role member type
834                     results.add(mi);
835                     matchingRoleIds.add(roleMemberBo.getRoleId());
836                 }
837                 matchingRoleIds.add(roleMemberBo.getRoleId());
838             } else {
839                 List<RoleMembership> lrmi = roleIdToMembershipMap.get(mi.getRoleId());
840                 if (lrmi == null) {
841                     lrmi = new ArrayList<RoleMembership>();
842                     roleIdToMembershipMap.put(mi.getRoleId(), lrmi);
843                 }
844                 lrmi.add(mi);
845             }
846         }
847         // if there is anything in the role to membership map, we need to check the role type services
848         // for those entries
849         if (!roleIdToMembershipMap.isEmpty()) {
850             // for each role, send in all the qualifiers for that role to the type service
851             // for evaluation, the service will return those which match
852             for (Map.Entry<String, List<RoleMembership>> entry : roleIdToMembershipMap.entrySet()) {
853                 //it is possible that the the roleTypeService is coming from a remote application
854                 // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
855                 try {
856                     RoleTypeService roleTypeService = getRoleTypeService(entry.getKey());
857                     List<RoleMembership> matchingMembers = roleTypeService.getMatchingRoleMemberships(qualification,
858                             entry.getValue());
859                     // loop over the matching entries, adding them to the results
860                     for (RoleMembership roleMemberships : matchingMembers) {
861                         if (MemberType.ROLE.equals(roleMemberships.getType())) {
862                             // if a role member type, do a non-recursive role member check
863                             // to obtain the group and principal members of that role
864                             // given the qualification
865                             // get the member role object
866                             RoleBoLite memberRole = getRoleBoLite(roleMemberships.getMemberId());
867                             if (memberRole.isActive()) {
868                                 Map<String, String> nestedRoleQualification = getNestedQualification(memberRole,
869                                         roles.get(roleMemberships.getRoleId()).getNamespaceCode(), roles.get(
870                                         roleMemberships.getRoleId()).getName(), memberRole.getNamespaceCode(),
871                                         memberRole.getName(), qualification, roleMemberships.getQualifier());
872                                 Collection<RoleMembership> nestedRoleMembers = getNestedRoleMembers(
873                                         nestedRoleQualification, roleMemberships, foundRoleTypeMembers);
874                                 if (!nestedRoleMembers.isEmpty()) {
875                                     results.addAll(nestedRoleMembers);
876                                     matchingRoleIds.add(roleMemberships.getRoleId());
877                                 }
878                             }
879                         } else { // not a role member
880                             results.add(roleMemberships);
881                             matchingRoleIds.add(roleMemberships.getRoleId());
882                         }
883                     }
884                 } catch (Exception ex) {
885                     LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + entry.getKey(),
886                             ex);
887                 }
888             }
889         }
890 
891         // handle derived roles
892         for (String roleId : allRoleIds) {
893             RoleTypeService roleTypeService = getRoleTypeService(roleId);
894             RoleBoLite role = roles.get(roleId);
895             // check if a derived role
896             try {
897                 if (isDerivedRoleType(roleTypeService)) {
898                     // for each derived role, get the list of principals and groups which are in that role given the qualification (per the role type service)
899                     List<RoleMembership> roleMembers = roleTypeService.getRoleMembersFromDerivedRole(
900                             role.getNamespaceCode(), role.getName(), qualification);
901                     if (!roleMembers.isEmpty()) {
902                         matchingRoleIds.add(roleId);
903                     }
904                     for (RoleMembership rm : roleMembers) {
905                         RoleMembership.Builder builder = RoleMembership.Builder.create(rm);
906                         builder.setRoleId(roleId);
907                         builder.setId("*");
908                         builder.setActiveFromDate(rm.getActiveFromDate());
909                         builder.setActiveToDate(rm.getActiveToDate());
910                         results.add(builder.build());
911                     }
912                 }
913             } catch (Exception ex) {
914                 LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleId, ex);
915             }
916         }
917 
918         if (followDelegations && !matchingRoleIds.isEmpty()) {
919             // we have a list of RoleMembershipInfo objects
920             // need to get delegations for distinct list of roles in that list
921             Map<String, DelegateTypeBo> delegationIdToDelegationMap = getStoredDelegationImplMapFromRoleIds(
922                     matchingRoleIds);
923             if (!delegationIdToDelegationMap.isEmpty()) {
924                 List<RoleMembership.Builder> membershipsWithDelegations = applyDelegationsToRoleMembers(results,
925                         delegationIdToDelegationMap.values(), qualification);
926                 resolveDelegationMemberRoles(membershipsWithDelegations, qualification, foundRoleTypeMembers);
927                 results = ModelObjectUtils.buildImmutableCopy(membershipsWithDelegations);
928             }
929         }
930 
931         // sort the results if a single role type service can be identified for
932         // all the matching role members
933         if (results.size() > 1) {
934             // if a single role: easy case
935             if (matchingRoleIds.size() == 1) {
936                 String roleId = matchingRoleIds.iterator().next();
937                 RoleTypeService roleTypeService = getRoleTypeService(roleId);
938                 //it is possible that the the roleTypeService is coming from a remote application
939                 // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
940                 try {
941                     if (roleTypeService != null) {
942                         results = roleTypeService.sortRoleMembers(results);
943                     }
944                 } catch (Exception ex) {
945                     LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleId, ex);
946                 }
947             } else if (matchingRoleIds.size() > 1) {
948                 // if more than one, check if there is only a single role type service
949                 String prevServiceName = null;
950                 boolean multipleServices = false;
951                 for (String roleId : matchingRoleIds) {
952                     String serviceName = KimApiServiceLocator.getKimTypeInfoService().getKimType(getRole(roleId)
953                             .getKimTypeId()).getServiceName();
954                     if (prevServiceName != null && !StringUtils.equals(prevServiceName, serviceName)) {
955                         multipleServices = true;
956                         break;
957                     }
958                     prevServiceName = serviceName;
959                 }
960                 if (!multipleServices) {
961                     String roleId = matchingRoleIds.iterator().next();
962                     //it is possible that the the roleTypeService is coming from a remote application
963                     // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
964                     try {
965                         RoleTypeService kimRoleTypeService = getRoleTypeService(roleId);
966                         if (kimRoleTypeService != null) {
967                             results = kimRoleTypeService.sortRoleMembers(results);
968                         }
969                     } catch (Exception ex) {
970                         LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleId, ex);
971                     }
972                 } else {
973                     LOG.warn("Did not sort role members - multiple role type services found.  Role Ids: "
974                             + matchingRoleIds);
975                 }
976             }
977         }
978         return Collections.unmodifiableList(results);
979     }
980 
981     /**
982      * Checks each of the result records to determine if there are potentially applicable delegation members for that
983      * role membership.  If there are, applicable delegations and members will be linked to the RoleMemberships in the
984      * given list.  An updated list will be returned from this method which includes the appropriate linked
985      * delegations.
986      */
987     protected List<RoleMembership.Builder> applyDelegationsToRoleMembers(List<RoleMembership> roleMemberships,
988             Collection<DelegateTypeBo> delegations, Map<String, String> qualification) {
989         MultiValueMap<String, String> roleIdToRoleMembershipIds = new LinkedMultiValueMap<String, String>();
990         /* Modified the roleMembershipIdToBuilders map to be multi-valued so
991          * role members that have multiple nested members (e.g. roles/groups)
992          * will properly resolve the delegations
993          */
994         MultiValueMap<String, RoleMembership.Builder> roleMembershipIdToBuilders =
995                 new LinkedMultiValueMap<String, RoleMembership.Builder>();
996         List<RoleMembership.Builder> roleMembershipBuilders = new ArrayList<RoleMembership.Builder>();
997         // to make our algorithm less painful, let's do some indexing and load the given list of RoleMemberships into
998         // builders
999         for (RoleMembership roleMembership : roleMemberships) {
1000             roleIdToRoleMembershipIds.add(roleMembership.getRoleId(), roleMembership.getId());
1001             RoleMembership.Builder builder = RoleMembership.Builder.create(roleMembership);
1002             builder.setActiveFromDate(roleMembership.getActiveFromDate());
1003             builder.setActiveToDate(roleMembership.getActiveToDate());
1004             roleMembershipBuilders.add(builder);
1005             roleMembershipIdToBuilders.add(roleMembership.getId(), builder);
1006         }
1007         for (DelegateTypeBo delegation : delegations) {
1008             // determine the candidate role memberships where this delegation can be mapped
1009             List<String> candidateRoleMembershipIds = roleIdToRoleMembershipIds.get(delegation.getRoleId());
1010             if (CollectionUtils.isNotEmpty(candidateRoleMembershipIds)) {
1011                 DelegationTypeService delegationTypeService = getDelegationTypeService(delegation.getDelegationId());
1012                 for (DelegateMemberBo delegationMember : delegation.getMembers()) {
1013                     // Make sure that the delegation member is active
1014                     if (delegationMember.isActive(DateTime.now()) && (delegationTypeService == null
1015                             || delegationTypeService.doesDelegationQualifierMatchQualification(qualification,
1016                             delegationMember.getQualifier()))) {
1017                         DelegateMember.Builder delegateMemberBuilder = DelegateMember.Builder.create(delegationMember);
1018                         // if the member has no role member id, check qualifications and apply to all matching role memberships on the role
1019                         if (StringUtils.isBlank(delegationMember.getRoleMemberId())) {
1020                             RoleTypeService roleTypeService = getRoleTypeService(delegation.getRoleId());
1021                             for (String roleMembershipId : candidateRoleMembershipIds) {
1022                                 for (RoleMembership.Builder roleMembershipBuilder : roleMembershipIdToBuilders.get(
1023                                         roleMembershipId)) {
1024                                     if (roleTypeService == null || roleTypeService.doesRoleQualifierMatchQualification(
1025                                             roleMembershipBuilder.getQualifier(), delegationMember.getQualifier())) {
1026                                         linkDelegateToRoleMembership(delegation, delegateMemberBuilder,
1027                                                 roleMembershipBuilder);
1028                                     }
1029                                 }
1030                             }
1031                         } else if (candidateRoleMembershipIds.contains(delegationMember.getRoleMemberId())) {
1032                             for (RoleMembership.Builder roleMembershipBuilder : roleMembershipIdToBuilders.get(
1033                                     delegationMember.getRoleMemberId())) {
1034                                 linkDelegateToRoleMembership(delegation, delegateMemberBuilder, roleMembershipBuilder);
1035                             }
1036                         }
1037                     }
1038                 }
1039             }
1040         }
1041         return roleMembershipBuilders;
1042     }
1043 
1044     protected void linkDelegateToRoleMembership(DelegateTypeBo delegation, DelegateMember.Builder delegateMemberBuilder,
1045             RoleMembership.Builder roleMembershipBuilder) {
1046         DelegateType.Builder delegateBuilder = null;
1047         for (DelegateType.Builder existingDelegateBuilder : roleMembershipBuilder.getDelegates()) {
1048             if (existingDelegateBuilder.getDelegationId().equals(delegation.getDelegationId())) {
1049                 delegateBuilder = existingDelegateBuilder;
1050             }
1051         }
1052         if (delegateBuilder == null) {
1053             delegateBuilder = DelegateType.Builder.create(delegation);
1054             delegateBuilder.setMembers(new ArrayList<DelegateMember.Builder>());
1055             roleMembershipBuilder.getDelegates().add(delegateBuilder);
1056         }
1057         delegateBuilder.getMembers().add(delegateMemberBuilder);
1058     }
1059 
1060     /**
1061      * Once the delegations for a RoleMembershipInfo object have been determined,
1062      * any "role" member types need to be resolved into groups and principals so that
1063      * further KIM requests are not needed.
1064      */
1065     protected void resolveDelegationMemberRoles(List<RoleMembership.Builder> membershipBuilders,
1066             Map<String, String> qualification, Set<String> foundRoleTypeMembers) {
1067         // check delegations assigned to this role
1068         for (RoleMembership.Builder roleMembership : membershipBuilders) {
1069             // the applicable delegation IDs will already be set in the RoleMembership.Builder
1070             // this code examines those delegations and obtains the member groups and principals
1071             for (DelegateType.Builder delegation : roleMembership.getDelegates()) {
1072                 List<DelegateMember.Builder> newMembers = new ArrayList<DelegateMember.Builder>();
1073                 for (DelegateMember.Builder member : delegation.getMembers()) {
1074                     if (MemberType.ROLE.equals(member.getType())) {
1075                         // loop over delegation roles and extract the role IDs where the qualifications match
1076                         Collection<RoleMembership> delegateMembers = getRoleMembers(Collections.singletonList(
1077                                 member.getMemberId()), qualification, false, foundRoleTypeMembers);
1078                         // loop over the role members and create the needed DelegationMember builders
1079                         for (RoleMembership rmi : delegateMembers) {
1080                             DelegateMember.Builder delegateMember = DelegateMember.Builder.create(member);
1081                             delegateMember.setMemberId(rmi.getMemberId());
1082                             delegateMember.setType(rmi.getType());
1083                             newMembers.add(delegateMember);
1084                         }
1085                     } else {
1086                         newMembers.add(member);
1087                     }
1088                 }
1089                 delegation.setMembers(newMembers);
1090             }
1091         }
1092     }
1093 
1094     @Override
1095     public boolean principalHasRole(String principalId, List<String> roleIds, Map<String, String> qualification,
1096             boolean checkDelegations) {
1097 
1098         incomingParamCheck(principalId, "principalId");
1099         incomingParamCheck(roleIds, "roleIds");
1100         return principalHasRole(new Context(principalId), principalId, roleIds, qualification, checkDelegations);
1101     }
1102 
1103     /**
1104      * An internal helper class which is used to keep context for an invocation of principalHasRole.
1105      */
1106     private final class Context {
1107 
1108         private String principalId;
1109         private List<String> principalGroupIds;
1110         private Map<String, RoleTypeService> roleTypeServiceCache;
1111         private Map<String, Boolean> isDerivedRoleTypeCache;
1112 
1113         Context(String principalId) {
1114             this.principalId = principalId;
1115             this.roleTypeServiceCache = new HashMap<String, RoleTypeService>();
1116             this.isDerivedRoleTypeCache = new HashMap<String, Boolean>();
1117         }
1118 
1119         String getPrincipalId() {
1120             return principalId;
1121         }
1122 
1123         List<String> getPrincipalGroupIds() {
1124             if (principalGroupIds == null) {
1125                 principalGroupIds = getGroupService().getGroupIdsByPrincipalId(principalId);
1126             }
1127             return principalGroupIds;
1128         }
1129 
1130         RoleTypeService getRoleTypeService(String kimTypeId) {
1131             if (roleTypeServiceCache.containsKey(kimTypeId)) {
1132                 return roleTypeServiceCache.get(kimTypeId);
1133             }
1134             RoleTypeService roleTypeService = null;
1135             if (kimTypeId != null) {
1136                 KimType roleType = KimApiServiceLocator.getKimTypeInfoService().getKimType(kimTypeId);
1137                 if (roleType != null && StringUtils.isNotBlank(roleType.getServiceName())) {
1138                     roleTypeService = getRoleTypeServiceByName(roleType.getServiceName());
1139                 }
1140             }
1141             if (roleTypeService == null) {
1142                 roleTypeService = KimImplServiceLocator.getDefaultRoleTypeService();
1143             }
1144             roleTypeServiceCache.put(kimTypeId, roleTypeService);
1145             return roleTypeService;
1146         }
1147 
1148         boolean isDerivedRoleType(String kimTypeId) {
1149             Boolean isDerived = isDerivedRoleTypeCache.get(kimTypeId);
1150             if (isDerived == null) {
1151                 isDerived = Boolean.valueOf(RoleServiceImpl.this.isDerivedRoleType(getRoleTypeService(kimTypeId)));
1152                 isDerivedRoleTypeCache.put(kimTypeId, isDerived);
1153             }
1154             return isDerived.booleanValue();
1155         }
1156     }
1157 
1158     /**
1159      * Uses a multi-phase approach to determining if the given principal of any of the roles given based
1160      * on the qualification map that is passed.
1161      */
1162     protected boolean principalHasRole(Context context, String principalId, List<String> roleIds,
1163             Map<String, String> qualification, boolean checkDelegations) {
1164         try {
1165             // Check the cache to find if it's already been determined that the principal is a member of any of
1166             // the roles with the given ids.
1167             List<String> roleIdsToCheck = new ArrayList<String>(roleIds.size());
1168             for (String roleId : roleIds) {
1169                 Boolean hasRole = getPrincipalHasRoleFromCache(principalId, roleId, qualification, checkDelegations);
1170                 if (hasRole != null) {
1171                     if (hasRole.booleanValue()) {
1172                         return true;
1173                     }
1174                 } else {
1175                     roleIdsToCheck.add(roleId);
1176                 }
1177             }
1178 
1179             // load the roles, this will also filter out inactive roles!
1180             List<Role> roles = loadRoles(roleIdsToCheck);
1181             // short-circuit if no roles match
1182             if (roles.isEmpty()) {
1183                 return false;
1184             }
1185 
1186             // sort putting derived roles last since they are most expensive to check
1187             LinkedList<Role> sortedRoles = new LinkedList<Role>();
1188             for (Role role : roles) {
1189                 boolean isDerivedRoleType = context.isDerivedRoleType(role.getKimTypeId());
1190 
1191                 if (isDerivedRoleType) {
1192                     sortedRoles.addLast(role);
1193                 } else {
1194                     sortedRoles.addFirst(role);
1195                 }
1196             }
1197 
1198             for (Role role : sortedRoles) {
1199                 boolean hasRole;
1200 
1201                 RoleTypeService roleTypeService = null;
1202                 try {
1203                     roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1204                 } catch (Exception ex) {
1205                     LOG.warn("Unable to find role type service with id: " + role.getKimTypeId(), ex);
1206                 }
1207 
1208                 boolean isDerivedRoleType = context.isDerivedRoleType(role.getKimTypeId());
1209                 if (isDerivedRoleType) {
1210                     hasRole = principalHasDerivedRole(context, principalId, role, qualification, roleTypeService);
1211                 } else {
1212                     hasRole = checkForMatchOnQualifications(context, principalId, role, qualification, roleTypeService);
1213 
1214                     if (!hasRole) {
1215                         hasRole = checkForMatchOnNestedRoles(context, principalId, role, qualification, roleTypeService);
1216                     }
1217                 }
1218 
1219                 if (!hasRole) {
1220                     if (checkDelegations && matchesOnDelegation(role.getId(), principalId,
1221                             context.getPrincipalGroupIds(), qualification, context)) {
1222                         hasRole = true;
1223                     }
1224                 }
1225 
1226                 // no match above, so principal doesn't have role
1227                 if (!isDerivedRoleType || (roleTypeService == null) || !roleTypeService
1228                         .dynamicRoleMembership(role.getNamespaceCode(), role.getName())) {
1229                     putPrincipalHasRoleInCache(hasRole, principalId, role.getId(), qualification, checkDelegations);
1230                 }
1231 
1232                 if (hasRole) {
1233                     return true;
1234                 }
1235             }
1236         } catch (Exception e) {
1237             LOG.warn("Caught exception during a principalHasRole check", e);
1238         }
1239 
1240         return false;
1241     }
1242 
1243     protected Boolean getPrincipalHasRoleFromCache(String principalId, String roleId, Map<String, String> qualification,
1244             boolean checkDelegations) {
1245         String key = buildPrincipalHasRoleCacheKey(principalId, roleId, qualification, checkDelegations);
1246         Cache.ValueWrapper value = cacheManager.getCache(Role.Cache.NAME).get(key);
1247         return value == null ? null : (Boolean) value.get();
1248     }
1249 
1250     protected boolean putPrincipalHasRoleInCache(boolean principalHasRole, String principalId, String roleId,
1251             Map<String, String> qualification, boolean checkDelegations) {
1252         String key = buildPrincipalHasRoleCacheKey(principalId, roleId, qualification, checkDelegations);
1253         cacheManager.getCache(Role.Cache.NAME).put(key, Boolean.valueOf(principalHasRole));
1254         return principalHasRole;
1255     }
1256 
1257     private String buildPrincipalHasRoleCacheKey(String principalId, String roleId, Map<String, String> qualification,
1258             boolean checkDelegations) {
1259         return new StringBuilder("{principalHasRole}").append("principalId=").append(principalId).append("|").append(
1260                 "roleId=").append(roleId).append("|").append("qualification=").append(CacheKeyUtils.mapKey(
1261                 qualification)).append("|").append("checkDelegations=").append(checkDelegations).toString();
1262     }
1263 
1264     /**
1265      * Check for derived roles and extract principals and groups from that, then check them against the
1266      * role type service passing in the qualification and principal.
1267      */
1268     protected boolean principalHasDerivedRole(Context context, String principalId, Role role,
1269             Map<String, String> qualification, RoleTypeService roleTypeService) {
1270         //it is possible that the the roleTypeService is coming from a remote application
1271         // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
1272         try {
1273             if ((roleTypeService != null) && roleTypeService.hasDerivedRole(principalId, context.getPrincipalGroupIds(),
1274                     role.getNamespaceCode(), role.getName(), qualification)) {
1275                 return true;
1276             }
1277         } catch (Exception ex) {
1278             LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + role.getId(), ex);
1279         }
1280 
1281         return false;
1282     }
1283 
1284     protected boolean checkForMatchOnQualifications(Context context, String principalId, Role role,
1285             Map<String, String> qualification, RoleTypeService roleTypeService) {
1286         Map<String, String> qualificationForExactMatch = getQualifiersForExactMatch(context, role, qualification);
1287         if (qualificationForExactMatch != null) {
1288             // Perform exact database-level matching. This can be done for all roles if the given qualification map
1289             // is null or empty since that means qualification matching does not need to be performed. It can also
1290             // be done for roles who's RoleTypeService defines qualifiers for exact match.
1291             List<RoleMemberBo> matchingRoleMembers = getStoredRolePrincipalsForPrincipalIdAndRoleIds(
1292                     Collections.singletonList(role.getId()), principalId, qualificationForExactMatch);
1293 
1294             // if a role member matched our principal, we're good to go
1295             if (CollectionUtils.isNotEmpty(matchingRoleMembers)) {
1296                 return true;
1297             }
1298 
1299             // now check groups
1300             if (!context.getPrincipalGroupIds().isEmpty()) {
1301                 List<RoleMemberBo> matchingRoleGroupMembers = getStoredRoleGroupsUsingExactMatchOnQualification(
1302                         context.getPrincipalGroupIds(), role.getId(), qualification);
1303                 if (CollectionUtils.isNotEmpty(matchingRoleGroupMembers)) {
1304                     return true;
1305                 }
1306             }
1307         } else {
1308             // Use RoleTypeService matching for roles which have not already been checked. Will need to determine
1309             // which role memberships match the given principal then delegate to the appropriate RoleTypeService
1310             // to execute matching logic.
1311             List<RoleMemberBo> matchingPrincipalRoleMembers = getRoleMembersForPrincipalId(Collections.singletonList(
1312                     role.getId()), principalId);
1313             List<RoleMemberBo> matchingGroupRoleMembers = getRoleMembersForGroupIds(role.getId(),
1314                     context.getPrincipalGroupIds());
1315             List<RoleMembership> roleMemberships = convertToRoleMemberships(matchingPrincipalRoleMembers,
1316                     matchingGroupRoleMembers);
1317             try {
1318                 if ((roleTypeService != null) && !roleMemberships.isEmpty() && !roleTypeService
1319                         .getMatchingRoleMemberships(qualification, roleMemberships).isEmpty()) {
1320                     return true;
1321                 }
1322             } catch (Exception ex) {
1323                 LOG.warn("Unable to find role type service with id: " + role.getKimTypeId());
1324             }
1325         }
1326 
1327         return false;
1328     }
1329 
1330     protected Map<String, String> getQualifiersForExactMatch(Context context, Role role,
1331             Map<String, String> qualification) {
1332         Map<String, String> qualificationForExactMatch = null;
1333 
1334         if (qualification == null || qualification.isEmpty()) {
1335             qualificationForExactMatch = new HashMap<String, String>();
1336         } else {
1337             RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1338             if (roleTypeService == null) {
1339                 return null;
1340             }
1341 
1342             List<String> attributesForExactMatch = getQualifiersForExactMatchFromRoleType(role.getKimTypeId(),
1343                     roleTypeService);
1344             if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
1345                 qualificationForExactMatch = populateQualifiersForExactMatch(qualification, attributesForExactMatch);
1346                 if (qualificationForExactMatch.isEmpty()) {
1347                     qualificationForExactMatch = null;
1348                 }
1349             }
1350         }
1351 
1352         return qualificationForExactMatch;
1353     }
1354 
1355     protected boolean checkForMatchOnNestedRoles(Context context, String principalId, Role role,
1356             Map<String, String> qualification, RoleTypeService roleTypeService) {
1357         Map<String, Role> roleIndex = new HashMap<String, Role>();
1358         roleIndex.put(role.getId(), role);
1359 
1360         List<RoleMemberBo> roleMemberBos = getStoredRoleMembersForRoleIds(new ArrayList<String>(roleIndex.keySet()),
1361                 MemberType.ROLE.getCode(), null);
1362         for (RoleMemberBo roleMemberBo : roleMemberBos) {
1363             try {
1364                 if (roleTypeService != null) {
1365                     if (roleTypeService.doesRoleQualifierMatchQualification(qualification,
1366                             roleMemberBo.getAttributes())) {
1367                         RoleBoLite memberRole = getRoleBoLite(roleMemberBo.getMemberId());
1368                         Map<String, String> nestedRoleQualification = getNestedQualification(memberRole,
1369                                 role.getNamespaceCode(), role.getName(), memberRole.getNamespaceCode(),
1370                                 memberRole.getName(), qualification, roleMemberBo.getAttributes());
1371 
1372                         if (principalHasRole(context, principalId, Collections.singletonList(
1373                                 roleMemberBo.getMemberId()), nestedRoleQualification, true)) {
1374                             return true;
1375                         }
1376                     }
1377                 } else {
1378                     // no qualifiers - role is always used - check membership
1379                     // no role type service, so can't convert qualification - just pass as is
1380                     if (principalHasRole(context, principalId, Collections.singletonList(roleMemberBo.getMemberId()),
1381                             qualification, true)) {
1382                         return true;
1383                     }
1384                 }
1385             } catch (Exception ex) {
1386                 LOG.warn("Not able to retrieve RoleTypeService from remote system for role Id: " + roleMemberBo
1387                         .getRoleId(), ex);
1388             }
1389         }
1390 
1391         return false;
1392     }
1393 
1394     protected List<String> getQualifiersForExactMatchFromRoleType(String kimTypeId, RoleTypeService roleTypeService) {
1395         String cacheKey = "{getQualifiersForExactMatch}kimTypeId=" + kimTypeId;
1396         Cache cache = cacheManager.getCache(Role.Cache.NAME);
1397         Cache.ValueWrapper value = cache.get(cacheKey);
1398         List<String> qualifiers = new ArrayList<String>();
1399         if (value == null) {
1400             try {
1401                 qualifiers = roleTypeService.getQualifiersForExactMatch();
1402                 cache.put(cacheKey, qualifiers);
1403             } catch (Exception e) {
1404                 LOG.warn("Caught exception when attempting to invoke a role type service", e);
1405             }
1406         } else {
1407             qualifiers = (List<String>) value.get();
1408         }
1409         return qualifiers;
1410     }
1411 
1412     public boolean isDerivedRoleType(RoleTypeService service) {
1413         return service != null && service.isDerivedRoleType();
1414     }
1415 
1416     private boolean dynamicRoleMembership(RoleTypeService service, Role role) {
1417         return service != null && role != null && service.dynamicRoleMembership(role.getNamespaceCode(),
1418                 role.getName());
1419     }
1420 
1421     @Override
1422     public boolean isDerivedRole(String roleId) {
1423         incomingParamCheck(roleId, "roleId");
1424         RoleTypeService service = getRoleTypeService(roleId);
1425         return isDerivedRoleType(service);
1426     }
1427 
1428     @Override
1429     public boolean isDynamicRoleMembership(String roleId) {
1430         incomingParamCheck(roleId, "roleId");
1431         RoleTypeService service = getRoleTypeService(roleId);
1432         try {
1433             return dynamicRoleMembership(service, getRole(roleId));
1434         } catch (Exception e) {
1435             LOG.warn("Caught exception while invoking a role type service for role " + roleId, e);
1436             // Returning true so the role won't be cached
1437             return true;
1438         }
1439     }
1440 
1441     /**
1442      * Support method for principalHasRole.  Checks delegations on the passed in roles for the given principal and
1443      * groups.  (It's assumed that the principal
1444      * belongs to the given groups.)
1445      * <p/>
1446      * Delegation checks are mostly the same as role checks except that the delegateBo itself is qualified against the
1447      * original role (like a RolePrincipal
1448      * or RoleGroup.)  And then, the members of that delegateBo may have additional qualifiers which are not part of the
1449      * original role qualifiers.
1450      * <p/>
1451      * For example:
1452      * <p/>
1453      * A role could be qualified by organization.  So, there is a person in the organization with primary authority for
1454      * that org.  But, then they delegate authority
1455      * for that organization (not their authority - the delegateBo is attached to the org.)  So, in this case the
1456      * delegateBo has a qualifier of the organization
1457      * when it is attached to the role.
1458      * <p/>
1459      * The principals then attached to that delegateBo (which is specific to the organization), may have additional
1460      * qualifiers.
1461      * For Example: dollar amount range, effective dates, document types.
1462      * As a subsequent step, those qualifiers are checked against the qualification passed in from the client.
1463      */
1464     protected boolean matchesOnDelegation(String roleId, String principalId, List<String> principalGroupIds,
1465             Map<String, String> qualification, Context context) {
1466         Set<String> allRoleIds = new HashSet<String>();
1467         allRoleIds.add(roleId);
1468 
1469         // get the list of delegations for the roles
1470         Map<String, DelegateTypeBo> delegations = getStoredDelegationImplMapFromRoleIds(allRoleIds);
1471 
1472         // If there are no delegations then we should cache that the principal
1473         // doesn't have the given roles if those roles do not have dynamic
1474         // membership
1475         if (delegations.isEmpty()) {
1476             return false;
1477         }
1478 
1479         // Build a map from a role ID to the delegations for that role ID
1480         Map<String, List<DelegateTypeBo>> roleToDelegations = new HashMap<String, List<DelegateTypeBo>>();
1481         for (DelegateTypeBo delegation : delegations.values()) {
1482             List<DelegateTypeBo> roleDelegations = roleToDelegations.get(delegation.getRoleId());
1483             if (roleDelegations == null) {
1484                 roleDelegations = new ArrayList<DelegateTypeBo>();
1485                 roleToDelegations.put(delegation.getRoleId(), roleDelegations);
1486             }
1487             roleDelegations.add(delegation);
1488         }
1489 
1490         // Iterate through each role and check its delegations to determine if
1491         // the principal has one of the roles
1492         for (String delegationRoleId : roleToDelegations.keySet()) {
1493             boolean matchesOnRoleDelegation = false;
1494             Role role = getRole(delegationRoleId);
1495             RoleTypeService roleTypeService = context.getRoleTypeService(role.getKimTypeId());
1496 
1497             // Iterate through each delegation for this role and determine if
1498             // the principal has the role through this delegation
1499             for (DelegateTypeBo delegation : roleToDelegations.get(delegationRoleId)) {
1500                 // If the delegation isn't active skip it
1501                 if (!delegation.isActive()) {
1502                     continue;
1503                 }
1504 
1505                 // Now iterate through all of the members of the delegation to
1506                 // determine if any of them apply to this principal
1507                 for (DelegateMemberBo delegateMemberBo : delegation.getMembers()) {
1508                     // If the membership isn't active skip the rest of the checks
1509                     if (!delegateMemberBo.isActive(new Timestamp(new Date().getTime()))) {
1510                         continue;
1511                     }
1512 
1513                     // If the membership is a principal type then check the
1514                     // delegate's member ID against the principal ID
1515                     if (MemberType.PRINCIPAL.equals(delegateMemberBo.getType()) && !delegateMemberBo.getMemberId()
1516                             .equals(principalId)) {
1517                         continue; // no match on principal
1518                     }
1519 
1520                     // If the membership is a group type then check to see if
1521                     // the group's ID is contained in the list of groups the
1522                     // principal belongs to
1523                     if (MemberType.GROUP.equals(delegateMemberBo.getType()) && !principalGroupIds.contains(
1524                             delegateMemberBo.getMemberId())) {
1525                         continue; // No match on group
1526                     }
1527 
1528                     // If the membership is a role type then we need to recurse
1529                     // into the principalHasRole method to check if this
1530                     // principal is a member of that role
1531                     if (MemberType.ROLE.equals(delegateMemberBo.getType()) && !principalHasRole(principalId,
1532                             Collections.singletonList(delegateMemberBo.getMemberId()), qualification, false)) {
1533                         continue; // No match on role
1534                     }
1535 
1536                     // OK, the member matches the current user, now check the qualifications
1537 
1538                     // NOTE: this compare is slightly different than the member enumeration
1539                     // since the requested qualifier is always being used rather than
1540                     // the role qualifier for the member (which is not available)
1541 
1542                     //it is possible that the the roleTypeService is coming from a remote application
1543                     // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
1544                     try {
1545                         if (roleTypeService != null && !roleTypeService.doesRoleQualifierMatchQualification(
1546                                 qualification, delegateMemberBo.getQualifier())) {
1547                             continue; // no match - skip to next record
1548                         }
1549                     } catch (Exception ex) {
1550                         LOG.warn("Unable to call doesRoleQualifierMatchQualification on role type service for role Id: "
1551                                 + delegation.getRoleId() + " / " + qualification + " / " + delegateMemberBo
1552                                 .getQualifier(), ex);
1553                         continue;
1554                     }
1555 
1556                     // role service matches this qualifier
1557                     // now try the delegateBo service
1558                     DelegationTypeService delegationTypeService = getDelegationTypeService(
1559                             delegateMemberBo.getDelegationId());
1560                     // QUESTION: does the qualifier map need to be merged with the main delegateBo qualification?
1561                     if (delegationTypeService != null && !delegationTypeService
1562                             .doesDelegationQualifierMatchQualification(qualification,
1563                                     delegateMemberBo.getQualifier())) {
1564                         continue; // no match - skip to next record
1565                     }
1566 
1567                     // check if a role member ID is present on the delegateBo record
1568                     // if so, check that the original role member would match the given qualifiers
1569                     if (StringUtils.isNotBlank(delegateMemberBo.getRoleMemberId())) {
1570                         RoleMemberBo rm = getRoleMemberBo(delegateMemberBo.getRoleMemberId());
1571                         if (rm != null) {
1572                             // check that the original role member's is active and that their
1573                             // qualifier would have matched this request's
1574                             // qualifications (that the original person would have the permission/responsibility
1575                             // for an action)
1576                             // this prevents a role-membership based delegateBo from surviving the inactivation/
1577                             // changing of the main person's role membership
1578                             if (!rm.isActive(new Timestamp(new Date().getTime()))) {
1579                                 continue;
1580                             }
1581 
1582                             Map<String, String> roleQualifier = rm.getAttributes();
1583                             //it is possible that the the roleTypeService is coming from a remote application
1584                             // and therefore it can't be guaranteed that it is up and working, so using a try/catch to catch this possibility.
1585                             try {
1586                                 if (roleTypeService != null && !roleTypeService.doesRoleQualifierMatchQualification(
1587                                         qualification, roleQualifier)) {
1588                                     continue;
1589                                 }
1590                             } catch (Exception ex) {
1591                                 LOG.warn(
1592                                         "Unable to call doesRoleQualifierMatchQualification on role type service for role Id: "
1593                                                 + delegation.getRoleId() + " / " + qualification + " / "
1594                                                 + roleQualifier, ex);
1595                                 continue;
1596                             }
1597                         } else {
1598                             LOG.warn("Unknown role member ID cited in the delegateBo member table:");
1599                             LOG.warn("       assignedToId: " + delegateMemberBo.getDelegationMemberId()
1600                                     + " / roleMemberId: " + delegateMemberBo.getRoleMemberId());
1601                         }
1602                     }
1603 
1604                     // If we've made it here then all of the tests pass so the
1605                     // principal must belong to this delegation so set the flag
1606                     // to true and break out of this loop
1607                     matchesOnRoleDelegation = true;
1608                     break;
1609                 }
1610 
1611                 // If we've found a match for one of the delegations break out
1612                 // of this loop
1613                 if (matchesOnRoleDelegation) {
1614                     break;
1615                 }
1616             }
1617 
1618             // If we've found a matching delegation skip processing the rest of
1619             // the roles
1620             if (matchesOnRoleDelegation) {
1621                 return matchesOnRoleDelegation;
1622             }
1623         }
1624 
1625         // If we get here we didn't find a matching delegation so return false
1626         return false;
1627     }
1628 
1629     protected List<RoleMembership> convertToRoleMemberships(List<RoleMemberBo>... roleMemberLists) {
1630         List<RoleMembership> roleMemberships = new ArrayList<RoleMembership>();
1631         for (List<RoleMemberBo> roleMembers : roleMemberLists) {
1632             for (RoleMemberBo roleMember : roleMembers) {
1633                 RoleMembership roleMembership = RoleMembership.Builder.create(roleMember.getRoleId(),
1634                         roleMember.getId(), roleMember.getMemberId(), roleMember.getType(), roleMember.getAttributes(),
1635                         roleMember.getActiveFromDate(), roleMember.getActiveToDate()).build();
1636                 roleMemberships.add(roleMembership);
1637             }
1638         }
1639         return roleMemberships;
1640     }
1641 
1642     /**
1643      * Helper method used by principalHasRole to build the role ID -> list of members map.
1644      *
1645      * @return <b>true</b> if no further checks are needed because no role service is defined
1646      */
1647     protected boolean getRoleIdToMembershipMap(Map<String, List<RoleMembership>> roleIdToMembershipMap,
1648             List<RoleMemberBo> roleMembers) {
1649         for (RoleMemberBo roleMemberBo : roleMembers) {
1650             RoleMembership roleMembership = RoleMembership.Builder.create(roleMemberBo.getRoleId(),
1651                     roleMemberBo.getId(), roleMemberBo.getMemberId(), roleMemberBo.getType(),
1652                     roleMemberBo.getAttributes(), roleMemberBo.getActiveFromDate(), roleMemberBo.getActiveToDate())
1653                     .build();
1654 
1655             // if the role type service is null, assume that all qualifiers match
1656             if (getRoleTypeService(roleMemberBo.getRoleId()) == null) {
1657                 return true;
1658             }
1659             List<RoleMembership> lrmi = roleIdToMembershipMap.get(roleMembership.getRoleId());
1660             if (lrmi == null) {
1661                 lrmi = new ArrayList<RoleMembership>();
1662                 roleIdToMembershipMap.put(roleMembership.getRoleId(), lrmi);
1663             }
1664             lrmi.add(roleMembership);
1665         }
1666         return false;
1667     }
1668 
1669     /**
1670      * Retrieves a KimDelegationImpl object by its ID. If the delegateBo already exists in the cache, this method will
1671      * return the cached
1672      * version; otherwise, it will retrieve the uncached version from the database and then cache it before returning
1673      * it.
1674      */
1675     protected DelegateTypeBo getKimDelegationImpl(String delegationId) {
1676         if (StringUtils.isBlank(delegationId)) {
1677             return null;
1678         }
1679 
1680         return getDataObjectService().find(DelegateTypeBo.class, delegationId);
1681     }
1682 
1683     protected DelegationTypeService getDelegationTypeService(String delegationId) {
1684         DelegationTypeService service = null;
1685         DelegateTypeBo delegateBo = getKimDelegationImpl(delegationId);
1686         KimType delegationType = KimApiServiceLocator.getKimTypeInfoService().getKimType(delegateBo.getKimTypeId());
1687         if (delegationType != null) {
1688             KimTypeService tempService = KimFrameworkServiceLocator.getKimTypeService(delegationType);
1689             if (tempService != null && tempService instanceof DelegationTypeService) {
1690                 service = (DelegationTypeService) tempService;
1691             } else {
1692                 LOG.error("Service returned for type " + delegationType + "(" + delegationType.getName()
1693                         + ") was not a DelegationTypeService.  Was a " + (tempService != null ? tempService.getClass() :
1694                         "(null)"));
1695             }
1696         } else { // delegateBo has no type - default to role type if possible
1697             RoleTypeService roleTypeService = getRoleTypeService(delegateBo.getRoleId());
1698             if (roleTypeService != null && roleTypeService instanceof DelegationTypeService) {
1699                 service = (DelegationTypeService) roleTypeService;
1700             }
1701         }
1702         return service;
1703     }
1704 
1705     protected Collection<RoleMembership> getNestedRoleMembers(Map<String, String> qualification, RoleMembership rm,
1706             Set<String> foundRoleTypeMembers) {
1707         // If this role has already been traversed, skip it
1708         if (foundRoleTypeMembers.contains(rm.getMemberId())) {
1709             return new ArrayList<RoleMembership>();  // return an empty list
1710         }
1711         foundRoleTypeMembers.add(rm.getMemberId());
1712 
1713         ArrayList<String> roleIdList = new ArrayList<String>(1);
1714         roleIdList.add(rm.getMemberId());
1715 
1716         // get the list of members from the nested role - ignore delegations on those sub-roles
1717         Collection<RoleMembership> currentNestedRoleMembers = getRoleMembers(roleIdList, qualification, false,
1718                 foundRoleTypeMembers);
1719 
1720         // add the roles whose members matched to the list for delegateBo checks later
1721         Collection<RoleMembership> returnRoleMembers = new ArrayList<RoleMembership>();
1722         for (RoleMembership roleMembership : currentNestedRoleMembers) {
1723             RoleMembership.Builder rmBuilder = RoleMembership.Builder.create(roleMembership);
1724 
1725             // use the member ID of the parent role (needed for responsibility joining)
1726             rmBuilder.setId(rm.getId());
1727             // store the role ID, so we know where this member actually came from
1728             rmBuilder.setRoleId(rm.getRoleId());
1729             rmBuilder.setEmbeddedRoleId(rm.getMemberId());
1730             rmBuilder.setActiveFromDate(rm.getActiveFromDate());
1731             rmBuilder.setActiveToDate(rm.getActiveToDate());
1732             returnRoleMembers.add(rmBuilder.build());
1733         }
1734         return returnRoleMembers;
1735     }
1736 
1737     /**
1738      * Retrieves a KimDelegationMemberImpl object by its ID and the ID of the delegation it belongs to. If the
1739      * delegation member exists in the cache,
1740      * this method will return the cached one; otherwise, it will retrieve the uncached version from the database and
1741      * then cache it before returning it.
1742      */
1743     protected DelegateMemberBo getKimDelegationMemberImplByDelegationAndId(String delegationId,
1744             String delegationMemberId) {
1745         if (StringUtils.isBlank(delegationId) || StringUtils.isBlank(delegationMemberId)) {
1746             return null;
1747         }
1748 
1749         Map<String, String> searchCriteria = new HashMap<String, String>(2);
1750         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_ID, delegationId);
1751         searchCriteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_MEMBER_ID, delegationMemberId);
1752         QueryResults<DelegateMemberBo> memberList = getDataObjectService().findMatching(DelegateMemberBo.class,
1753                 QueryByCriteria.Builder.andAttributes(searchCriteria).build());
1754         if (!memberList.getResults().isEmpty()) {
1755             return memberList.getResults().get(0);
1756         }
1757         return null;
1758     }
1759 
1760     private List<RoleMemberBo> getStoredRoleMembersUsingExactMatchOnQualification(String principalId,
1761             List<String> groupIds, List<String> roleIds, Map<String, String> qualification) {
1762         List<String> copyRoleIds = new ArrayList<String>(roleIds);
1763         List<RoleMemberBo> roleMemberBoList = new ArrayList<RoleMemberBo>();
1764 
1765         for (String roleId : roleIds) {
1766             RoleTypeService roleTypeService = getRoleTypeService(roleId);
1767             if (roleTypeService != null) {
1768                 List<String> attributesForExactMatch = null;
1769                 try {
1770                     attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
1771                 } catch (Exception e) {
1772                     LOG.warn("Caught exception when attempting to invoke a role type service for role " + roleId, e);
1773                 }
1774                 if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
1775                     copyRoleIds.remove(roleId);
1776                     roleMemberBoList.addAll(getStoredRoleMembersForRoleIdsWithFilters(Collections.singletonList(roleId),
1777                             principalId, groupIds, populateQualifiersForExactMatch(qualification,
1778                             attributesForExactMatch)));
1779                 }
1780             }
1781         }
1782         if (CollectionUtils.isNotEmpty(copyRoleIds)) {
1783             roleMemberBoList.addAll(getStoredRoleMembersForRoleIdsWithFilters(copyRoleIds, principalId, groupIds,
1784                     null));
1785         }
1786         return roleMemberBoList;
1787     }
1788 
1789     private List<RoleMemberBo> getStoredRoleGroupsUsingExactMatchOnQualification(List<String> groupIds, String roleId,
1790             Map<String, String> qualification) {
1791         Set<String> roleIds = new HashSet<String>();
1792         if (roleId != null) {
1793             roleIds.add(roleId);
1794         }
1795         return getStoredRoleGroupsUsingExactMatchOnQualification(groupIds, roleIds, qualification);
1796     }
1797 
1798     private List<RoleMemberBo> getStoredRoleGroupsUsingExactMatchOnQualification(List<String> groupIds,
1799             Set<String> roleIds, Map<String, String> qualification) {
1800         List<String> copyRoleIds = new ArrayList<String>(roleIds);
1801         List<RoleMemberBo> roleMemberBos = new ArrayList<RoleMemberBo>();
1802 
1803         for (String roleId : roleIds) {
1804             RoleTypeService roleTypeService = getRoleTypeService(roleId);
1805             if (roleTypeService != null) {
1806                 List<String> attributesForExactMatch = null;
1807                 try {
1808                     attributesForExactMatch = roleTypeService.getQualifiersForExactMatch();
1809                 } catch (Exception e) {
1810                     LOG.warn("Caught exception when attempting to invoke a role type service for role " + roleId, e);
1811                 }
1812                 if (CollectionUtils.isNotEmpty(attributesForExactMatch)) {
1813                     copyRoleIds.remove(roleId);
1814                     roleMemberBos.addAll(getStoredRoleGroupsForGroupIdsAndRoleIds(Collections.singletonList(roleId),
1815                             groupIds, populateQualifiersForExactMatch(qualification, attributesForExactMatch)));
1816                 }
1817             }
1818         }
1819         if (CollectionUtils.isNotEmpty(copyRoleIds)) {
1820             roleMemberBos.addAll(getStoredRoleGroupsForGroupIdsAndRoleIds(copyRoleIds, groupIds, null));
1821         }
1822         return roleMemberBos;
1823     }
1824 
1825     private List<DelegateMember> getDelegateMembersForDelegation(DelegateTypeBo delegateBo) {
1826         if (delegateBo == null || delegateBo.getMembers() == null) {
1827             return null;
1828         }
1829         List<DelegateMember> delegateMembersReturnList = new ArrayList<DelegateMember>();
1830         for (DelegateMemberBo delegateMemberBo : delegateBo.getMembers()) {
1831             //FIXME: What is up with this!?!
1832             DelegateMember delegateMember = getDelegateCompleteInfo(delegateBo, delegateMemberBo);
1833 
1834             delegateMembersReturnList.add(DelegateMemberBo.to(delegateMemberBo));
1835         }
1836         return Collections.unmodifiableList(delegateMembersReturnList);
1837     }
1838 
1839     private DelegateMember getDelegateCompleteInfo(DelegateTypeBo delegateBo, DelegateMemberBo delegateMemberBo) {
1840         if (delegateBo == null || delegateMemberBo == null) {
1841             return null;
1842         }
1843 
1844         DelegateMember.Builder delegateMemberBuilder = DelegateMember.Builder.create(delegateMemberBo);
1845         delegateMemberBuilder.setType(delegateMemberBo.getType());
1846         return delegateMemberBuilder.build();
1847     }
1848 
1849     @Override
1850     public RoleMember assignPrincipalToRole(String principalId, String namespaceCode, String roleName,
1851             Map<String, String> qualifier) throws RiceIllegalArgumentException {
1852         incomingParamCheck(principalId, "principalId");
1853         incomingParamCheck(namespaceCode, "namespaceCode");
1854         incomingParamCheck(roleName, "roleName");
1855         incomingParamCheck(qualifier, "qualifier");
1856 
1857         // look up the role
1858         RoleBoLite role = getRoleBoLiteByName(namespaceCode, roleName);
1859 
1860         // check that identical member does not already exist
1861         List<RoleMember> membersMatchByExactQualifiers = doAnyMemberRecordsMatchByExactQualifier(role, principalId,
1862                 memberTypeToRoleDaoActionMap.get(MemberType.PRINCIPAL.getCode()), qualifier);
1863         if (CollectionUtils.isNotEmpty(membersMatchByExactQualifiers)) {
1864             return membersMatchByExactQualifiers.get(0);
1865         }
1866         List<String> roleIds = new ArrayList<String>();
1867         roleIds.add(role.getId());
1868         List<RoleMemberBo> roleMembers = getRoleDao().getRoleMembersForRoleIds(roleIds, MemberType.PRINCIPAL.getCode(),
1869                 qualifier);
1870         RoleMember anyMemberMatch = doAnyMemberRecordsMatch(roleMembers, principalId, MemberType.PRINCIPAL.getCode(),
1871                 qualifier);
1872         if (null != anyMemberMatch) {
1873             return anyMemberMatch;
1874         }
1875 
1876         // create the new role member object
1877         RoleMemberBo newRoleMember = new RoleMemberBo();
1878 
1879         newRoleMember.setRoleId(role.getId());
1880         newRoleMember.setMemberId(principalId);
1881         newRoleMember.setType(MemberType.PRINCIPAL);
1882 
1883         // build role member attribute objects from the given Map<String, String>
1884         addMemberAttributeData(newRoleMember, qualifier, role.getKimTypeId());
1885 
1886         // add row to member table
1887         // When members are added to roles, clients must be notified.
1888         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(newRoleMember));
1889     }
1890 
1891     @Override
1892     public RoleMember assignGroupToRole(String groupId, String namespaceCode, String roleName,
1893             Map<String, String> qualifier) throws RiceIllegalStateException {
1894         incomingParamCheck(groupId, "groupId");
1895         incomingParamCheck(namespaceCode, "namespaceCode");
1896         incomingParamCheck(roleName, "roleName");
1897         incomingParamCheck(qualifier, "qualifier");
1898 
1899         // look up the role
1900         RoleBo role = getRoleBoByName(namespaceCode, roleName);
1901 
1902         // check that identical member does not already exist
1903         List<RoleMember> membersMatchByExactQualifiers = doAnyMemberRecordsMatchByExactQualifier(role, groupId,
1904                 memberTypeToRoleDaoActionMap.get(MemberType.GROUP.getCode()), qualifier);
1905         if (CollectionUtils.isNotEmpty(membersMatchByExactQualifiers)) {
1906             return membersMatchByExactQualifiers.get(0);
1907         }
1908         RoleMember anyMemberMatch = doAnyMemberRecordsMatch(role.getMembers(), groupId, MemberType.GROUP.getCode(),
1909                 qualifier);
1910         if (null != anyMemberMatch) {
1911             return anyMemberMatch;
1912         }
1913 
1914         // create the new role member object
1915         RoleMemberBo newRoleMember = new RoleMemberBo();
1916         newRoleMember.setRoleId(role.getId());
1917         newRoleMember.setMemberId(groupId);
1918         newRoleMember.setType(MemberType.GROUP);
1919 
1920         // build role member attribute objects from the given Map<String, String>
1921         addMemberAttributeData(newRoleMember, qualifier, role.getKimTypeId());
1922 
1923         // When members are added to roles, clients must be notified.
1924         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(newRoleMember));
1925     }
1926 
1927     @Override
1928     public RoleMember assignRoleToRole(String roleId, String namespaceCode, String roleName,
1929             Map<String, String> qualifier) throws RiceIllegalStateException {
1930         incomingParamCheck(roleId, "roleId");
1931         incomingParamCheck(namespaceCode, "namespaceCode");
1932         incomingParamCheck(roleName, "roleName");
1933         incomingParamCheck(qualifier, "qualifier");
1934 
1935         // look up the roleBo
1936         RoleBo roleBo = getRoleBoByName(namespaceCode, roleName);
1937 
1938         // check that identical member does not already exist
1939         List<RoleMember> membersMatchByExactQualifiers = doAnyMemberRecordsMatchByExactQualifier(roleBo, roleId,
1940                 memberTypeToRoleDaoActionMap.get(MemberType.ROLE.getCode()), qualifier);
1941         if (CollectionUtils.isNotEmpty(membersMatchByExactQualifiers)) {
1942             return membersMatchByExactQualifiers.get(0);
1943         }
1944         RoleMember anyMemberMatch = doAnyMemberRecordsMatch(roleBo.getMembers(), roleId, MemberType.ROLE.getCode(),
1945                 qualifier);
1946         if (null != anyMemberMatch) {
1947             return anyMemberMatch;
1948         }
1949 
1950         // Check to make sure this doesn't create a circular membership
1951         if (!checkForCircularRoleMembership(roleId, roleBo)) {
1952             throw new IllegalArgumentException("Circular roleBo reference.");
1953         }
1954         // create the new roleBo member object
1955         RoleMemberBo newRoleMember = new RoleMemberBo();
1956         newRoleMember.setRoleId(roleBo.getId());
1957         newRoleMember.setMemberId(roleId);
1958         newRoleMember.setType(MemberType.ROLE);
1959         // build roleBo member attribute objects from the given Map<String, String>
1960         addMemberAttributeData(newRoleMember, qualifier, roleBo.getKimTypeId());
1961 
1962         // When members are added to roles, clients must be notified.
1963         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(newRoleMember));
1964     }
1965 
1966     @Override
1967     public RoleMember createRoleMember(RoleMember roleMember) throws RiceIllegalStateException {
1968         incomingParamCheck(roleMember, "roleMember");
1969 
1970         if (StringUtils.isNotBlank(roleMember.getId()) && getRoleMemberBo(roleMember.getId()) != null) {
1971             throw new RiceIllegalStateException("the roleMember to create already exists: " + roleMember);
1972         }
1973 
1974         String kimTypeId = getRoleBoLite(roleMember.getRoleId()).getKimTypeId();
1975         List<RoleMemberAttributeDataBo> attrBos = Collections.emptyList();
1976         attrBos = KimAttributeDataBo.createFrom(RoleMemberAttributeDataBo.class, roleMember.getAttributes(), kimTypeId);
1977 
1978         RoleMemberBo bo = RoleMemberBo.from(roleMember);
1979         bo.setAttributeDetails(attrBos);
1980         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(bo));
1981     }
1982 
1983     @Override
1984     public RoleMember updateRoleMember(@WebParam(
1985             name = "roleMember") RoleMember roleMember) throws RiceIllegalArgumentException, RiceIllegalStateException {
1986         incomingParamCheck(roleMember, "roleMember");
1987 
1988         RoleMemberBo roleMemberBo = null;
1989         if (StringUtils.isNotBlank(roleMember.getId())) {
1990             roleMemberBo = getRoleMemberBo(roleMember.getId());
1991         }
1992         if (StringUtils.isBlank(roleMember.getId()) || roleMemberBo == null) {
1993             throw new RiceIllegalStateException("the roleMember to update does not exists: " + roleMember);
1994         }
1995 
1996         //RoleMemberBo bo = RoleMemberBo.from(roleMember);
1997         //List<RoleMemberAttributeDataBo> updateAttrBos =   new ArrayList<RoleMemberAttributeDataBo>();
1998 
1999         // Copy the main data
2000         roleMemberBo.setMemberId(roleMember.getMemberId());
2001         roleMemberBo.setTypeCode(roleMember.getType().getCode());
2002         roleMemberBo.setActiveFromDateValue(roleMember.getActiveFromDate() == null ? null : new Timestamp(
2003                 roleMember.getActiveFromDate().getMillis()));
2004         roleMemberBo.setActiveToDateValue(roleMember.getActiveToDate() == null ? null : new Timestamp(
2005                 roleMember.getActiveToDate().getMillis()));
2006 
2007         Iterator<RoleResponsibilityActionBo> actions = roleMemberBo.getRoleRspActions().iterator();
2008         List<RoleResponsibilityAction> newActionList = new ArrayList(roleMember.getRoleRspActions());
2009         // loop over the existing actions
2010         while (actions.hasNext()) {
2011             RoleResponsibilityActionBo action = actions.next();
2012             // look for a match in the new list
2013             boolean matched = false;
2014             Iterator<RoleResponsibilityAction> newActions = newActionList.iterator();
2015             while (newActions.hasNext()) {
2016                 RoleResponsibilityAction newAction = newActions.next();
2017                 if (newAction.getId().equals(action.getId())) {
2018                     matched = true;
2019                     action.setActionPolicyCode(newAction.getActionPolicyCode());
2020                     action.setActionTypeCode(newAction.getActionTypeCode());
2021                     action.setPriorityNumber(newAction.getPriorityNumber());
2022                     action.setForceAction(newAction.isForceAction());
2023                     newActions.remove(); // processed this one - we don't want to see it again
2024                     break;
2025                 }
2026             }
2027             if (!matched) {
2028                 // nothing in the new list matched - this means this action was deleted
2029                 actions.remove();
2030             }
2031         }
2032         // now - anything left in the new attribute list needs to be added
2033         for (RoleResponsibilityAction rra : newActionList) {
2034             roleMemberBo.getRoleRspActions().add(RoleResponsibilityActionBo.from(rra));
2035         }
2036 
2037         String kimTypeId = getRoleBoLite(roleMember.getRoleId()).getKimTypeId();
2038         List<RoleMemberAttributeDataBo> newAttributeBos = KimAttributeDataBo.createFrom(RoleMemberAttributeDataBo.class,
2039                 roleMember.getAttributes(), kimTypeId);
2040         Iterator<RoleMemberAttributeDataBo> attributes = roleMemberBo.getAttributeDetails().iterator();
2041         // loop over the existing attributes
2042         while (attributes.hasNext()) {
2043             RoleMemberAttributeDataBo attr = attributes.next();
2044             // look for a match in the new list
2045             boolean matched = false;
2046             Iterator<RoleMemberAttributeDataBo> newAttributes = newAttributeBos.iterator();
2047             while (newAttributes.hasNext()) {
2048                 RoleMemberAttributeDataBo newAttr = newAttributes.next();
2049                 if (newAttr.getKimTypeId().equals(attr.getKimTypeId()) && newAttr.getKimAttributeId().equals(
2050                         attr.getKimAttributeId())) {
2051                     matched = true;
2052                     attr.setAttributeValue(newAttr.getAttributeValue());
2053                     newAttributes.remove(); // processed this one - we don't want to see it again
2054                     break;
2055                 }
2056             }
2057             if (!matched) {
2058                 // nothing in the new list matched - this means this attribute was deleted
2059                 attributes.remove();
2060             }
2061         }
2062         // now - anything left in the new attribute list needs to be added
2063         roleMemberBo.getAttributeDetails().addAll(newAttributeBos);
2064 
2065         // FIXME : this code does not delete removed attributes
2066         //        while ( newAttributes.hasNext() ) {
2067         //            RoleMemberAttributeDataBo newAttr = newAttributes.next();
2068         //            boolean matched = false;
2069         //            for (RoleMemberAttributeDataBo roleMemberAttrDataBo :  roleMemberBo.getAttributeDetails()) {
2070         //                if (newAttr.getKimTypeId().equals(roleMemberAttrDataBo.getKimTypeId())
2071         //                        && newAttr.getKimAttributeId().equals(roleMemberAttrDataBo.getKimAttributeId())) {
2072         //                    roleMemberAttrDataBo.setAttributeValue( newAttr.getAttributeValue() );
2073         //                    matched = true;
2074         //                    newAttributes.remove();
2075         //                    break;
2076         //                }
2077         //            }
2078         //            if (!matched) {
2079         //                newAttr.setAssignedToId(roleMemberBo.getId());
2080         //                roleMemberBo.getAttributeDetails().add(newAttr);
2081         //                newAttributes.remove();
2082         //            }
2083         //        }
2084 
2085         return RoleMemberBo.to(getResponsibilityInternalService().saveRoleMember(roleMemberBo));
2086     }
2087 
2088     @Override
2089     public DelegateMember updateDelegateMember(@WebParam(
2090             name = "delegateMember") DelegateMember delegateMember)
2091             throws RiceIllegalArgumentException, RiceIllegalStateException {
2092 
2093         //check delegateMember not empty
2094         incomingParamCheck(delegateMember, "delegateMember");
2095 
2096         //check delegate exists
2097         String delegationId = delegateMember.getDelegationId();
2098         incomingParamCheck(delegationId, "delegationId");
2099 
2100         DelegateTypeBo delegate = getKimDelegationImpl(delegationId);
2101         DelegateMemberBo delegateMemberBo = null;
2102 
2103         if (StringUtils.isNotEmpty(delegateMember.getDelegationMemberId())) {
2104             delegateMemberBo = getDelegateMemberBo(delegateMember.getDelegationMemberId());
2105         }
2106         if (delegateMemberBo == null) {
2107             throw new RiceIllegalStateException("the delegate does not exist: " + delegationId);
2108         }
2109 
2110         // copy the main data
2111         delegateMemberBo.setActiveFromDateValue(delegateMember.getActiveFromDate() == null ? null : new Timestamp(
2112                 delegateMember.getActiveFromDate().getMillis()));
2113         delegateMemberBo.setActiveToDateValue(delegateMember.getActiveToDate() == null ? null : new Timestamp(
2114                 delegateMember.getActiveToDate().getMillis()));
2115         delegateMemberBo.setMemberId(delegateMember.getMemberId());
2116         delegateMemberBo.setRoleMemberId(delegateMember.getRoleMemberId());
2117         delegateMemberBo.setTypeCode(delegateMember.getType().getCode());
2118 
2119         String kimTypeId = delegate.getKimTypeId();
2120         List<DelegateMemberAttributeDataBo> newAttributeBos = KimAttributeDataBo.createFrom(
2121                 DelegateMemberAttributeDataBo.class, delegateMember.getAttributes(), kimTypeId);
2122         Iterator<DelegateMemberAttributeDataBo> attributes = delegateMemberBo.getAttributeDetails().iterator();
2123         // loop over the existing attributes
2124         while (attributes.hasNext()) {
2125             DelegateMemberAttributeDataBo attr = attributes.next();
2126             // look for a match in the new list
2127             boolean matched = false;
2128             Iterator<DelegateMemberAttributeDataBo> newAttributes = newAttributeBos.iterator();
2129             while (newAttributes.hasNext()) {
2130                 DelegateMemberAttributeDataBo newAttr = newAttributes.next();
2131                 if (newAttr.getKimTypeId().equals(attr.getKimTypeId()) && newAttr.getKimAttributeId().equals(
2132                         attr.getKimAttributeId())) {
2133                     matched = true;
2134                     attr.setAttributeValue(newAttr.getAttributeValue());
2135                     newAttributes.remove(); // processed this one - we don't want to see it again
2136                     break;
2137                 }
2138             }
2139             if (!matched) {
2140                 // nothing in the new list matched - this means this attribute was deleted
2141                 attributes.remove();
2142             }
2143         }
2144         // now - anything left in the new attribute list needs to be added
2145         delegateMemberBo.getAttributeDetails().addAll(newAttributeBos);
2146 
2147         return DelegateMemberBo.to(getResponsibilityInternalService().saveDelegateMember(delegateMemberBo));
2148     }
2149 
2150     @Override
2151     public DelegateMember createDelegateMember(@WebParam(
2152             name = "delegateMember") DelegateMember delegateMember)
2153             throws RiceIllegalArgumentException, RiceIllegalStateException {
2154         //ensure object not empty
2155         incomingParamCheck(delegateMember, "delegateMember");
2156 
2157         //check key is null
2158         if (delegateMember.getDelegationMemberId() != null) {
2159             throw new RiceIllegalStateException(
2160                     "the delegate member already exists: " + delegateMember.getDelegationMemberId());
2161         }
2162 
2163         //check delegate exists
2164         String delegationId = delegateMember.getDelegationId();
2165         incomingParamCheck(delegationId, "delegationId");
2166         DelegateTypeBo delegate = getKimDelegationImpl(delegationId);
2167         if (delegate == null) {
2168             throw new RiceIllegalStateException("the delegate does not exist: " + delegationId);
2169         }
2170 
2171         //check member exists
2172         String memberId = delegateMember.getMemberId();
2173         incomingParamCheck(memberId, "memberId");
2174         Principal kPrincipal = KimApiServiceLocator.getIdentityService().getPrincipal(memberId);
2175         if (kPrincipal == null) {
2176             throw new RiceIllegalStateException("the user does not exist: " + memberId);
2177         }
2178 
2179         //create member delegate
2180         String kimTypeId = getRoleBoLite(delegate.getRoleId()).getKimTypeId();
2181         List<DelegateMemberAttributeDataBo> attrBos = Collections.emptyList();
2182         attrBos = KimAttributeDataBo.createFrom(DelegateMemberAttributeDataBo.class, delegateMember.getAttributes(),
2183                 kimTypeId);
2184         DelegateMemberBo bo = DelegateMemberBo.from(delegateMember);
2185         bo.setAttributeDetails(attrBos);
2186         return DelegateMemberBo.to(getResponsibilityInternalService().saveDelegateMember(bo));
2187     }
2188 
2189     @Override
2190     public void removeDelegateMembers(@WebParam(
2191             name = "delegateMembers") List<DelegateMember> delegateMembers)
2192             throws RiceIllegalArgumentException, RiceIllegalStateException {
2193         incomingParamCheck(delegateMembers, "delegateMembers");
2194         for (DelegateMember delegateMember : delegateMembers) {
2195             DelegateMember.Builder delegateMemberInfo = DelegateMember.Builder.create();
2196             delegateMemberInfo.setDelegationMemberId(delegateMember.getDelegationMemberId());
2197             delegateMemberInfo.setAttributes(delegateMember.getAttributes());
2198             delegateMemberInfo.setDelegationId(delegateMember.getDelegationId());
2199             delegateMemberInfo.setMemberId(delegateMember.getMemberId());
2200             delegateMemberInfo.setRoleMemberId(delegateMember.getRoleMemberId());
2201             delegateMemberInfo.setType(delegateMember.getType());
2202             delegateMemberInfo.setActiveFromDate(delegateMember.getActiveFromDate());
2203             delegateMemberInfo.setActiveToDate(DateTime.now());
2204             updateDelegateMember(delegateMemberInfo.build());
2205         }
2206     }
2207 
2208     @Override
2209     public RoleResponsibilityAction createRoleResponsibilityAction(RoleResponsibilityAction roleResponsibilityAction)
2210             throws RiceIllegalArgumentException, RiceIllegalStateException {
2211         incomingParamCheck(roleResponsibilityAction, "roleResponsibilityAction");
2212 
2213         if (StringUtils.isNotBlank(roleResponsibilityAction.getId()) && getRoleResponsibilityActionBo(
2214                 roleResponsibilityAction.getId()) != null) {
2215             throw new RiceIllegalStateException(
2216                     "the roleResponsibilityAction to create already exists: " + roleResponsibilityAction);
2217         }
2218 
2219         RoleResponsibilityActionBo bo = RoleResponsibilityActionBo.from(roleResponsibilityAction);
2220         return RoleResponsibilityActionBo.to(getDataObjectService().save(bo));
2221     }
2222 
2223     /**
2224      * Queues ActionRequest refresh/regeneration for RoleResponsbilityAction change
2225      *
2226      * @param bo the changed or deleted RoleResponsibilityActionBo
2227      */
2228     protected void updateActionRequestsForRoleResponsibilityActionChange(RoleResponsibilityActionBo bo) {
2229         RoleResponsibilityBo rr = bo.getRoleResponsibility();
2230         if (rr != null) {
2231             getResponsibilityInternalService().updateActionRequestsForResponsibilityChange(Collections.singleton(
2232                     rr.getResponsibilityId()));
2233         }
2234     }
2235 
2236     @Override
2237     public RoleResponsibilityAction updateRoleResponsibilityAction(RoleResponsibilityAction roleResponsibilityAction)
2238             throws RiceIllegalArgumentException, RiceIllegalStateException {
2239         incomingParamCheck(roleResponsibilityAction, "roleResponsibilityAction");
2240 
2241         if (StringUtils.isBlank(roleResponsibilityAction.getId()) || getRoleResponsibilityActionBo(
2242                 roleResponsibilityAction.getId()) == null) {
2243             throw new RiceIllegalStateException(
2244                     "the roleResponsibilityAction to create does not exist: " + roleResponsibilityAction);
2245         }
2246 
2247         RoleResponsibilityActionBo bo = RoleResponsibilityActionBo.from(roleResponsibilityAction);
2248         roleResponsibilityAction = RoleResponsibilityActionBo.to(getDataObjectService().save(bo));
2249 
2250         // update action requests
2251         updateActionRequestsForRoleResponsibilityActionChange(bo);
2252 
2253         return roleResponsibilityAction;
2254     }
2255 
2256     @Override
2257     public void deleteRoleResponsibilityAction(String roleResponsibilityActionId)
2258             throws RiceIllegalArgumentException, RiceIllegalStateException {
2259         incomingParamCheck(roleResponsibilityActionId, "roleResponsibilityActionId");
2260 
2261         RoleResponsibilityActionBo bo = getRoleResponsibilityActionBo(roleResponsibilityActionId);
2262         if (StringUtils.isBlank(roleResponsibilityActionId) || bo == null) {
2263             throw new RiceIllegalStateException(
2264                     "the roleResponsibilityAction to delete does not exist: " + roleResponsibilityActionId);
2265         }
2266 
2267         getDataObjectService().delete(bo);
2268 
2269         // update action requests
2270         updateActionRequestsForRoleResponsibilityActionChange(bo);
2271     }
2272 
2273     @Override
2274     public DelegateType createDelegateType(DelegateType delegateType)
2275             throws RiceIllegalArgumentException, RiceIllegalStateException {
2276         incomingParamCheck(delegateType, "delegateType");
2277 
2278         if (StringUtils.isNotBlank(delegateType.getDelegationId()) && getDelegateTypeByDelegationId(
2279                 delegateType.getDelegationId()) != null) {
2280             throw new RiceIllegalStateException("the delegateType to create already exists: " + delegateType);
2281         }
2282 
2283         DelegateTypeBo bo = DelegateTypeBo.from(delegateType);
2284         return DelegateTypeBo.to(getDataObjectService().save(bo));
2285     }
2286 
2287     @Override
2288     public DelegateType updateDelegateType(DelegateType delegateType)
2289             throws RiceIllegalArgumentException, RiceIllegalStateException {
2290         incomingParamCheck(delegateType, "delegateType");
2291 
2292         if (StringUtils.isBlank(delegateType.getDelegationId()) || getDelegateTypeByDelegationId(
2293                 delegateType.getDelegationId()) == null) {
2294             throw new RiceIllegalStateException("the delegateType to update does not exist: " + delegateType);
2295         }
2296 
2297         DelegateTypeBo bo = DelegateTypeBo.from(delegateType);
2298         return DelegateTypeBo.to(getDataObjectService().save(bo));
2299     }
2300 
2301     private void removeRoleMembers(List<RoleMemberBo> members) {
2302         if (CollectionUtils.isNotEmpty(members)) {
2303             for (RoleMemberBo rm : members) {
2304                 getResponsibilityInternalService().removeRoleMember(rm);
2305             }
2306         }
2307     }
2308 
2309     private List<RoleMemberBo> getRoleMembersByDefaultStrategy(String roleId, String memberId, String memberTypeCode,
2310             Map<String, String> qualifier) {
2311         List<RoleMemberBo> rms = new ArrayList<RoleMemberBo>();
2312         List<RoleMemberBo> roleMem = getRoleMembershipsForMemberId(memberTypeCode, memberId, qualifier);
2313         for (RoleMemberBo rm : roleMem) {
2314             if (rm.getRoleId().equals(roleId)) {
2315                 // if found, remove
2316                 rms.add(rm);
2317             }
2318         }
2319         return rms;
2320     }
2321 
2322     @Override
2323     public void removePrincipalFromRole(String principalId, String namespaceCode, String roleName,
2324             Map<String, String> qualifier) throws RiceIllegalArgumentException {
2325         if (StringUtils.isBlank(principalId)) {
2326             throw new RiceIllegalArgumentException("principalId is null");
2327         }
2328 
2329         if (StringUtils.isBlank(namespaceCode)) {
2330             throw new RiceIllegalArgumentException("namespaceCode is null");
2331         }
2332 
2333         if (StringUtils.isBlank(roleName)) {
2334             throw new RiceIllegalArgumentException("roleName is null");
2335         }
2336 
2337         if (qualifier == null) {
2338             throw new RiceIllegalArgumentException("qualifier is null");
2339         }
2340         // look up the role
2341         RoleBoLite role = getRoleBoLiteByName(namespaceCode, roleName);
2342         // pull all the principal members
2343         // look for an exact qualifier match
2344         List<RoleMemberBo> rms = getRoleMembersByExactQualifierMatch(role, principalId,
2345                 memberTypeToRoleDaoActionMap.get(MemberType.PRINCIPAL.getCode()), qualifier);
2346         if (CollectionUtils.isEmpty(rms)) {
2347             //Convert qualifier keys to use KIM attribute IDs since that's what getRoleMembersByDefaultStrategy is looking for.
2348             String kimTypeId = role.getKimTypeId();
2349             Map<String, String> attributeQualifierMap = new HashMap<String, String>();
2350             for (String qualifierKey : qualifier.keySet()) {
2351                 attributeQualifierMap.put(getKimAttributeId(kimTypeId, qualifierKey), qualifier.get(qualifierKey));
2352             }
2353 
2354             rms = getRoleMembersByDefaultStrategy(role.getId(), principalId, MemberType.PRINCIPAL.getCode(),
2355                     attributeQualifierMap);
2356         }
2357         removeRoleMembers(rms);
2358     }
2359 
2360     @Override
2361     public void removeGroupFromRole(String groupId, String namespaceCode, String roleName,
2362             Map<String, String> qualifier) throws RiceIllegalArgumentException {
2363         if (StringUtils.isBlank(groupId)) {
2364             throw new RiceIllegalArgumentException("groupId is null");
2365         }
2366 
2367         if (StringUtils.isBlank(namespaceCode)) {
2368             throw new RiceIllegalArgumentException("namespaceCode is null");
2369         }
2370 
2371         if (StringUtils.isBlank(roleName)) {
2372             throw new RiceIllegalArgumentException("roleName is null");
2373         }
2374 
2375         if (qualifier == null) {
2376             throw new RiceIllegalArgumentException("qualifier is null");
2377         }
2378 
2379         // look up the roleBo
2380         RoleBoLite roleBo = getRoleBoLiteByName(namespaceCode, roleName);
2381         // pull all the group roleBo members
2382         // look for an exact qualifier match
2383         List<RoleMemberBo> rms = getRoleMembersByExactQualifierMatch(roleBo, groupId, memberTypeToRoleDaoActionMap.get(
2384                 MemberType.GROUP.getCode()), qualifier);
2385         if (CollectionUtils.isEmpty(rms)) {
2386             rms = getRoleMembersByDefaultStrategy(roleBo.getId(), groupId, MemberType.GROUP.getCode(), qualifier);
2387         }
2388         removeRoleMembers(rms);
2389     }
2390 
2391     @Override
2392     public void removeRoleFromRole(String roleId, String namespaceCode, String roleName, Map<String, String> qualifier)
2393             throws RiceIllegalArgumentException {
2394         incomingParamCheck(roleId, "roleId");
2395         incomingParamCheck(namespaceCode, "namespaceCode");
2396         incomingParamCheck(roleName, "roleName");
2397         incomingParamCheck(qualifier, "qualifier");
2398 
2399         // look up the role
2400         RoleBoLite role = getRoleBoLiteByName(namespaceCode, roleName);
2401         // pull all the group role members
2402         // look for an exact qualifier match
2403         List<RoleMemberBo> rms = getRoleMembersByExactQualifierMatch(role, roleId, memberTypeToRoleDaoActionMap.get(
2404                 MemberType.ROLE.getCode()), qualifier);
2405         if (CollectionUtils.isEmpty(rms)) {
2406             rms = getRoleMembersByDefaultStrategy(role.getId(), roleId, MemberType.ROLE.getCode(), qualifier);
2407         }
2408         removeRoleMembers(rms);
2409     }
2410 
2411     @Override
2412     public void assignPermissionToRole(String permissionId, String roleId) throws RiceIllegalArgumentException {
2413         incomingParamCheck(permissionId, "permissionId");
2414         incomingParamCheck(roleId, "roleId");
2415 
2416         RolePermissionBo newRolePermission = new RolePermissionBo();
2417 
2418         Long nextSeq = new Long(MaxValueIncrementerFactory.getIncrementer(getDataSource(),
2419                 KimConstants.SequenceNames.KRIM_ROLE_PERM_ID_S).nextLongValue());
2420 
2421         newRolePermission.setId(nextSeq.toString());
2422         newRolePermission.setRoleId(roleId);
2423         newRolePermission.setPermissionId(permissionId);
2424         newRolePermission.setActive(true);
2425 
2426         getDataObjectService().save(newRolePermission);
2427     }
2428 
2429     @Override
2430     public void revokePermissionFromRole(String permissionId, String roleId) throws RiceIllegalArgumentException {
2431         incomingParamCheck(permissionId, "permissionId");
2432         incomingParamCheck(roleId, "roleId");
2433 
2434         Map<String, Object> params = new HashMap<String, Object>();
2435         params.put("roleId", roleId);
2436         params.put("permissionId", permissionId);
2437         params.put("active", Boolean.TRUE);
2438         QueryResults<RolePermissionBo> rolePermissionBos = getDataObjectService().findMatching(RolePermissionBo.class,
2439                 QueryByCriteria.Builder.andAttributes(params).build());
2440         List<RolePermissionBo> rolePermsToSave = new ArrayList<RolePermissionBo>();
2441         for (RolePermissionBo rolePerm : rolePermissionBos.getResults()) {
2442             rolePerm.setActive(false);
2443             rolePermsToSave.add(rolePerm);
2444         }
2445 
2446         getDataObjectService().save(rolePermsToSave);
2447     }
2448 
2449     protected void addMemberAttributeData(RoleMemberBo roleMember, Map<String, String> qualifier, String kimTypeId) {
2450         List<RoleMemberAttributeDataBo> attributes = new ArrayList<RoleMemberAttributeDataBo>();
2451         for (Map.Entry<String, String> entry : qualifier.entrySet()) {
2452             RoleMemberAttributeDataBo roleMemberAttrBo = new RoleMemberAttributeDataBo();
2453             roleMemberAttrBo.setAttributeValue(entry.getValue());
2454             roleMemberAttrBo.setKimTypeId(kimTypeId);
2455             roleMemberAttrBo.setAssignedToId(roleMember.getId());
2456             // look up the attribute ID
2457             roleMemberAttrBo.setKimAttributeId(getKimAttributeId(kimTypeId, entry.getKey()));
2458 
2459             Map<String, String> criteria = new HashMap<String, String>();
2460             criteria.put(KimConstants.PrimaryKeyConstants.KIM_ATTRIBUTE_ID, roleMemberAttrBo.getKimAttributeId());
2461             //criteria.put(KimConstants.PrimaryKeyConstants.ROLE_MEMBER_ID, roleMember.getId());
2462             criteria.put("assignedToId", roleMember.getId());
2463             QueryResults<RoleMemberAttributeDataBo> origRoleMemberAttributes = getDataObjectService().findMatching(
2464                     RoleMemberAttributeDataBo.class, QueryByCriteria.Builder.andAttributes(criteria).build());
2465             RoleMemberAttributeDataBo origRoleMemberAttribute =
2466                     (!origRoleMemberAttributes.getResults().isEmpty()) ? origRoleMemberAttributes.getResults().get(0) :
2467                             null;
2468             if (origRoleMemberAttribute != null) {
2469                 roleMemberAttrBo.setId(origRoleMemberAttribute.getId());
2470                 roleMemberAttrBo.setVersionNumber(origRoleMemberAttribute.getVersionNumber());
2471             }
2472             attributes.add(roleMemberAttrBo);
2473         }
2474         roleMember.setAttributeDetails(attributes);
2475     }
2476 
2477     protected void addDelegationMemberAttributeData(DelegateMemberBo delegationMember, Map<String, String> qualifier,
2478             String kimTypeId) {
2479         List<DelegateMemberAttributeDataBo> attributes = new ArrayList<DelegateMemberAttributeDataBo>();
2480         for (Map.Entry<String, String> entry : qualifier.entrySet()) {
2481             DelegateMemberAttributeDataBo delegateMemberAttrBo = new DelegateMemberAttributeDataBo();
2482             delegateMemberAttrBo.setAttributeValue(entry.getValue());
2483             delegateMemberAttrBo.setKimTypeId(kimTypeId);
2484             delegateMemberAttrBo.setAssignedToId(delegationMember.getDelegationMemberId());
2485             // look up the attribute ID
2486             delegateMemberAttrBo.setKimAttributeId(getKimAttributeId(kimTypeId, entry.getKey()));
2487             Map<String, String> criteria = new HashMap<String, String>();
2488             criteria.put(KimConstants.PrimaryKeyConstants.KIM_ATTRIBUTE_ID, delegateMemberAttrBo.getKimAttributeId());
2489             criteria.put(KimConstants.PrimaryKeyConstants.DELEGATION_MEMBER_ID,
2490                     delegationMember.getDelegationMemberId());
2491             QueryResults<DelegateMemberAttributeDataBo> origDelegationMemberAttributes =
2492                     getDataObjectService().findMatching(DelegateMemberAttributeDataBo.class,
2493                             QueryByCriteria.Builder.andAttributes(criteria).build());
2494             DelegateMemberAttributeDataBo origDelegationMemberAttribute =
2495                     (!origDelegationMemberAttributes.getResults().isEmpty()) ?
2496                             origDelegationMemberAttributes.getResults().get(0) : null;
2497             if (origDelegationMemberAttribute != null) {
2498                 delegateMemberAttrBo.setId(origDelegationMemberAttribute.getId());
2499                 delegateMemberAttrBo.setVersionNumber(origDelegationMemberAttribute.getVersionNumber());
2500             }
2501             attributes.add(delegateMemberAttrBo);
2502         }
2503         delegationMember.setAttributeDetails(attributes);
2504     }
2505 
2506     // --------------------
2507     // Persistence Methods
2508     // --------------------
2509 
2510     private void deleteNullMemberAttributeData(List<RoleMemberAttributeDataBo> attributes) {
2511         List<RoleMemberAttributeDataBo> attributesToDelete = new ArrayList<RoleMemberAttributeDataBo>();
2512         for (RoleMemberAttributeDataBo attribute : attributes) {
2513             if (attribute.getAttributeValue() == null) {
2514                 attributesToDelete.add(attribute);
2515             }
2516         }
2517         getDataObjectService().delete(attributesToDelete);
2518     }
2519 
2520     private void deleteNullDelegationMemberAttributeData(List<DelegateMemberAttributeDataBo> attributes) {
2521         List<DelegateMemberAttributeDataBo> attributesToDelete = new ArrayList<DelegateMemberAttributeDataBo>();
2522 
2523         for (DelegateMemberAttributeDataBo attribute : attributes) {
2524             if (attribute.getAttributeValue() == null) {
2525                 attributesToDelete.add(attribute);
2526             }
2527         }
2528         getDataObjectService().delete(attributesToDelete);
2529     }
2530 
2531     protected void logPrincipalHasRoleCheck(String principalId, List<String> roleIds,
2532             Map<String, String> roleQualifiers) {
2533         StringBuilder sb = new StringBuilder();
2534         sb.append('\n');
2535         sb.append("Has Role     : ").append(roleIds).append('\n');
2536         if (roleIds != null) {
2537             for (String roleId : roleIds) {
2538                 Role role = getRole(roleId);
2539                 if (role != null) {
2540                     sb.append("        Name : ").append(role.getNamespaceCode()).append('/').append(role.getName());
2541                     sb.append(" (").append(roleId).append(')');
2542                     sb.append('\n');
2543                 }
2544             }
2545         }
2546         sb.append("   Principal : ").append(principalId);
2547         if (principalId != null) {
2548             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
2549             if (principal != null) {
2550                 sb.append(" (").append(principal.getPrincipalName()).append(')');
2551             }
2552         }
2553         sb.append('\n');
2554         sb.append("     Details :\n");
2555         if (roleQualifiers != null) {
2556             sb.append(roleQualifiers);
2557         } else {
2558             sb.append("               [null]\n");
2559         }
2560         if (LOG.isTraceEnabled()) {
2561             LOG.trace(sb.append(ExceptionUtils.getStackTrace(new Throwable())));
2562         } else {
2563             LOG.debug(sb.toString());
2564         }
2565     }
2566 
2567     private void incomingParamCheck(Object object, String name) {
2568         if (object == null) {
2569             throw new RiceIllegalArgumentException(name + " was null");
2570         } else if (object instanceof String && StringUtils.isBlank((String) object)) {
2571             throw new RiceIllegalArgumentException(name + " was blank");
2572         }
2573     }
2574 
2575     /**
2576      * This gets the proxied version of the role service which will go through
2577      * Spring's caching mechanism for method calls rather than skipping it when
2578      * methods are called directly.
2579      *
2580      * @return The proxied role service
2581      */
2582     protected RoleService getProxiedRoleService() {
2583         if (this.proxiedRoleService == null) {
2584             this.proxiedRoleService = KimApiServiceLocator.getRoleService();
2585         }
2586         return this.proxiedRoleService;
2587     }
2588 
2589     /**
2590      * Sets the cache manager which this service implementation can for internal caching.
2591      * Calling this setter is optional, though the value passed to it must not be null.
2592      *
2593      * @param cacheManager the cache manager to use for internal caching, must not be null
2594      * @throws IllegalArgumentException if a null cache manager is passed
2595      */
2596     public void setCacheManager(CacheManager cacheManager) {
2597         if (cacheManager == null) {
2598             throw new IllegalArgumentException("cacheManager must not be null");
2599         }
2600         this.cacheManager = cacheManager;
2601     }
2602 
2603     protected DataSource getDataSource() {
2604         return KimImplServiceLocator.getDataSource();
2605     }
2606 
2607     private static class VersionedService<T> {
2608 
2609         String version;
2610         T service;
2611 
2612         VersionedService(String version, T service) {
2613             this.version = version;
2614             this.service = service;
2615         }
2616 
2617         T getService() {
2618             return this.service;
2619         }
2620 
2621         String getVersion() {
2622             return this.version;
2623         }
2624     }
2625 
2626     protected VersionedService<RoleTypeService> getVersionedRoleTypeService(KimType typeInfo) {
2627         QName serviceName = KimTypeUtils.resolveKimTypeServiceName(typeInfo.getServiceName());
2628         if (serviceName != null) {
2629             // default version since the base services have been available since then
2630             String version = CoreConstants.Versions.VERSION_2_0_0;
2631             RoleTypeService roleTypeService = null;
2632 
2633             try {
2634 
2635                 ServiceBus serviceBus = KsbApiServiceLocator.getServiceBus();
2636                 Endpoint endpoint = serviceBus.getEndpoint(serviceName);
2637                 if (endpoint != null) {
2638                     version = endpoint.getServiceConfiguration().getServiceVersion();
2639                 }
2640                 KimTypeService service = GlobalResourceLoader.getService(serviceName);
2641                 if (service != null && service instanceof RoleTypeService) {
2642                     roleTypeService = (RoleTypeService) service;
2643                 } else {
2644                     roleTypeService = (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
2645                 }
2646             } catch (Exception ex) {
2647                 roleTypeService = (RoleTypeService) KimImplServiceLocator.getService("kimNoMembersRoleTypeService");
2648             }
2649 
2650             return new VersionedService<RoleTypeService>(version, roleTypeService);
2651         }
2652 
2653         return null;
2654     }
2655 
2656     private Map<String, String> getNestedQualification(RoleBoLite memberRole, String namespaceCode, String roleName,
2657             String memberNamespaceCode, String memberName, Map<String, String> qualification,
2658             Map<String, String> memberQualification) {
2659         VersionedService<RoleTypeService> versionedRoleTypeService = getVersionedRoleTypeService(KimTypeBo.to(
2660                 memberRole.getKimRoleType()));
2661         // if null service - just return the original qualification (pre 2.3.4 - ignoring memberQualifications)
2662         if (versionedRoleTypeService == null) {
2663             return qualification;
2664         }
2665         boolean versionOk = VersionHelper.compareVersion(versionedRoleTypeService.getVersion(),
2666                 CoreConstants.Versions.VERSION_2_3_4) != -1;
2667         if (versionOk) {
2668             return versionedRoleTypeService.getService().convertQualificationForMemberRolesAndMemberAttributes(
2669                     namespaceCode, roleName, memberNamespaceCode, memberName, qualification, memberQualification);
2670         } else {
2671             return versionedRoleTypeService.getService().convertQualificationForMemberRoles(namespaceCode, roleName,
2672                     memberNamespaceCode, memberName, qualification);
2673         }
2674     }
2675 }