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