001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018 019import java.util.Collections; 020import java.util.LinkedHashSet; 021import java.util.LinkedList; 022import java.util.List; 023import java.util.Set; 024import java.util.concurrent.locks.ReadWriteLock; 025import java.util.concurrent.locks.ReentrantReadWriteLock; 026 027import org.forgerock.i18n.LocalizableMessage; 028import org.forgerock.i18n.LocalizedIllegalArgumentException; 029import org.forgerock.i18n.slf4j.LocalizedLogger; 030import org.forgerock.opendj.config.server.ConfigException; 031import org.forgerock.opendj.ldap.ByteString; 032import org.forgerock.opendj.ldap.DN; 033import org.forgerock.opendj.ldap.DN.CompactDn; 034import org.forgerock.opendj.ldap.ModificationType; 035import org.forgerock.opendj.ldap.ResultCode; 036import org.forgerock.opendj.ldap.SearchScope; 037import org.forgerock.opendj.ldap.schema.AttributeType; 038import org.forgerock.util.Reject; 039import org.opends.server.admin.std.server.GroupImplementationCfg; 040import org.opends.server.admin.std.server.StaticGroupImplementationCfg; 041import org.opends.server.api.Group; 042import org.opends.server.core.DirectoryServer; 043import org.opends.server.core.ModifyOperation; 044import org.opends.server.core.ModifyOperationBasis; 045import org.opends.server.core.ServerContext; 046import org.opends.server.protocols.ldap.LDAPControl; 047import org.opends.server.types.Attribute; 048import org.opends.server.types.Attributes; 049import org.opends.server.types.Control; 050import org.opends.server.types.DirectoryConfig; 051import org.opends.server.types.DirectoryException; 052import org.opends.server.types.Entry; 053import org.opends.server.types.InitializationException; 054import org.opends.server.types.MemberList; 055import org.opends.server.types.MembershipException; 056import org.opends.server.types.Modification; 057import org.opends.server.types.SearchFilter; 058 059import static org.opends.messages.ExtensionMessages.*; 060import static org.opends.server.core.DirectoryServer.*; 061import static org.opends.server.protocols.internal.InternalClientConnection.*; 062import static org.opends.server.util.CollectionUtils.*; 063import static org.opends.server.util.ServerConstants.*; 064 065/** 066 * A static group implementation, in which the DNs of all members are explicitly 067 * listed. 068 * <p> 069 * There are three variants of static groups: 070 * <ul> 071 * <li>one based on the {@code groupOfNames} object class: which stores the 072 * member list in the {@code member} attribute</li> 073 * <li>one based on the {@code groupOfEntries} object class, which also stores 074 * the member list in the {@code member} attribute</li> 075 * <li>one based on the {@code groupOfUniqueNames} object class, which stores 076 * the member list in the {@code uniqueMember} attribute.</li> 077 * </ul> 078 */ 079public class StaticGroup extends Group<StaticGroupImplementationCfg> 080{ 081 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 082 083 /** The attribute type used to hold the membership list for this group. */ 084 private AttributeType memberAttributeType; 085 086 /** The DN of the entry that holds the definition for this group. */ 087 private DN groupEntryDN; 088 089 /** The set of the DNs of the members for this group. */ 090 private LinkedHashSet<CompactDn> memberDNs; 091 092 /** The list of nested group DNs for this group. */ 093 private LinkedList<DN> nestedGroups = new LinkedList<>(); 094 095 /** Passed to the group manager to see if the nested group list needs to be refreshed. */ 096 private long nestedGroupRefreshToken = DirectoryServer.getGroupManager().refreshToken(); 097 098 /** Read/write lock protecting memberDNs and nestedGroups. */ 099 private ReadWriteLock lock = new ReentrantReadWriteLock(); 100 101 private ServerContext serverContext; 102 103 /** 104 * Creates an uninitialized static group. This is intended for internal use 105 * only, to allow {@code GroupManager} to dynamically create a group. 106 */ 107 public StaticGroup() 108 { 109 super(); 110 } 111 112 /** 113 * Creates a new static group instance with the provided information. 114 * 115 * @param groupEntryDN The DN of the entry that holds the definition 116 * for this group. 117 * @param memberAttributeType The attribute type used to hold the membership 118 * list for this group. 119 * @param memberDNs The set of the DNs of the members for this 120 * group. 121 */ 122 private StaticGroup(ServerContext serverContext, DN groupEntryDN, AttributeType memberAttributeType, 123 LinkedHashSet<CompactDn> memberDNs) 124 { 125 super(); 126 Reject.ifNull(groupEntryDN, memberAttributeType, memberDNs); 127 128 this.serverContext = serverContext; 129 this.groupEntryDN = groupEntryDN; 130 this.memberAttributeType = memberAttributeType; 131 this.memberDNs = memberDNs; 132 } 133 134 /** {@inheritDoc} */ 135 @Override 136 public void initializeGroupImplementation(StaticGroupImplementationCfg configuration) 137 throws ConfigException, InitializationException 138 { 139 // No additional initialization is required. 140 } 141 142 /** {@inheritDoc} */ 143 @Override 144 public StaticGroup newInstance(ServerContext serverContext, Entry groupEntry) throws DirectoryException 145 { 146 Reject.ifNull(groupEntry); 147 148 // Determine whether it is a groupOfNames, groupOfEntries or 149 // groupOfUniqueNames entry. If not, then that's a problem. 150 AttributeType someMemberAttributeType; 151 boolean hasGroupOfEntriesClass = hasObjectClass(groupEntry, OC_GROUP_OF_ENTRIES_LC); 152 boolean hasGroupOfNamesClass = hasObjectClass(groupEntry, OC_GROUP_OF_NAMES_LC); 153 boolean hasGroupOfUniqueNamesClass = hasObjectClass(groupEntry, OC_GROUP_OF_UNIQUE_NAMES_LC); 154 if (hasGroupOfEntriesClass) 155 { 156 if (hasGroupOfNamesClass) 157 { 158 LocalizableMessage message = ERR_STATICGROUP_INVALID_OC_COMBINATION.get( 159 groupEntry.getName(), OC_GROUP_OF_ENTRIES, OC_GROUP_OF_NAMES); 160 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message); 161 } 162 else if (hasGroupOfUniqueNamesClass) 163 { 164 LocalizableMessage message = ERR_STATICGROUP_INVALID_OC_COMBINATION.get( 165 groupEntry.getName(), OC_GROUP_OF_ENTRIES, OC_GROUP_OF_UNIQUE_NAMES); 166 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message); 167 } 168 169 someMemberAttributeType = DirectoryServer.getAttributeType(ATTR_MEMBER); 170 } 171 else if (hasGroupOfNamesClass) 172 { 173 if (hasGroupOfUniqueNamesClass) 174 { 175 LocalizableMessage message = ERR_STATICGROUP_INVALID_OC_COMBINATION.get( 176 groupEntry.getName(), OC_GROUP_OF_NAMES, OC_GROUP_OF_UNIQUE_NAMES); 177 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message); 178 } 179 180 someMemberAttributeType = DirectoryServer.getAttributeType(ATTR_MEMBER); 181 } 182 else if (hasGroupOfUniqueNamesClass) 183 { 184 someMemberAttributeType = DirectoryServer.getAttributeType(ATTR_UNIQUE_MEMBER_LC); 185 } 186 else 187 { 188 LocalizableMessage message = 189 ERR_STATICGROUP_NO_VALID_OC.get(groupEntry.getName(), OC_GROUP_OF_NAMES, OC_GROUP_OF_UNIQUE_NAMES); 190 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message); 191 } 192 193 List<Attribute> memberAttrList = groupEntry.getAttribute(someMemberAttributeType); 194 int membersCount = 0; 195 for (Attribute a : memberAttrList) 196 { 197 membersCount += a.size(); 198 } 199 LinkedHashSet<CompactDn> someMemberDNs = new LinkedHashSet<>(membersCount); 200 for (Attribute a : memberAttrList) 201 { 202 for (ByteString v : a) 203 { 204 try 205 { 206 someMemberDNs.add(DN.valueOf(v.toString()).compact()); 207 } 208 catch (LocalizedIllegalArgumentException e) 209 { 210 logger.traceException(e); 211 logger.error(ERR_STATICGROUP_CANNOT_DECODE_MEMBER_VALUE_AS_DN, 212 v, someMemberAttributeType.getNameOrOID(), groupEntry.getName(), e.getMessageObject()); 213 } 214 } 215 } 216 return new StaticGroup(serverContext, groupEntry.getName(), someMemberAttributeType, someMemberDNs); 217 } 218 219 /** {@inheritDoc} */ 220 @Override 221 public SearchFilter getGroupDefinitionFilter() 222 throws DirectoryException 223 { 224 // FIXME -- This needs to exclude enhanced groups once we have support for them. 225 String filterString = 226 "(&(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames)" + 227 "(objectClass=groupOfEntries))" + 228 "(!(objectClass=ds-virtual-static-group)))"; 229 return SearchFilter.createFilterFromString(filterString); 230 } 231 232 /** {@inheritDoc} */ 233 @Override 234 public boolean isGroupDefinition(Entry entry) 235 { 236 Reject.ifNull(entry); 237 238 // FIXME -- This needs to exclude enhanced groups once we have support for them. 239 if (hasObjectClass(entry, OC_VIRTUAL_STATIC_GROUP)) 240 { 241 return false; 242 } 243 244 boolean hasGroupOfEntriesClass = hasObjectClass(entry, OC_GROUP_OF_ENTRIES_LC); 245 boolean hasGroupOfNamesClass = hasObjectClass(entry, OC_GROUP_OF_NAMES_LC); 246 boolean hasGroupOfUniqueNamesClass = hasObjectClass(entry, OC_GROUP_OF_UNIQUE_NAMES_LC); 247 if (hasGroupOfEntriesClass) 248 { 249 return !hasGroupOfNamesClass 250 && !hasGroupOfUniqueNamesClass; 251 } 252 else if (hasGroupOfNamesClass) 253 { 254 return !hasGroupOfUniqueNamesClass; 255 } 256 else 257 { 258 return hasGroupOfUniqueNamesClass; 259 } 260 } 261 262 private boolean hasObjectClass(Entry entry, String ocName) 263 { 264 return entry.hasObjectClass(DirectoryConfig.getObjectClass(ocName, true)); 265 } 266 267 /** {@inheritDoc} */ 268 @Override 269 public DN getGroupDN() 270 { 271 return groupEntryDN; 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 public void setGroupDN(DN groupDN) 277 { 278 groupEntryDN = groupDN; 279 } 280 281 /** {@inheritDoc} */ 282 @Override 283 public boolean supportsNestedGroups() 284 { 285 return true; 286 } 287 288 /** {@inheritDoc} */ 289 @Override 290 public List<DN> getNestedGroupDNs() 291 { 292 try 293 { 294 reloadIfNeeded(); 295 } 296 catch (DirectoryException ex) 297 { 298 return Collections.<DN>emptyList(); 299 } 300 lock.readLock().lock(); 301 try 302 { 303 return nestedGroups; 304 } 305 finally 306 { 307 lock.readLock().unlock(); 308 } 309 } 310 311 /** {@inheritDoc} */ 312 @Override 313 public void addNestedGroup(DN nestedGroupDN) 314 throws UnsupportedOperationException, DirectoryException 315 { 316 Reject.ifNull(nestedGroupDN); 317 318 lock.writeLock().lock(); 319 try 320 { 321 if (nestedGroups.contains(nestedGroupDN)) 322 { 323 LocalizableMessage msg = ERR_STATICGROUP_ADD_NESTED_GROUP_ALREADY_EXISTS.get(nestedGroupDN, groupEntryDN); 324 throw new DirectoryException(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS, msg); 325 } 326 327 ModifyOperation modifyOperation = newModifyOperation(ModificationType.ADD, nestedGroupDN); 328 modifyOperation.run(); 329 if (modifyOperation.getResultCode() != ResultCode.SUCCESS) 330 { 331 LocalizableMessage msg = ERR_STATICGROUP_ADD_MEMBER_UPDATE_FAILED.get( 332 nestedGroupDN, groupEntryDN, modifyOperation.getErrorMessage()); 333 throw new DirectoryException(modifyOperation.getResultCode(), msg); 334 } 335 336 LinkedList<DN> newNestedGroups = new LinkedList<>(nestedGroups); 337 newNestedGroups.add(nestedGroupDN); 338 nestedGroups = newNestedGroups; 339 //Add it to the member DN list. 340 LinkedHashSet<CompactDn> newMemberDNs = new LinkedHashSet<>(memberDNs); 341 newMemberDNs.add(toCompactDn(nestedGroupDN)); 342 memberDNs = newMemberDNs; 343 } 344 finally 345 { 346 lock.writeLock().unlock(); 347 } 348 } 349 350 /** {@inheritDoc} */ 351 @Override 352 public void removeNestedGroup(DN nestedGroupDN) 353 throws UnsupportedOperationException, DirectoryException 354 { 355 Reject.ifNull(nestedGroupDN); 356 357 lock.writeLock().lock(); 358 try 359 { 360 if (! nestedGroups.contains(nestedGroupDN)) 361 { 362 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, 363 ERR_STATICGROUP_REMOVE_NESTED_GROUP_NO_SUCH_GROUP.get(nestedGroupDN, groupEntryDN)); 364 } 365 366 ModifyOperation modifyOperation = newModifyOperation(ModificationType.DELETE, nestedGroupDN); 367 modifyOperation.run(); 368 if (modifyOperation.getResultCode() != ResultCode.SUCCESS) 369 { 370 LocalizableMessage message = ERR_STATICGROUP_REMOVE_MEMBER_UPDATE_FAILED.get( 371 nestedGroupDN, groupEntryDN, modifyOperation.getErrorMessage()); 372 throw new DirectoryException(modifyOperation.getResultCode(), message); 373 } 374 375 LinkedList<DN> newNestedGroups = new LinkedList<>(nestedGroups); 376 newNestedGroups.remove(nestedGroupDN); 377 nestedGroups = newNestedGroups; 378 //Remove it from the member DN list. 379 LinkedHashSet<CompactDn> newMemberDNs = new LinkedHashSet<>(memberDNs); 380 newMemberDNs.remove(toCompactDn(nestedGroupDN)); 381 memberDNs = newMemberDNs; 382 } 383 finally 384 { 385 lock.writeLock().unlock(); 386 } 387 } 388 389 /** {@inheritDoc} */ 390 @Override 391 public boolean isMember(DN userDN, Set<DN> examinedGroups) throws DirectoryException 392 { 393 reloadIfNeeded(); 394 CompactDn compactUserDN = toCompactDn(userDN); 395 lock.readLock().lock(); 396 try 397 { 398 if (memberDNs.contains(compactUserDN)) 399 { 400 return true; 401 } 402 else if (!examinedGroups.add(getGroupDN())) 403 { 404 return false; 405 } 406 else 407 { 408 for (DN nestedGroupDN : nestedGroups) 409 { 410 Group<? extends GroupImplementationCfg> group = getGroupManager().getGroupInstance(nestedGroupDN); 411 if (group != null && group.isMember(userDN, examinedGroups)) 412 { 413 return true; 414 } 415 } 416 } 417 } 418 finally 419 { 420 lock.readLock().unlock(); 421 } 422 return false; 423 } 424 425 /** {@inheritDoc} */ 426 @Override 427 public boolean isMember(Entry userEntry, Set<DN> examinedGroups) 428 throws DirectoryException 429 { 430 return isMember(userEntry.getName(), examinedGroups); 431 } 432 433 /** 434 * Check if the group manager has registered a new group instance or removed a 435 * a group instance that might impact this group's membership list. 436 */ 437 private void reloadIfNeeded() throws DirectoryException 438 { 439 //Check if group instances have changed by passing the group manager 440 //the current token. 441 if (DirectoryServer.getGroupManager().hasInstancesChanged(nestedGroupRefreshToken)) 442 { 443 lock.writeLock().lock(); 444 try 445 { 446 Group<?> thisGroup = DirectoryServer.getGroupManager().getGroupInstance(groupEntryDN); 447 // Check if the group itself has been removed 448 if (thisGroup == null) 449 { 450 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, 451 ERR_STATICGROUP_GROUP_INSTANCE_INVALID.get(groupEntryDN)); 452 } 453 else if (thisGroup != this) 454 { 455 LinkedHashSet<CompactDn> newMemberDNs = new LinkedHashSet<>(); 456 MemberList memberList = thisGroup.getMembers(); 457 while (memberList.hasMoreMembers()) 458 { 459 try 460 { 461 newMemberDNs.add(toCompactDn(memberList.nextMemberDN())); 462 } 463 catch (MembershipException ex) 464 { 465 // TODO: should we throw an exception there instead of silently fail ? 466 } 467 } 468 memberDNs = newMemberDNs; 469 } 470 nestedGroups.clear(); 471 for (CompactDn compactDn : memberDNs) 472 { 473 DN dn = fromCompactDn(compactDn); 474 Group<?> group = DirectoryServer.getGroupManager().getGroupInstance(dn); 475 if (group != null) 476 { 477 nestedGroups.add(group.getGroupDN()); 478 } 479 } 480 nestedGroupRefreshToken = DirectoryServer.getGroupManager().refreshToken(); 481 } 482 finally 483 { 484 lock.writeLock().unlock(); 485 } 486 } 487 } 488 489 /** {@inheritDoc} */ 490 @Override 491 public MemberList getMembers() throws DirectoryException 492 { 493 reloadIfNeeded(); 494 lock.readLock().lock(); 495 try 496 { 497 return new SimpleStaticGroupMemberList(groupEntryDN, memberDNs); 498 } 499 finally 500 { 501 lock.readLock().unlock(); 502 } 503 } 504 505 /** {@inheritDoc} */ 506 @Override 507 public MemberList getMembers(DN baseDN, SearchScope scope, SearchFilter filter) throws DirectoryException 508 { 509 reloadIfNeeded(); 510 lock.readLock().lock(); 511 try 512 { 513 if (baseDN == null && filter == null) 514 { 515 return new SimpleStaticGroupMemberList(groupEntryDN, memberDNs); 516 } 517 return new FilteredStaticGroupMemberList(groupEntryDN, memberDNs, baseDN, scope, filter); 518 } 519 finally 520 { 521 lock.readLock().unlock(); 522 } 523 } 524 525 /** {@inheritDoc} */ 526 @Override 527 public boolean mayAlterMemberList() 528 { 529 return true; 530 } 531 532 /** {@inheritDoc} */ 533 @Override 534 public void updateMembers(List<Modification> modifications) 535 throws UnsupportedOperationException, DirectoryException 536 { 537 Reject.ifNull(memberDNs); 538 Reject.ifNull(nestedGroups); 539 540 reloadIfNeeded(); 541 lock.writeLock().lock(); 542 try 543 { 544 for (Modification mod : modifications) 545 { 546 Attribute attribute = mod.getAttribute(); 547 if (attribute.getAttributeDescription().getAttributeType().equals(memberAttributeType)) 548 { 549 switch (mod.getModificationType().asEnum()) 550 { 551 case ADD: 552 for (ByteString v : attribute) 553 { 554 DN member = DN.valueOf(v); 555 memberDNs.add(toCompactDn(member)); 556 if (DirectoryServer.getGroupManager().getGroupInstance(member) != null) 557 { 558 nestedGroups.add(member); 559 } 560 } 561 break; 562 case DELETE: 563 if (attribute.isEmpty()) 564 { 565 memberDNs.clear(); 566 nestedGroups.clear(); 567 } 568 else 569 { 570 for (ByteString v : attribute) 571 { 572 DN member = DN.valueOf(v); 573 memberDNs.remove(toCompactDn(member)); 574 nestedGroups.remove(member); 575 } 576 } 577 break; 578 case REPLACE: 579 memberDNs.clear(); 580 nestedGroups.clear(); 581 for (ByteString v : attribute) 582 { 583 DN member = DN.valueOf(v); 584 memberDNs.add(toCompactDn(member)); 585 if (DirectoryServer.getGroupManager().getGroupInstance(member) != null) 586 { 587 nestedGroups.add(member); 588 } 589 } 590 break; 591 } 592 } 593 } 594 } 595 finally { 596 lock.writeLock().unlock(); 597 } 598 } 599 600 /** {@inheritDoc} */ 601 @Override 602 public void addMember(Entry userEntry) throws UnsupportedOperationException, DirectoryException 603 { 604 Reject.ifNull(userEntry); 605 606 lock.writeLock().lock(); 607 try 608 { 609 DN userDN = userEntry.getName(); 610 CompactDn compactUserDN = toCompactDn(userDN); 611 612 if (memberDNs.contains(compactUserDN)) 613 { 614 LocalizableMessage message = ERR_STATICGROUP_ADD_MEMBER_ALREADY_EXISTS.get(userDN, groupEntryDN); 615 throw new DirectoryException(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS, message); 616 } 617 618 ModifyOperation modifyOperation = newModifyOperation(ModificationType.ADD, userDN); 619 modifyOperation.run(); 620 if (modifyOperation.getResultCode() != ResultCode.SUCCESS) 621 { 622 throw new DirectoryException(modifyOperation.getResultCode(), 623 ERR_STATICGROUP_ADD_MEMBER_UPDATE_FAILED.get(userDN, groupEntryDN, modifyOperation.getErrorMessage())); 624 } 625 626 LinkedHashSet<CompactDn> newMemberDNs = new LinkedHashSet<CompactDn>(memberDNs); 627 newMemberDNs.add(compactUserDN); 628 memberDNs = newMemberDNs; 629 } 630 finally 631 { 632 lock.writeLock().unlock(); 633 } 634 } 635 636 /** {@inheritDoc} */ 637 @Override 638 public void removeMember(DN userDN) throws UnsupportedOperationException, DirectoryException 639 { 640 Reject.ifNull(userDN); 641 642 CompactDn compactUserDN = toCompactDn(userDN); 643 lock.writeLock().lock(); 644 try 645 { 646 if (! memberDNs.contains(compactUserDN)) 647 { 648 LocalizableMessage message = ERR_STATICGROUP_REMOVE_MEMBER_NO_SUCH_MEMBER.get(userDN, groupEntryDN); 649 throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, message); 650 } 651 652 ModifyOperation modifyOperation = newModifyOperation(ModificationType.DELETE, userDN); 653 modifyOperation.run(); 654 if (modifyOperation.getResultCode() != ResultCode.SUCCESS) 655 { 656 throw new DirectoryException(modifyOperation.getResultCode(), 657 ERR_STATICGROUP_REMOVE_MEMBER_UPDATE_FAILED.get(userDN, groupEntryDN, modifyOperation.getErrorMessage())); 658 } 659 660 LinkedHashSet<CompactDn> newMemberDNs = new LinkedHashSet<>(memberDNs); 661 newMemberDNs.remove(compactUserDN); 662 memberDNs = newMemberDNs; 663 //If it is in the nested group list remove it. 664 if (nestedGroups.contains(userDN)) 665 { 666 LinkedList<DN> newNestedGroups = new LinkedList<>(nestedGroups); 667 newNestedGroups.remove(userDN); 668 nestedGroups = newNestedGroups; 669 } 670 } 671 finally 672 { 673 lock.writeLock().unlock(); 674 } 675 } 676 677 private ModifyOperation newModifyOperation(ModificationType modType, DN userDN) 678 { 679 Attribute attr = Attributes.create(memberAttributeType, userDN.toString()); 680 LinkedList<Modification> mods = newLinkedList(new Modification(modType, attr)); 681 Control control = new LDAPControl(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE, false); 682 683 return new ModifyOperationBasis(getRootConnection(), nextOperationID(), nextMessageID(), 684 newLinkedList(control), groupEntryDN, mods); 685 } 686 687 /** {@inheritDoc} */ 688 @Override 689 public void toString(StringBuilder buffer) 690 { 691 buffer.append("StaticGroup("); 692 buffer.append(groupEntryDN); 693 buffer.append(")"); 694 } 695 696 /** 697 * Convert the provided DN to a compact DN. 698 * 699 * @param dn 700 * The DN 701 * @return the compact representation of the DN 702 */ 703 private CompactDn toCompactDn(DN dn) 704 { 705 return dn.compact(); 706 } 707 708 /** 709 * Convert the provided compact DN to a DN. 710 * 711 * @param compactDn 712 * Compact representation of a DN 713 * @return the regular DN 714 */ 715 static DN fromCompactDn(CompactDn compactDn) 716 { 717 return compactDn.toDn(); 718 } 719} 720