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