001 /** 002 * Copyright 2005-2011 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.group; 017 018 import org.apache.commons.collections.CollectionUtils; 019 import org.apache.commons.collections.Predicate; 020 import org.apache.commons.lang.StringUtils; 021 import org.apache.log4j.Logger; 022 import org.kuali.rice.core.api.criteria.CriteriaLookupService; 023 import org.kuali.rice.core.api.criteria.GenericQueryResults; 024 import org.kuali.rice.core.api.criteria.LookupCustomizer; 025 import org.kuali.rice.core.api.criteria.QueryByCriteria; 026 import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 027 import org.kuali.rice.core.api.exception.RiceRuntimeException; 028 import org.kuali.rice.core.api.membership.MemberType; 029 import org.kuali.rice.kim.api.KimConstants; 030 import org.kuali.rice.kim.api.group.Group; 031 import org.kuali.rice.kim.api.group.GroupMember; 032 import org.kuali.rice.kim.api.group.GroupMemberQueryResults; 033 import org.kuali.rice.kim.api.group.GroupQueryResults; 034 import org.kuali.rice.kim.api.group.GroupService; 035 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 036 import org.kuali.rice.kim.impl.KIMPropertyConstants; 037 import org.kuali.rice.kim.impl.common.attribute.AttributeTransform; 038 import org.kuali.rice.kim.impl.common.attribute.KimAttributeDataBo; 039 import org.kuali.rice.kim.impl.services.KimImplServiceLocator; 040 import org.kuali.rice.krad.service.BusinessObjectService; 041 042 import javax.jws.WebParam; 043 import java.sql.Timestamp; 044 import java.util.ArrayList; 045 import java.util.Collection; 046 import java.util.Collections; 047 import java.util.HashMap; 048 import java.util.HashSet; 049 import java.util.List; 050 import java.util.Map; 051 import java.util.Set; 052 053 import static org.kuali.rice.core.api.criteria.PredicateFactory.*; 054 055 public class GroupServiceImpl extends GroupServiceBase implements GroupService { 056 private static final Logger LOG = Logger.getLogger(GroupServiceImpl.class); 057 058 protected BusinessObjectService businessObjectService; 059 private CriteriaLookupService criteriaLookupService; 060 061 @Override 062 public Group getGroup(String groupId) throws RiceIllegalArgumentException { 063 incomingParamCheck(groupId, "groupId"); 064 return GroupBo.to(getGroupBo(groupId)); 065 } 066 067 @Override 068 public List<Group> getGroupsByPrincipalId(String principalId) throws RiceIllegalArgumentException { 069 incomingParamCheck(principalId, "principalId"); 070 return getGroupsByPrincipalIdAndNamespaceCodeInternal(principalId, null); 071 } 072 073 @Override 074 public List<Group> getGroupsByPrincipalIdAndNamespaceCode(String principalId, String namespaceCode) throws RiceIllegalArgumentException { 075 incomingParamCheck(principalId, "principalId"); 076 incomingParamCheck(namespaceCode, "namespaceCode"); 077 078 return getGroupsByPrincipalIdAndNamespaceCodeInternal(principalId, namespaceCode); 079 } 080 081 protected List<Group> getGroupsByPrincipalIdAndNamespaceCodeInternal(String principalId, String namespaceCode) throws RiceIllegalArgumentException { 082 083 Collection<Group> directGroups = getDirectGroupsForPrincipal( principalId, namespaceCode ); 084 Set<Group> groups = new HashSet<Group>(); 085 groups.addAll(directGroups); 086 for ( Group group : directGroups ) { 087 groups.add( group ); 088 groups.addAll( getParentGroups( group.getId() ) ); 089 } 090 return Collections.unmodifiableList(new ArrayList<Group>( groups )); 091 } 092 093 @Override 094 public List<String> findGroupIds(final QueryByCriteria queryByCriteria) throws RiceIllegalArgumentException { 095 incomingParamCheck(queryByCriteria, "queryByCriteria"); 096 097 GroupQueryResults results = this.findGroups(queryByCriteria); 098 List<String> result = new ArrayList<String>(); 099 100 for (Group group : results.getResults()) { 101 result.add(group.getId()); 102 } 103 104 return Collections.unmodifiableList(result); 105 } 106 107 @Override 108 public boolean isDirectMemberOfGroup(String principalId, String groupId) throws RiceIllegalArgumentException { 109 incomingParamCheck(principalId, "principalId"); 110 incomingParamCheck(groupId, "groupId"); 111 112 Map<String,String> criteria = new HashMap<String,String>(); 113 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_ID, principalId); 114 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_TYPE_CODE, KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode()); 115 criteria.put(KIMPropertyConstants.GroupMember.GROUP_ID, groupId); 116 117 Collection<GroupMemberBo> groupMembers = businessObjectService.findMatching(GroupMemberBo.class, criteria); 118 for ( GroupMemberBo gm : groupMembers ) { 119 if ( gm.isActive(new Timestamp(System.currentTimeMillis())) ) { 120 return true; 121 } 122 } 123 return false; 124 } 125 126 @Override 127 public List<String> getGroupIdsByPrincipalId(String principalId) throws RiceIllegalArgumentException { 128 incomingParamCheck(principalId, "principalId"); 129 return getGroupIdsByPrincipalIdAndNamespaceCodeInternal(principalId, null); 130 } 131 132 @Override 133 public List<String> getGroupIdsByPrincipalIdAndNamespaceCode(String principalId, String namespaceCode) throws RiceIllegalArgumentException { 134 incomingParamCheck(principalId, "principalId"); 135 incomingParamCheck(namespaceCode, "namespaceCode"); 136 137 List<String> result = new ArrayList<String>(); 138 139 if (principalId != null) { 140 List<Group> groupList = getGroupsByPrincipalIdAndNamespaceCode(principalId, namespaceCode); 141 142 for (Group group : groupList) { 143 result.add(group.getId()); 144 } 145 } 146 147 return Collections.unmodifiableList(result); 148 } 149 150 protected List<String> getGroupIdsByPrincipalIdAndNamespaceCodeInternal(String principalId, String namespaceCode) throws RiceIllegalArgumentException { 151 152 List<String> result = new ArrayList<String>(); 153 154 if (principalId != null) { 155 List<Group> groupList = getGroupsByPrincipalIdAndNamespaceCodeInternal(principalId, namespaceCode); 156 157 for (Group group : groupList) { 158 result.add(group.getId()); 159 } 160 } 161 162 return Collections.unmodifiableList(result); 163 } 164 165 @Override 166 public List<String> getDirectGroupIdsByPrincipalId(String principalId) throws RiceIllegalArgumentException { 167 incomingParamCheck(principalId, "principalId"); 168 169 List<String> result = new ArrayList<String>(); 170 171 if (principalId != null) { 172 Collection<Group> groupList = getDirectGroupsForPrincipal(principalId); 173 174 for (Group g : groupList) { 175 result.add(g.getId()); 176 } 177 } 178 179 return Collections.unmodifiableList(result); 180 } 181 182 @Override 183 public List<String> getMemberPrincipalIds(String groupId) throws RiceIllegalArgumentException { 184 incomingParamCheck(groupId, "groupId"); 185 186 return getMemberPrincipalIdsInternal(groupId, new HashSet<String>()); 187 } 188 189 @Override 190 public List<String> getDirectMemberPrincipalIds(String groupId) throws RiceIllegalArgumentException { 191 incomingParamCheck(groupId, "groupId"); 192 193 return this.getMemberIdsByType(getMembersOfGroup(groupId), KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE); 194 } 195 196 @Override 197 public List<String> getMemberGroupIds(String groupId) throws RiceIllegalArgumentException { 198 incomingParamCheck(groupId, "groupId"); 199 200 List<GroupBo> groups = getMemberGroupBos( groupId ); 201 ArrayList<String> groupIds = new ArrayList<String>( groups.size() ); 202 for ( GroupBo group : groups ) { 203 if ( group.isActive() ) { 204 groupIds.add( group.getId() ); 205 } 206 } 207 return Collections.unmodifiableList(groupIds); 208 } 209 210 211 protected List<GroupBo> getMemberGroupBos(String groupId) { 212 if ( groupId == null ) { 213 return Collections.emptyList(); 214 } 215 Set<GroupBo> groups = new HashSet<GroupBo>(); 216 217 GroupBo group = getGroupBo(groupId); 218 getMemberGroupsInternal(group, groups); 219 220 return new ArrayList<GroupBo>(groups); 221 } 222 223 protected void getMemberGroupsInternal( GroupBo group, Set<GroupBo> groups ) { 224 if ( group == null ) { 225 return; 226 } 227 List<String> groupIds = group.getMemberGroupIds(); 228 229 for (String id : groupIds) { 230 GroupBo memberGroup = getGroupBo(id); 231 // if we've already seen that group, don't recurse into it 232 if ( memberGroup.isActive() && !groups.contains( memberGroup ) ) { 233 groups.add(memberGroup); 234 getMemberGroupsInternal(memberGroup,groups); 235 } 236 } 237 238 } 239 240 @Override 241 public boolean isGroupMemberOfGroup(String groupMemberId, String groupId) throws RiceIllegalArgumentException { 242 incomingParamCheck(groupMemberId, "groupMemberId"); 243 incomingParamCheck(groupId, "groupId"); 244 245 return isMemberOfGroupInternal(groupMemberId, groupId, new HashSet<String>(), KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE); 246 } 247 248 @Override 249 public boolean isMemberOfGroup(String principalId, String groupId) throws RiceIllegalArgumentException{ 250 incomingParamCheck(principalId, "principalId"); 251 incomingParamCheck(groupId, "groupId"); 252 253 Set<String> visitedGroupIds = new HashSet<String>(); 254 return isMemberOfGroupInternal(principalId, groupId, visitedGroupIds, KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE); 255 } 256 257 @Override 258 public List<String> getDirectMemberGroupIds(String groupId) throws RiceIllegalArgumentException{ 259 incomingParamCheck(groupId, "groupId"); 260 261 return this.getMemberIdsByType(getMembersOfGroup(groupId), KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE); 262 } 263 264 @Override 265 public List<String> getParentGroupIds(String groupId) throws RiceIllegalArgumentException { 266 incomingParamCheck(groupId, "groupId"); 267 268 List<String> result = new ArrayList<String>(); 269 if (groupId != null) { 270 List<Group> groupList = getParentGroups(groupId); 271 272 for (Group group : groupList) { 273 result.add(group.getId()); 274 } 275 } 276 277 return Collections.unmodifiableList(result); 278 } 279 280 @Override 281 public List<String> getDirectParentGroupIds(String groupId) throws RiceIllegalArgumentException { 282 incomingParamCheck(groupId, "groupId"); 283 284 List<String> result = new ArrayList<String>(); 285 if (groupId != null) { 286 List<Group> groupList = getDirectParentGroups(groupId); 287 for (Group group : groupList) { 288 result.add(group.getId()); 289 } 290 } 291 292 return Collections.unmodifiableList(result); 293 } 294 295 @Override 296 public Map<String, String> getAttributes(String groupId) throws RiceIllegalArgumentException { 297 incomingParamCheck(groupId, "groupId"); 298 299 Group group = getGroup(groupId); 300 if (group != null) { 301 return group.getAttributes(); 302 } 303 return Collections.emptyMap(); 304 } 305 306 @Override 307 public List<GroupMember> getMembers(List<String> groupIds) throws RiceIllegalArgumentException{ 308 if (CollectionUtils.isEmpty(groupIds)) { 309 throw new RiceIllegalArgumentException("groupIds is empty"); 310 } 311 312 //TODO: PRIME example of something for new Criteria API 313 List<GroupMember> groupMembers = new ArrayList<GroupMember>(); 314 for (String groupId : groupIds) { 315 groupMembers.addAll(getMembersOfGroup(groupId)); 316 } 317 return Collections.unmodifiableList(groupMembers); 318 } 319 320 @Override 321 public List<Group> getGroups(Collection<String> groupIds) throws RiceIllegalArgumentException { 322 incomingParamCheck(groupIds, "groupIds"); 323 if (groupIds.isEmpty()) { 324 return Collections.emptyList(); 325 } 326 final QueryByCriteria.Builder builder = QueryByCriteria.Builder.create(); 327 builder.setPredicates(and(in("id", groupIds.toArray()), equal("active", "Y"))); 328 GroupQueryResults qr = findGroups(builder.build()); 329 330 return qr.getResults(); 331 } 332 333 @Override 334 public Group getGroupByNameAndNamespaceCode(String namespaceCode, String groupName) throws RiceIllegalArgumentException{ 335 incomingParamCheck(namespaceCode, "namespaceCode"); 336 incomingParamCheck(groupName, "groupName"); 337 338 Map<String,String> criteria = new HashMap<String,String>(); 339 criteria.put(KimConstants.UniqueKeyConstants.NAMESPACE_CODE, namespaceCode); 340 criteria.put(KimConstants.UniqueKeyConstants.GROUP_NAME, groupName); 341 Collection<GroupBo> groups = businessObjectService.findMatching(GroupBo.class, criteria); 342 if ( !groups.isEmpty() ) { 343 return GroupBo.to(groups.iterator().next()); 344 } 345 return null; 346 } 347 348 @Override 349 public GroupQueryResults findGroups(final QueryByCriteria queryByCriteria) throws RiceIllegalArgumentException { 350 incomingParamCheck(queryByCriteria, "queryByCriteria"); 351 352 LookupCustomizer.Builder<GroupBo> lc = LookupCustomizer.Builder.create(); 353 lc.setPredicateTransform(AttributeTransform.getInstance()); 354 355 GenericQueryResults<GroupBo> results = criteriaLookupService.lookup(GroupBo.class, queryByCriteria, lc.build()); 356 357 GroupQueryResults.Builder builder = GroupQueryResults.Builder.create(); 358 builder.setMoreResultsAvailable(results.isMoreResultsAvailable()); 359 builder.setTotalRowCount(results.getTotalRowCount()); 360 361 final List<Group.Builder> ims = new ArrayList<Group.Builder>(); 362 for (GroupBo bo : results.getResults()) { 363 ims.add(Group.Builder.create(bo)); 364 } 365 366 builder.setResults(ims); 367 return builder.build(); 368 } 369 370 @Override 371 public GroupMemberQueryResults findGroupMembers(final QueryByCriteria queryByCriteria) throws RiceIllegalArgumentException { 372 incomingParamCheck(queryByCriteria, "queryByCriteria"); 373 374 GenericQueryResults<GroupMemberBo> results = criteriaLookupService.lookup(GroupMemberBo.class, queryByCriteria); 375 376 GroupMemberQueryResults.Builder builder = GroupMemberQueryResults.Builder.create(); 377 builder.setMoreResultsAvailable(results.isMoreResultsAvailable()); 378 builder.setTotalRowCount(results.getTotalRowCount()); 379 380 final List<GroupMember.Builder> ims = new ArrayList<GroupMember.Builder>(); 381 for (GroupMemberBo bo : results.getResults()) { 382 ims.add(GroupMember.Builder.create(bo)); 383 } 384 385 builder.setResults(ims); 386 return builder.build(); 387 } 388 389 390 protected boolean isMemberOfGroupInternal(String memberId, String groupId, Set<String> visitedGroupIds, MemberType memberType) { 391 if ( memberId == null || groupId == null ) { 392 return false; 393 } 394 395 // when group traversal is not needed 396 Group group = getGroup(groupId); 397 if ( group == null || !group.isActive() ) { 398 return false; 399 } 400 401 List<GroupMember> members = getMembersOfGroup(group.getId()); 402 // check the immediate group 403 for (String groupMemberId : getMemberIdsByType(members, memberType)) { 404 if (groupMemberId.equals(memberId)) { 405 return true; 406 } 407 } 408 409 // check each contained group, returning as soon as a match is found 410 for ( String memberGroupId : getMemberIdsByType(members, KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE) ) { 411 if (!visitedGroupIds.contains(memberGroupId)){ 412 visitedGroupIds.add(memberGroupId); 413 if ( isMemberOfGroupInternal( memberId, memberGroupId, visitedGroupIds, memberType ) ) { 414 return true; 415 } 416 } 417 } 418 419 // no match found, return false 420 return false; 421 } 422 423 protected void getParentGroupsInternal( String groupId, Set<Group> groups ) { 424 List<Group> parentGroups = getDirectParentGroups( groupId ); 425 for ( Group group : parentGroups ) { 426 if ( !groups.contains( group ) ) { 427 groups.add( group ); 428 getParentGroupsInternal( group.getId(), groups ); 429 } 430 } 431 } 432 433 protected List<Group> getDirectParentGroups(String groupId) { 434 if ( groupId == null ) { 435 return Collections.emptyList(); 436 } 437 Map<String,String> criteria = new HashMap<String,String>(); 438 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_ID, groupId); 439 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_TYPE_CODE, KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE.getCode()); 440 441 List<GroupMemberBo> groupMembers = (List<GroupMemberBo>)businessObjectService.findMatching(GroupMemberBo.class, criteria); 442 Set<String> matchingGroupIds = new HashSet<String>(); 443 // filter to active groups 444 for ( GroupMemberBo gm : groupMembers ) { 445 if ( gm.isActive(new Timestamp(System.currentTimeMillis())) ) { 446 matchingGroupIds.add(gm.getGroupId()); 447 } 448 } 449 if (CollectionUtils.isNotEmpty(matchingGroupIds)) { 450 return getGroups(matchingGroupIds); 451 } 452 return Collections.emptyList(); 453 } 454 455 @Override 456 public List<GroupMember> getMembersOfGroup(String groupId) throws RiceIllegalArgumentException { 457 incomingParamCheck(groupId, "groupId"); 458 Map<String,String> criteria = new HashMap<String,String>(); 459 criteria.put(KIMPropertyConstants.GroupMember.GROUP_ID, groupId); 460 461 Collection<GroupMemberBo> groupMembersBos = businessObjectService.findMatching(GroupMemberBo.class, criteria); 462 List<GroupMember> groupMembers = new ArrayList<GroupMember>(); 463 for (GroupMemberBo groupBo : groupMembersBos) { 464 if (groupBo.isActive(new Timestamp(System.currentTimeMillis()))){ 465 groupMembers.add(GroupMemberBo.to(groupBo)); 466 } 467 } 468 return Collections.unmodifiableList(groupMembers); 469 } 470 471 protected List<String> getMemberIdsByType(Collection<GroupMember> members, MemberType memberType) { 472 List<String> membersIds = new ArrayList<String>(); 473 if (members != null) { 474 for (GroupMember member : members) { 475 if (member.getType().equals(memberType)) { 476 membersIds.add(member.getMemberId()); 477 } 478 } 479 } 480 return Collections.unmodifiableList(membersIds); 481 } 482 483 protected GroupBo getGroupBo(String groupId) { 484 incomingParamCheck(groupId, "groupId"); 485 return businessObjectService.findByPrimaryKey(GroupBo.class, Collections.singletonMap("id", groupId)); 486 } 487 488 protected GroupMemberBo getGroupMemberBo(String id) { 489 incomingParamCheck(id, "id"); 490 return businessObjectService.findByPrimaryKey(GroupMemberBo.class, Collections.singletonMap("id", id)); 491 } 492 493 protected List<Group> getParentGroups(String groupId) throws RiceIllegalArgumentException { 494 if ( StringUtils.isEmpty(groupId) ) { 495 throw new RiceIllegalArgumentException("groupId is blank"); 496 } 497 Set<Group> groups = new HashSet<Group>(); 498 getParentGroupsInternal( groupId, groups ); 499 return new ArrayList<Group>( groups ); 500 } 501 502 protected List<String> getMemberPrincipalIdsInternal(String groupId, Set<String> visitedGroupIds) { 503 if ( groupId == null ) { 504 return Collections.emptyList(); 505 } 506 Set<String> ids = new HashSet<String>(); 507 GroupBo group = getGroupBo(groupId); 508 if ( group == null || !group.isActive()) { 509 return Collections.emptyList(); 510 } 511 512 //List<String> memberIds = getMemberIdsByType(group, memberType); 513 //List<GroupMember> members = new ArrayList<GroupMember>(getMembersOfGroup(group.getId())); 514 ids.addAll( group.getMemberPrincipalIds()); 515 visitedGroupIds.add(group.getId()); 516 517 for (String memberGroupId : group.getMemberGroupIds()) { 518 if (!visitedGroupIds.contains(memberGroupId)){ 519 ids.addAll(getMemberPrincipalIdsInternal(memberGroupId, visitedGroupIds)); 520 } 521 } 522 523 return Collections.unmodifiableList(new ArrayList<String>(ids)); 524 } 525 526 protected Collection<Group> getDirectGroupsForPrincipal( String principalId ) { 527 return getDirectGroupsForPrincipal( principalId, null ); 528 } 529 530 protected Collection<Group> getDirectGroupsForPrincipal( String principalId, String namespaceCode ) { 531 if ( principalId == null ) { 532 return Collections.emptyList(); 533 } 534 Map<String,Object> criteria = new HashMap<String,Object>(); 535 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_ID, principalId); 536 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_TYPE_CODE, KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE.getCode()); 537 Collection<GroupMemberBo> groupMembers = businessObjectService.findMatching(GroupMemberBo.class, criteria); 538 Set<String> groupIds = new HashSet<String>( groupMembers.size() ); 539 // only return the active members 540 for ( GroupMemberBo gm : groupMembers ) { 541 if ( gm.isActive(new Timestamp(System.currentTimeMillis())) ) { 542 groupIds.add( gm.getGroupId() ); 543 } 544 } 545 // pull all the group information for the matching members 546 List<Group> groups = CollectionUtils.isEmpty(groupIds) ? Collections.<Group>emptyList() : getGroups(groupIds); 547 List<Group> result = new ArrayList<Group>( groups.size() ); 548 // filter by namespace if necessary 549 for ( Group group : groups ) { 550 if ( group.isActive() ) { 551 if ( StringUtils.isBlank(namespaceCode) || StringUtils.equals(namespaceCode, group.getNamespaceCode() ) ) { 552 result.add(group); 553 } 554 } 555 } 556 return result; 557 } 558 559 @Override 560 public boolean addGroupToGroup(String childId, String parentId) throws RiceIllegalArgumentException { 561 incomingParamCheck(childId, "childId"); 562 incomingParamCheck(parentId, "parentId"); 563 564 if(childId.equals(parentId)) { 565 throw new RiceIllegalArgumentException("Can't add group to itself."); 566 } 567 if(isGroupMemberOfGroup(parentId, childId)) { 568 throw new RiceIllegalArgumentException("Circular group reference."); 569 } 570 571 GroupMemberBo groupMember = new GroupMemberBo(); 572 groupMember.setGroupId(parentId); 573 groupMember.setType(KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE); 574 groupMember.setMemberId(childId); 575 576 this.businessObjectService.save(groupMember); 577 return true; 578 } 579 580 @Override 581 public boolean addPrincipalToGroup(String principalId, String groupId) throws RiceIllegalArgumentException { 582 incomingParamCheck(principalId, "principalId"); 583 incomingParamCheck(groupId, "groupId"); 584 585 GroupMemberBo groupMember = new GroupMemberBo(); 586 groupMember.setGroupId(groupId); 587 groupMember.setType(KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE); 588 groupMember.setMemberId(principalId); 589 590 groupMember = this.businessObjectService.save(groupMember); 591 KimImplServiceLocator.getGroupInternalService().updateForUserAddedToGroup(groupMember.getMemberId(), 592 groupMember.getGroupId()); 593 return true; 594 } 595 596 @Override 597 public Group createGroup(Group group) throws RiceIllegalArgumentException { 598 incomingParamCheck(group, "group"); 599 if (StringUtils.isNotBlank(group.getId()) && getGroup(group.getId()) != null) { 600 throw new RiceIllegalArgumentException("the group to create already exists: " + group); 601 } 602 List<GroupAttributeBo> attrBos = KimAttributeDataBo 603 .createFrom(GroupAttributeBo.class, group.getAttributes(), group.getKimTypeId()); 604 if (StringUtils.isNotEmpty(group.getId())) { 605 for (GroupAttributeBo attr : attrBos) { 606 attr.setAssignedToId(group.getId()); 607 } 608 } 609 GroupBo bo = GroupBo.from(group); 610 bo.setAttributeDetails(attrBos); 611 612 bo = saveGroup(bo); 613 614 return GroupBo.to(bo); 615 } 616 617 @Override 618 public Group updateGroup(Group group) throws RiceIllegalArgumentException{ 619 incomingParamCheck(group, "group"); 620 GroupBo origGroup = getGroupBo(group.getId()); 621 if (StringUtils.isBlank(group.getId()) || origGroup == null) { 622 throw new RiceIllegalArgumentException("the group does not exist: " + group); 623 } 624 List<GroupAttributeBo> attrBos = KimAttributeDataBo.createFrom(GroupAttributeBo.class, group.getAttributes(), group.getKimTypeId()); 625 GroupBo bo = GroupBo.from(group); 626 bo.setMembers(origGroup.getMembers()); 627 bo.setAttributeDetails(attrBos); 628 629 bo = saveGroup(bo); 630 if (origGroup.isActive() 631 && !bo.isActive()) { 632 KimImplServiceLocator.getRoleInternalService().groupInactivated(bo.getId()); 633 } 634 635 return GroupBo.to(bo); 636 } 637 638 @Override 639 public Group updateGroup(String groupId, Group group) throws RiceIllegalArgumentException{ 640 incomingParamCheck(group, "group"); 641 incomingParamCheck(groupId, "groupId"); 642 643 if (StringUtils.equals(groupId, group.getId())) { 644 return updateGroup(group); 645 } 646 647 //if group Ids are different, inactivate old group, and create new with new id based off old 648 GroupBo groupBo = getGroupBo(groupId); 649 650 if (StringUtils.isBlank(group.getId()) || groupBo == null) { 651 throw new RiceIllegalArgumentException("the group does not exist: " + group); 652 } 653 654 //create and save new group 655 GroupBo newGroup = GroupBo.from(group); 656 newGroup.setMembers(groupBo.getMembers()); 657 List<GroupAttributeBo> attrBos = KimAttributeDataBo.createFrom(GroupAttributeBo.class, group.getAttributes(), group.getKimTypeId()); 658 newGroup.setAttributeDetails(attrBos); 659 newGroup = saveGroup(newGroup); 660 661 //inactivate and save old group 662 groupBo.setActive(false); 663 saveGroup(groupBo); 664 665 return GroupBo.to(newGroup); 666 } 667 668 @Override 669 public GroupMember createGroupMember(GroupMember groupMember) throws RiceIllegalArgumentException { 670 incomingParamCheck(groupMember, "groupMember"); 671 if (StringUtils.isNotBlank(groupMember.getId()) && getGroupMemberBo(groupMember.getId()) != null) { 672 throw new RiceIllegalArgumentException("the groupMember to create already exists: " + groupMember); 673 } 674 675 GroupMemberBo bo = GroupMemberBo.from(groupMember); 676 GroupBo groupBo = getGroupBo(groupMember.getGroupId()); 677 groupBo.getMembers().add(bo); 678 groupBo = saveGroup(groupBo); 679 680 //get new groupMember from saved group 681 for (GroupMemberBo member : groupBo.getMembers()) { 682 if (member.getMemberId().equals(groupMember.getMemberId()) 683 && member.getType().equals(groupMember.getType()) 684 && member.getActiveFromDate().equals(groupMember.getActiveFromDate()) 685 && member.getActiveToDate().equals(groupMember.getActiveToDate())) { 686 return GroupMemberBo.to(member); 687 } 688 } 689 return GroupMemberBo.to(bo); 690 } 691 692 @Override 693 public GroupMember updateGroupMember( 694 @WebParam(name = "groupMember") GroupMember groupMember) throws RiceIllegalArgumentException { 695 incomingParamCheck(groupMember, "groupMember"); 696 if (StringUtils.isBlank(groupMember.getId()) || getGroupMemberBo(groupMember.getId()) == null) { 697 throw new RiceIllegalArgumentException("the groupMember to update does not exist: " + groupMember); 698 } 699 700 GroupMemberBo bo = GroupMemberBo.from(groupMember); 701 GroupBo groupBo = getGroupBo(groupMember.getGroupId()); 702 //find and replace the existing member 703 704 List<GroupMemberBo> memberList = new ArrayList<GroupMemberBo>(); 705 for (GroupMemberBo member : groupBo.getMembers()) { 706 if (member.getId().equals(bo.getId())) { 707 memberList.add(bo); 708 } else { 709 memberList.add(member); 710 } 711 712 } 713 groupBo.setMembers(memberList); 714 groupBo = saveGroup(groupBo); 715 716 //get new groupMember from saved group 717 for (GroupMemberBo member : groupBo.getMembers()) { 718 if (member.getId().equals(groupMember.getId())) { 719 return GroupMemberBo.to(member); 720 } 721 } 722 return GroupMemberBo.to(bo); 723 } 724 725 @Override 726 public void removeAllMembers(String groupId) throws RiceIllegalArgumentException{ 727 incomingParamCheck(groupId, "groupId"); 728 729 730 GroupService groupService = KimApiServiceLocator.getGroupService(); 731 List<String> memberPrincipalsBefore = groupService.getMemberPrincipalIds(groupId); 732 733 Collection<GroupMemberBo> toDeactivate = getActiveGroupMembers(groupId, null, null); 734 java.sql.Timestamp today = new java.sql.Timestamp(System.currentTimeMillis()); 735 736 // Set principals as inactive 737 for (GroupMemberBo aToDeactivate : toDeactivate) { 738 aToDeactivate.setActiveToDateValue(today); 739 } 740 741 // Save 742 this.businessObjectService.save(new ArrayList<GroupMemberBo>(toDeactivate)); 743 List<String> memberPrincipalsAfter = groupService.getMemberPrincipalIds(groupId); 744 745 if (!CollectionUtils.isEmpty(memberPrincipalsAfter)) { 746 // should never happen! 747 LOG.warn("after attempting removal of all members, group with id '" + groupId + "' still has principal members"); 748 } 749 750 // do updates 751 KimImplServiceLocator.getGroupInternalService().updateForWorkgroupChange(groupId, memberPrincipalsBefore, 752 memberPrincipalsAfter); 753 } 754 755 @Override 756 public boolean removeGroupFromGroup(String childId, String parentId) throws RiceIllegalArgumentException { 757 incomingParamCheck(childId, "childId"); 758 incomingParamCheck(parentId, "parentId"); 759 760 java.sql.Timestamp today = new java.sql.Timestamp(System.currentTimeMillis()); 761 762 List<GroupMemberBo> groupMembers = 763 getActiveGroupMembers(parentId, childId, KimConstants.KimGroupMemberTypes.GROUP_MEMBER_TYPE); 764 765 if(groupMembers.size() == 1) { 766 GroupMemberBo groupMember = groupMembers.get(0); 767 groupMember.setActiveToDateValue(today); 768 this.businessObjectService.save(groupMember); 769 return true; 770 } 771 772 return false; 773 } 774 775 @Override 776 public boolean removePrincipalFromGroup(String principalId, String groupId) throws RiceIllegalArgumentException { 777 incomingParamCheck(principalId, "principalId"); 778 incomingParamCheck(groupId, "groupId"); 779 780 List<GroupMemberBo> groupMembers = 781 getActiveGroupMembers(groupId, principalId, KimConstants.KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE); 782 783 if(groupMembers.size() == 1) { 784 GroupMemberBo member = groupMembers.iterator().next(); 785 member.setActiveToDateValue(new java.sql.Timestamp(System.currentTimeMillis())); 786 this.businessObjectService.save(member); 787 KimImplServiceLocator.getGroupInternalService().updateForUserRemovedFromGroup(member.getMemberId(), 788 member.getGroupId()); 789 return true; 790 } 791 792 return false; 793 } 794 795 protected GroupBo saveGroup(GroupBo group) { 796 if ( group == null ) { 797 return null; 798 } else if (group.getId() != null) { 799 // Get the version of the group that is in the DB 800 GroupBo oldGroup = getGroupBo(group.getId()); 801 802 if (oldGroup != null) { 803 // Inactivate and re-add members no longer in the group (in order to preserve history). 804 java.sql.Timestamp activeTo = new java.sql.Timestamp(System.currentTimeMillis()); 805 List<GroupMemberBo> toReAdd = null; 806 807 if (oldGroup.getMembers() != null) { 808 for (GroupMemberBo member : oldGroup.getMembers()) { 809 // if the old member isn't in the new group 810 if (group.getMembers() == null || !group.getMembers().contains(member)) { 811 // inactivate the member 812 member.setActiveToDateValue(activeTo); 813 if (toReAdd == null) { 814 toReAdd = new ArrayList<GroupMemberBo>(); 815 } 816 // queue it up for re-adding 817 toReAdd.add(member); 818 } 819 } 820 } 821 822 // do the re-adding 823 if (toReAdd != null) { 824 List<GroupMemberBo> groupMembers = group.getMembers(); 825 if (groupMembers == null) { 826 groupMembers = new ArrayList<GroupMemberBo>(toReAdd.size()); 827 } 828 group.setMembers(groupMembers); 829 } 830 } 831 } 832 833 return KimImplServiceLocator.getGroupInternalService().saveWorkgroup(group); 834 } 835 836 837 /** 838 * This helper method gets the active group members of the specified type (see {@link org.kuali.rice.kim.api.KimConstants.KimGroupMemberTypes}). 839 * If the optional params are null, it will return all active members for the specified group regardless 840 * of type. 841 * 842 * @param parentId 843 * @param childId optional, but if provided then memberType must be too 844 * @param memberType optional, but must be provided if childId is 845 * @return a list of group members 846 */ 847 private List<GroupMemberBo> getActiveGroupMembers(String parentId, String childId, MemberType memberType) { 848 final java.sql.Date today = new java.sql.Date(System.currentTimeMillis()); 849 850 if (childId != null && memberType == null) { 851 throw new RiceRuntimeException("memberType must be non-null if childId is non-null"); 852 } 853 854 Map<String,Object> criteria = new HashMap<String,Object>(4); 855 criteria.put(KIMPropertyConstants.GroupMember.GROUP_ID, parentId); 856 857 if (childId != null) { 858 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_ID, childId); 859 criteria.put(KIMPropertyConstants.GroupMember.MEMBER_TYPE_CODE, memberType.getCode()); 860 } 861 862 Collection<GroupMemberBo> groupMembers = this.businessObjectService.findMatching(GroupMemberBo.class, criteria); 863 864 CollectionUtils.filter(groupMembers, new Predicate() { 865 @Override public boolean evaluate(Object object) { 866 GroupMemberBo member = (GroupMemberBo) object; 867 // keep in the collection (return true) if the activeToDate is null, or if it is set to a future date 868 return member.getActiveToDate() == null || today.before(member.getActiveToDate().toDate()); 869 } 870 }); 871 872 return new ArrayList<GroupMemberBo>(groupMembers); 873 } 874 875 /** 876 * Sets the businessObjectService attribute value. 877 * 878 * @param businessObjectService The businessObjectService to set. 879 */ 880 public void setBusinessObjectService(final BusinessObjectService businessObjectService) { 881 this.businessObjectService = businessObjectService; 882 } 883 884 /** 885 * Sets the criteriaLookupService attribute value. 886 * 887 * @param criteriaLookupService The criteriaLookupService to set. 888 */ 889 public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) { 890 this.criteriaLookupService = criteriaLookupService; 891 } 892 893 private void incomingParamCheck(Object object, String name) { 894 if (object == null) { 895 throw new RiceIllegalArgumentException(name + " was null"); 896 } else if (object instanceof String 897 && StringUtils.isBlank((String) object)) { 898 throw new RiceIllegalArgumentException(name + " was blank"); 899 } 900 } 901 }