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