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