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