001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2009-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import java.util.*; 030import java.util.concurrent.CopyOnWriteArrayList; 031import java.util.concurrent.locks.ReadWriteLock; 032import java.util.concurrent.locks.ReentrantReadWriteLock; 033 034import org.forgerock.i18n.slf4j.LocalizedLogger; 035import org.forgerock.opendj.ldap.ResultCode; 036import org.forgerock.opendj.ldap.SearchScope; 037import org.opends.server.api.Backend; 038import org.opends.server.api.BackendInitializationListener; 039import org.opends.server.api.ClientConnection; 040import org.opends.server.api.DITCacheMap; 041import org.opends.server.api.SubentryChangeListener; 042import org.opends.server.api.plugin.InternalDirectoryServerPlugin; 043import org.opends.server.api.plugin.PluginResult; 044import org.opends.server.api.plugin.PluginResult.PostOperation; 045import org.opends.server.api.plugin.PluginResult.PreOperation; 046import org.opends.server.api.plugin.PluginType; 047import org.opends.server.controls.SubentriesControl; 048import org.opends.server.protocols.internal.InternalClientConnection; 049import org.opends.server.protocols.internal.InternalSearchOperation; 050import org.opends.server.protocols.internal.SearchRequest; 051import org.opends.server.types.*; 052import org.opends.server.types.operation.PostOperationAddOperation; 053import org.opends.server.types.operation.PostOperationDeleteOperation; 054import org.opends.server.types.operation.PostOperationModifyDNOperation; 055import org.opends.server.types.operation.PostOperationModifyOperation; 056import org.opends.server.types.operation.PostSynchronizationAddOperation; 057import org.opends.server.types.operation.PostSynchronizationDeleteOperation; 058import org.opends.server.types.operation.PostSynchronizationModifyDNOperation; 059import org.opends.server.types.operation.PostSynchronizationModifyOperation; 060import org.opends.server.types.operation.PreOperationAddOperation; 061import org.opends.server.types.operation.PreOperationDeleteOperation; 062import org.opends.server.types.operation.PreOperationModifyDNOperation; 063import org.opends.server.types.operation.PreOperationModifyOperation; 064import org.opends.server.workflowelement.localbackend.LocalBackendSearchOperation; 065 066import static org.opends.messages.CoreMessages.*; 067import static org.opends.server.config.ConfigConstants.*; 068import static org.opends.server.protocols.internal.InternalClientConnection.*; 069import static org.opends.server.protocols.internal.Requests.*; 070import static org.opends.server.util.CollectionUtils.*; 071import static org.opends.server.util.ServerConstants.*; 072 073/** 074 * This class provides a mechanism for interacting with subentries defined in 075 * the Directory Server. It will handle all necessary processing at server 076 * startup to identify and load subentries within the server. 077 * <BR><BR> 078 * FIXME: At the present time, it assumes that all of the necessary 079 * information about subentries defined in the server can be held in 080 * memory. If it is determined that this approach is not workable 081 * in all cases, then we will need an alternate strategy. 082 */ 083public class SubentryManager extends InternalDirectoryServerPlugin 084 implements BackendInitializationListener 085{ 086 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 087 088 /** A mapping between the DNs and applicable subentries. */ 089 private Map<DN,List<SubEntry>> dn2SubEntry; 090 091 /** A mapping between the DNs and applicable collective subentries. */ 092 private Map<DN,List<SubEntry>> dn2CollectiveSubEntry; 093 094 /** A mapping between subentry DNs and subentry objects. */ 095 private DITCacheMap<SubEntry> dit2SubEntry; 096 097 /** Internal search all operational attributes. */ 098 private Set<String> requestAttrs; 099 100 /** Lock to protect internal data structures. */ 101 private final ReadWriteLock lock; 102 103 /** The set of change notification listeners. */ 104 private List<SubentryChangeListener> changeListeners; 105 106 /** Dummy configuration DN for Subentry Manager. */ 107 private static final String CONFIG_DN = "cn=Subentry Manager,cn=config"; 108 109 /** 110 * Creates a new instance of this subentry manager. 111 * 112 * @throws DirectoryException If a problem occurs while 113 * creating an instance of 114 * the subentry manager. 115 */ 116 public SubentryManager() throws DirectoryException 117 { 118 super(DN.valueOf(CONFIG_DN), EnumSet.of( 119 PluginType.PRE_OPERATION_ADD, 120 PluginType.PRE_OPERATION_DELETE, 121 PluginType.PRE_OPERATION_MODIFY, 122 PluginType.PRE_OPERATION_MODIFY_DN, 123 PluginType.POST_OPERATION_ADD, 124 PluginType.POST_OPERATION_DELETE, 125 PluginType.POST_OPERATION_MODIFY, 126 PluginType.POST_OPERATION_MODIFY_DN, 127 PluginType.POST_SYNCHRONIZATION_ADD, 128 PluginType.POST_SYNCHRONIZATION_DELETE, 129 PluginType.POST_SYNCHRONIZATION_MODIFY, 130 PluginType.POST_SYNCHRONIZATION_MODIFY_DN), 131 true); 132 133 lock = new ReentrantReadWriteLock(); 134 135 dn2SubEntry = new HashMap<>(); 136 dn2CollectiveSubEntry = new HashMap<>(); 137 dit2SubEntry = new DITCacheMap<>(); 138 changeListeners = new CopyOnWriteArrayList<>(); 139 requestAttrs = newLinkedHashSet("*", "+"); 140 141 DirectoryServer.registerInternalPlugin(this); 142 DirectoryServer.registerBackendInitializationListener(this); 143 } 144 145 /** 146 * Perform any required finalization tasks for Subentry Manager. 147 * This should only be called at Directory Server shutdown. 148 */ 149 public void finalizeSubentryManager() 150 { 151 // Deregister as internal plugin and 152 // backend initialization listener. 153 DirectoryServer.deregisterInternalPlugin(this); 154 DirectoryServer.deregisterBackendInitializationListener(this); 155 } 156 157 /** 158 * Registers the provided change notification listener with this manager 159 * so that it will be notified of any add, delete, modify, or modify DN 160 * operations that are performed. 161 * 162 * @param changeListener The change notification listener to register 163 * with this manager. 164 */ 165 public void registerChangeListener( 166 SubentryChangeListener changeListener) 167 { 168 changeListeners.add(changeListener); 169 } 170 171 /** 172 * Deregisters the provided change notification listener with this manager 173 * so that it will no longer be notified of any add, delete, modify, or 174 * modify DN operations that are performed. 175 * 176 * @param changeListener The change notification listener to deregister 177 * with this manager. 178 */ 179 public void deregisterChangeListener( 180 SubentryChangeListener changeListener) 181 { 182 changeListeners.remove(changeListener); 183 } 184 185 /** 186 * Add a given entry to this subentry manager. 187 * @param entry to add. 188 */ 189 private void addSubentry(Entry entry) throws DirectoryException 190 { 191 SubEntry subEntry = new SubEntry(entry); 192 SubtreeSpecification subSpec = 193 subEntry.getSubTreeSpecification(); 194 DN subDN = subSpec.getBaseDN(); 195 List<SubEntry> subList = null; 196 lock.writeLock().lock(); 197 try 198 { 199 if (subEntry.isCollective() || subEntry.isInheritedCollective()) 200 { 201 subList = dn2CollectiveSubEntry.get(subDN); 202 } 203 else 204 { 205 subList = dn2SubEntry.get(subDN); 206 } 207 if (subList == null) 208 { 209 subList = new ArrayList<>(); 210 if (subEntry.isCollective() || subEntry.isInheritedCollective()) 211 { 212 dn2CollectiveSubEntry.put(subDN, subList); 213 } 214 else 215 { 216 dn2SubEntry.put(subDN, subList); 217 } 218 } 219 dit2SubEntry.put(entry.getName(), subEntry); 220 subList.add(subEntry); 221 } 222 finally 223 { 224 lock.writeLock().unlock(); 225 } 226 } 227 228 /** 229 * Remove a given entry from this subentry manager. 230 * @param entry to remove. 231 */ 232 private void removeSubentry(Entry entry) 233 { 234 lock.writeLock().lock(); 235 try 236 { 237 boolean removed = false; 238 Iterator<Map.Entry<DN, List<SubEntry>>> setIterator = 239 dn2SubEntry.entrySet().iterator(); 240 while (setIterator.hasNext()) 241 { 242 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 243 List<SubEntry> subList = mapEntry.getValue(); 244 Iterator<SubEntry> listIterator = subList.iterator(); 245 while (listIterator.hasNext()) 246 { 247 SubEntry subEntry = listIterator.next(); 248 if (subEntry.getDN().equals(entry.getName())) 249 { 250 dit2SubEntry.remove(entry.getName()); 251 listIterator.remove(); 252 removed = true; 253 break; 254 } 255 } 256 if (subList.isEmpty()) 257 { 258 setIterator.remove(); 259 } 260 if (removed) 261 { 262 return; 263 } 264 } 265 setIterator = dn2CollectiveSubEntry.entrySet().iterator(); 266 while (setIterator.hasNext()) 267 { 268 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 269 List<SubEntry> subList = mapEntry.getValue(); 270 Iterator<SubEntry> listIterator = subList.iterator(); 271 while (listIterator.hasNext()) 272 { 273 SubEntry subEntry = listIterator.next(); 274 if (subEntry.getDN().equals(entry.getName())) 275 { 276 dit2SubEntry.remove(entry.getName()); 277 listIterator.remove(); 278 removed = true; 279 break; 280 } 281 } 282 if (subList.isEmpty()) 283 { 284 setIterator.remove(); 285 } 286 if (removed) 287 { 288 return; 289 } 290 } 291 } 292 finally 293 { 294 lock.writeLock().unlock(); 295 } 296 } 297 298 /** 299 * {@inheritDoc} In this case, the server will search the backend to find 300 * all subentries that it may contain and register them with this manager. 301 */ 302 @Override 303 public void performBackendInitializationProcessing(Backend<?> backend) 304 { 305 InternalClientConnection conn = getRootConnection(); 306 SubentriesControl control = new SubentriesControl(true, true); 307 308 SearchFilter filter = null; 309 try 310 { 311 filter = SearchFilter.createFilterFromString("(|" + 312 "(" + ATTR_OBJECTCLASS + "=" + OC_SUBENTRY + ")" + 313 "(" + ATTR_OBJECTCLASS + "=" + OC_LDAP_SUBENTRY + ")" + 314 ")"); 315 if (backend.getEntryCount() > 0 && ! backend.isIndexed(filter)) 316 { 317 logger.warn(WARN_SUBENTRY_FILTER_NOT_INDEXED, filter, backend.getBackendID()); 318 } 319 } 320 catch (Exception e) 321 { 322 logger.traceException(e); 323 } 324 325 for (DN baseDN : backend.getBaseDNs()) 326 { 327 try 328 { 329 if (! backend.entryExists(baseDN)) 330 { 331 continue; 332 } 333 } 334 catch (Exception e) 335 { 336 logger.traceException(e); 337 338 // FIXME -- Is there anything that we need to do here? 339 continue; 340 } 341 342 SearchRequest request = newSearchRequest(baseDN, SearchScope.WHOLE_SUBTREE, filter) 343 .addAttribute(requestAttrs) 344 .addControl(control); 345 InternalSearchOperation internalSearch = 346 new InternalSearchOperation(conn, nextOperationID(), nextMessageID(), request); 347 LocalBackendSearchOperation localSearch = new LocalBackendSearchOperation(internalSearch); 348 349 try 350 { 351 backend.search(localSearch); 352 } 353 catch (Exception e) 354 { 355 logger.traceException(e); 356 357 // FIXME -- Is there anything that we need to do here? 358 continue; 359 } 360 361 for (SearchResultEntry entry : internalSearch.getSearchEntries()) 362 { 363 if (entry.isSubentry() || entry.isLDAPSubentry()) 364 { 365 try 366 { 367 addSubentry(entry); 368 369 // Notify change listeners. 370 for (SubentryChangeListener changeListener : 371 changeListeners) 372 { 373 try 374 { 375 changeListener.handleSubentryAdd(entry); 376 } 377 catch (Exception e) 378 { 379 logger.traceException(e); 380 } 381 } 382 } 383 catch (Exception e) 384 { 385 logger.traceException(e); 386 387 // FIXME -- Handle this. 388 continue; 389 } 390 } 391 } 392 } 393 } 394 395 /** 396 * Return all subentries for this manager. 397 * Note that this getter will skip any collective subentries, 398 * returning only applicable regular subentries. 399 * @return all subentries for this manager. 400 */ 401 public List<SubEntry> getSubentries() 402 { 403 if (dn2SubEntry.isEmpty()) 404 { 405 return Collections.emptyList(); 406 } 407 408 List<SubEntry> subentries = new ArrayList<>(); 409 410 lock.readLock().lock(); 411 try 412 { 413 for (List<SubEntry> subList : dn2SubEntry.values()) 414 { 415 subentries.addAll(subList); 416 } 417 } 418 finally 419 { 420 lock.readLock().unlock(); 421 } 422 423 return subentries; 424 } 425 426 /** 427 * Return subentries applicable to specific DN. 428 * Note that this getter will skip any collective subentries, 429 * returning only applicable regular subentries. 430 * @param dn for which to retrieve applicable 431 * subentries. 432 * @return applicable subentries. 433 */ 434 public List<SubEntry> getSubentries(DN dn) 435 { 436 if (dn2SubEntry.isEmpty()) 437 { 438 return Collections.emptyList(); 439 } 440 441 List<SubEntry> subentries = new ArrayList<>(); 442 lock.readLock().lock(); 443 try 444 { 445 for (DN subDN = dn; subDN != null; 446 subDN = subDN.parent()) 447 { 448 List<SubEntry> subList = dn2SubEntry.get(subDN); 449 if (subList != null) 450 { 451 for (SubEntry subEntry : subList) 452 { 453 SubtreeSpecification subSpec = 454 subEntry.getSubTreeSpecification(); 455 if (subSpec.isDNWithinScope(dn)) 456 { 457 subentries.add(subEntry); 458 } 459 } 460 } 461 } 462 } 463 finally 464 { 465 lock.readLock().unlock(); 466 } 467 468 return subentries; 469 } 470 471 /** 472 * Return subentries applicable to specific entry. 473 * Note that this getter will skip any collective subentries, 474 * returning only applicable regular subentries. 475 * @param entry for which to retrieve applicable 476 * subentries. 477 * @return applicable subentries. 478 */ 479 public List<SubEntry> getSubentries(Entry entry) 480 { 481 if (dn2SubEntry.isEmpty()) 482 { 483 return Collections.emptyList(); 484 } 485 486 List<SubEntry> subentries = new ArrayList<>(); 487 488 lock.readLock().lock(); 489 try 490 { 491 for (DN subDN = entry.getName(); subDN != null; 492 subDN = subDN.parent()) 493 { 494 List<SubEntry> subList = dn2SubEntry.get(subDN); 495 if (subList != null) 496 { 497 for (SubEntry subEntry : subList) 498 { 499 SubtreeSpecification subSpec = 500 subEntry.getSubTreeSpecification(); 501 if (subSpec.isWithinScope(entry)) 502 { 503 subentries.add(subEntry); 504 } 505 } 506 } 507 } 508 } 509 finally 510 { 511 lock.readLock().unlock(); 512 } 513 514 return subentries; 515 } 516 517 /** 518 * Return collective subentries applicable to specific DN. 519 * Note that this getter will skip any regular subentries, 520 * returning only applicable collective subentries. 521 * @param dn for which to retrieve applicable 522 * subentries. 523 * @return applicable subentries. 524 */ 525 public List<SubEntry> getCollectiveSubentries(DN dn) 526 { 527 if (dn2CollectiveSubEntry.isEmpty()) 528 { 529 return Collections.emptyList(); 530 } 531 532 List<SubEntry> subentries = new ArrayList<>(); 533 534 lock.readLock().lock(); 535 try 536 { 537 for (DN subDN = dn; subDN != null; 538 subDN = subDN.parent()) 539 { 540 List<SubEntry> subList = dn2CollectiveSubEntry.get(subDN); 541 if (subList != null) 542 { 543 for (SubEntry subEntry : subList) 544 { 545 SubtreeSpecification subSpec = 546 subEntry.getSubTreeSpecification(); 547 if (subSpec.isDNWithinScope(dn)) 548 { 549 subentries.add(subEntry); 550 } 551 } 552 } 553 } 554 } 555 finally 556 { 557 lock.readLock().unlock(); 558 } 559 560 return subentries; 561 } 562 563 /** 564 * Return collective subentries applicable to specific entry. 565 * Note that this getter will skip any regular subentries, 566 * returning only applicable collective subentries. 567 * @param entry for which to retrieve applicable 568 * subentries. 569 * @return applicable subentries. 570 */ 571 public List<SubEntry> getCollectiveSubentries(Entry entry) 572 { 573 if (dn2CollectiveSubEntry.isEmpty()) 574 { 575 return Collections.emptyList(); 576 } 577 578 List<SubEntry> subentries = new ArrayList<>(); 579 580 lock.readLock().lock(); 581 try 582 { 583 for (DN subDN = entry.getName(); subDN != null; 584 subDN = subDN.parent()) 585 { 586 List<SubEntry> subList = dn2CollectiveSubEntry.get(subDN); 587 if (subList != null) 588 { 589 for (SubEntry subEntry : subList) 590 { 591 SubtreeSpecification subSpec = 592 subEntry.getSubTreeSpecification(); 593 if (subSpec.isWithinScope(entry)) 594 { 595 subentries.add(subEntry); 596 } 597 } 598 } 599 } 600 } 601 finally 602 { 603 lock.readLock().unlock(); 604 } 605 606 return subentries; 607 } 608 609 /** 610 * {@inheritDoc} In this case, the server will de-register 611 * all subentries associated with the provided backend. 612 */ 613 @Override 614 public void performBackendFinalizationProcessing(Backend<?> backend) 615 { 616 lock.writeLock().lock(); 617 try 618 { 619 Iterator<Map.Entry<DN, List<SubEntry>>> setIterator = 620 dn2SubEntry.entrySet().iterator(); 621 while (setIterator.hasNext()) 622 { 623 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 624 List<SubEntry> subList = mapEntry.getValue(); 625 Iterator<SubEntry> listIterator = subList.iterator(); 626 while (listIterator.hasNext()) 627 { 628 SubEntry subEntry = listIterator.next(); 629 if (backend.handlesEntry(subEntry.getDN())) 630 { 631 dit2SubEntry.remove(subEntry.getDN()); 632 listIterator.remove(); 633 634 // Notify change listeners. 635 for (SubentryChangeListener changeListener : 636 changeListeners) 637 { 638 try 639 { 640 changeListener.handleSubentryDelete( 641 subEntry.getEntry()); 642 } 643 catch (Exception e) 644 { 645 logger.traceException(e); 646 } 647 } 648 } 649 } 650 if (subList.isEmpty()) 651 { 652 setIterator.remove(); 653 } 654 } 655 setIterator = dn2CollectiveSubEntry.entrySet().iterator(); 656 while (setIterator.hasNext()) 657 { 658 Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next(); 659 List<SubEntry> subList = mapEntry.getValue(); 660 Iterator<SubEntry> listIterator = subList.iterator(); 661 while (listIterator.hasNext()) 662 { 663 SubEntry subEntry = listIterator.next(); 664 if (backend.handlesEntry(subEntry.getDN())) 665 { 666 dit2SubEntry.remove(subEntry.getDN()); 667 listIterator.remove(); 668 669 // Notify change listeners. 670 for (SubentryChangeListener changeListener : 671 changeListeners) 672 { 673 try 674 { 675 changeListener.handleSubentryDelete( 676 subEntry.getEntry()); 677 } 678 catch (Exception e) 679 { 680 logger.traceException(e); 681 } 682 } 683 } 684 } 685 if (subList.isEmpty()) 686 { 687 setIterator.remove(); 688 } 689 } 690 } 691 finally 692 { 693 lock.writeLock().unlock(); 694 } 695 } 696 697 private void doPostAdd(Entry entry) 698 { 699 if (entry.isSubentry() || entry.isLDAPSubentry()) 700 { 701 lock.writeLock().lock(); 702 try 703 { 704 try 705 { 706 addSubentry(entry); 707 708 // Notify change listeners. 709 for (SubentryChangeListener changeListener : 710 changeListeners) 711 { 712 try 713 { 714 changeListener.handleSubentryAdd(entry); 715 } 716 catch (Exception e) 717 { 718 logger.traceException(e); 719 } 720 } 721 } 722 catch (Exception e) 723 { 724 logger.traceException(e); 725 726 // FIXME -- Handle this. 727 } 728 } 729 finally 730 { 731 lock.writeLock().unlock(); 732 } 733 } 734 } 735 736 private void doPostDelete(Entry entry) 737 { 738 lock.writeLock().lock(); 739 try 740 { 741 for (SubEntry subEntry : dit2SubEntry.getSubtree(entry.getName())) 742 { 743 removeSubentry(subEntry.getEntry()); 744 745 // Notify change listeners. 746 for (SubentryChangeListener changeListener : 747 changeListeners) 748 { 749 try 750 { 751 changeListener.handleSubentryDelete(subEntry.getEntry()); 752 } 753 catch (Exception e) 754 { 755 logger.traceException(e); 756 } 757 } 758 } 759 } 760 finally 761 { 762 lock.writeLock().unlock(); 763 } 764 } 765 766 private void doPostModify(Entry oldEntry, Entry newEntry) 767 { 768 boolean notify = false; 769 770 lock.writeLock().lock(); 771 try 772 { 773 if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry()) 774 { 775 removeSubentry(oldEntry); 776 notify = true; 777 } 778 if (newEntry.isSubentry() || newEntry.isLDAPSubentry()) 779 { 780 try 781 { 782 addSubentry(newEntry); 783 notify = true; 784 } 785 catch (Exception e) 786 { 787 logger.traceException(e); 788 789 // FIXME -- Handle this. 790 } 791 } 792 793 if (notify) 794 { 795 // Notify change listeners. 796 for (SubentryChangeListener changeListener : 797 changeListeners) 798 { 799 try 800 { 801 changeListener.handleSubentryModify( 802 oldEntry, newEntry); 803 } 804 catch (Exception e) 805 { 806 logger.traceException(e); 807 } 808 } 809 } 810 } 811 finally 812 { 813 lock.writeLock().unlock(); 814 } 815 } 816 817 private void doPostModifyDN(final Entry oldEntry, final Entry newEntry) 818 { 819 lock.writeLock().lock(); 820 try 821 { 822 Collection<SubEntry> setToDelete = dit2SubEntry.getSubtree(oldEntry.getName()); 823 for (SubEntry subentry : setToDelete) 824 { 825 final Entry currentSubentry = subentry.getEntry(); 826 removeSubentry(currentSubentry); 827 828 Entry renamedSubentry = null; 829 try 830 { 831 renamedSubentry = currentSubentry.duplicate(false); 832 final DN renamedDN = currentSubentry.getName().rename(oldEntry.getName(), newEntry.getName()); 833 renamedSubentry.setDN(renamedDN); 834 addSubentry(renamedSubentry); 835 } 836 catch (Exception e) 837 { 838 // Shouldnt happen. 839 logger.traceException(e); 840 } 841 842 // Notify change listeners. 843 for (SubentryChangeListener changeListener : 844 changeListeners) 845 { 846 try 847 { 848 changeListener.handleSubentryModify(currentSubentry, renamedSubentry); 849 } 850 catch (Exception e) 851 { 852 logger.traceException(e); 853 } 854 } 855 } 856 } 857 finally 858 { 859 lock.writeLock().unlock(); 860 } 861 } 862 863 /** {@inheritDoc} */ 864 @Override 865 public PreOperation doPreOperation( 866 PreOperationAddOperation addOperation) 867 { 868 Entry entry = addOperation.getEntryToAdd(); 869 870 if (entry.isSubentry() || entry.isLDAPSubentry()) 871 { 872 ClientConnection conn = addOperation.getClientConnection(); 873 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 874 conn.getOperationInProgress( 875 addOperation.getMessageID()))) 876 { 877 return PluginResult.PreOperation.stopProcessing( 878 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 879 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 880 } 881 for (SubentryChangeListener changeListener : 882 changeListeners) 883 { 884 try 885 { 886 changeListener.checkSubentryAddAcceptable(entry); 887 } 888 catch (DirectoryException de) 889 { 890 logger.traceException(de); 891 892 return PluginResult.PreOperation.stopProcessing( 893 de.getResultCode(), de.getMessageObject()); 894 } 895 } 896 } 897 898 return PluginResult.PreOperation.continueOperationProcessing(); 899 } 900 901 /** {@inheritDoc} */ 902 @Override 903 public PreOperation doPreOperation( 904 PreOperationDeleteOperation deleteOperation) 905 { 906 Entry entry = deleteOperation.getEntryToDelete(); 907 boolean hasSubentryWritePrivilege = false; 908 909 lock.readLock().lock(); 910 try 911 { 912 for (SubEntry subEntry : dit2SubEntry.getSubtree(entry.getName())) 913 { 914 if (!hasSubentryWritePrivilege) 915 { 916 ClientConnection conn = deleteOperation.getClientConnection(); 917 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 918 conn.getOperationInProgress( 919 deleteOperation.getMessageID()))) 920 { 921 return PluginResult.PreOperation.stopProcessing( 922 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 923 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 924 } 925 hasSubentryWritePrivilege = true; 926 } 927 for (SubentryChangeListener changeListener : 928 changeListeners) 929 { 930 try 931 { 932 changeListener.checkSubentryDeleteAcceptable( 933 subEntry.getEntry()); 934 } 935 catch (DirectoryException de) 936 { 937 logger.traceException(de); 938 939 return PluginResult.PreOperation.stopProcessing( 940 de.getResultCode(), de.getMessageObject()); 941 } 942 } 943 } 944 } 945 finally 946 { 947 lock.readLock().unlock(); 948 } 949 950 return PluginResult.PreOperation.continueOperationProcessing(); 951 } 952 953 /** {@inheritDoc} */ 954 @Override 955 public PreOperation doPreOperation( 956 PreOperationModifyOperation modifyOperation) 957 { 958 Entry oldEntry = modifyOperation.getCurrentEntry(); 959 Entry newEntry = modifyOperation.getModifiedEntry(); 960 961 if (newEntry.isSubentry() || newEntry.isLDAPSubentry() || 962 oldEntry.isSubentry() || oldEntry.isLDAPSubentry()) 963 { 964 ClientConnection conn = modifyOperation.getClientConnection(); 965 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 966 conn.getOperationInProgress( 967 modifyOperation.getMessageID()))) 968 { 969 return PluginResult.PreOperation.stopProcessing( 970 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 971 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 972 } 973 for (SubentryChangeListener changeListener : 974 changeListeners) 975 { 976 try 977 { 978 changeListener.checkSubentryModifyAcceptable( 979 oldEntry, newEntry); 980 } 981 catch (DirectoryException de) 982 { 983 logger.traceException(de); 984 985 return PluginResult.PreOperation.stopProcessing( 986 de.getResultCode(), de.getMessageObject()); 987 } 988 } 989 } 990 991 return PluginResult.PreOperation.continueOperationProcessing(); 992 } 993 994 /** {@inheritDoc} */ 995 @Override 996 public PreOperation doPreOperation(PreOperationModifyDNOperation modifyDNOperation) 997 { 998 boolean hasSubentryWritePrivilege = false; 999 1000 lock.readLock().lock(); 1001 try 1002 { 1003 final Entry oldEntry = modifyDNOperation.getOriginalEntry(); 1004 Collection<SubEntry> setToDelete = 1005 dit2SubEntry.getSubtree(oldEntry.getName()); 1006 for (SubEntry subentry : setToDelete) 1007 { 1008 if (!hasSubentryWritePrivilege) 1009 { 1010 ClientConnection conn = modifyDNOperation.getClientConnection(); 1011 if (!conn.hasPrivilege(Privilege.SUBENTRY_WRITE, 1012 conn.getOperationInProgress( 1013 modifyDNOperation.getMessageID()))) 1014 { 1015 return PluginResult.PreOperation.stopProcessing( 1016 ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 1017 ERR_SUBENTRY_WRITE_INSUFFICIENT_PRIVILEGES.get()); 1018 } 1019 hasSubentryWritePrivilege = true; 1020 } 1021 1022 final Entry newEntry = modifyDNOperation.getUpdatedEntry(); 1023 final Entry currentSubentry = subentry.getEntry(); 1024 final Entry renamedSubentry = currentSubentry.duplicate(false); 1025 final DN renamedDN = currentSubentry.getName().rename(oldEntry.getName(), newEntry.getName()); 1026 renamedSubentry.setDN(renamedDN); 1027 1028 for (SubentryChangeListener changeListener : changeListeners) 1029 { 1030 try 1031 { 1032 changeListener.checkSubentryModifyAcceptable(currentSubentry, renamedSubentry); 1033 } 1034 catch (DirectoryException de) 1035 { 1036 logger.traceException(de); 1037 1038 return PluginResult.PreOperation.stopProcessing( 1039 de.getResultCode(), de.getMessageObject()); 1040 } 1041 } 1042 } 1043 } 1044 finally 1045 { 1046 lock.readLock().unlock(); 1047 } 1048 1049 return PluginResult.PreOperation.continueOperationProcessing(); 1050 } 1051 1052 /** {@inheritDoc} */ 1053 @Override 1054 public PostOperation doPostOperation( 1055 PostOperationAddOperation addOperation) 1056 { 1057 // Only do something if the operation is successful, meaning there 1058 // has been a change. 1059 if (addOperation.getResultCode() == ResultCode.SUCCESS) 1060 { 1061 doPostAdd(addOperation.getEntryToAdd()); 1062 } 1063 1064 // If we've gotten here, then everything is acceptable. 1065 return PluginResult.PostOperation.continueOperationProcessing(); 1066 } 1067 1068 /** {@inheritDoc} */ 1069 @Override 1070 public PostOperation doPostOperation( 1071 PostOperationDeleteOperation deleteOperation) 1072 { 1073 // Only do something if the operation is successful, meaning there 1074 // has been a change. 1075 if (deleteOperation.getResultCode() == ResultCode.SUCCESS) 1076 { 1077 doPostDelete(deleteOperation.getEntryToDelete()); 1078 } 1079 1080 // If we've gotten here, then everything is acceptable. 1081 return PluginResult.PostOperation.continueOperationProcessing(); 1082 } 1083 1084 /** {@inheritDoc} */ 1085 @Override 1086 public PostOperation doPostOperation( 1087 PostOperationModifyOperation modifyOperation) 1088 { 1089 // Only do something if the operation is successful, meaning there 1090 // has been a change. 1091 if (modifyOperation.getResultCode() == ResultCode.SUCCESS) 1092 { 1093 doPostModify(modifyOperation.getCurrentEntry(), 1094 modifyOperation.getModifiedEntry()); 1095 } 1096 1097 // If we've gotten here, then everything is acceptable. 1098 return PluginResult.PostOperation.continueOperationProcessing(); 1099 } 1100 1101 /** {@inheritDoc} */ 1102 @Override 1103 public PostOperation doPostOperation( 1104 PostOperationModifyDNOperation modifyDNOperation) 1105 { 1106 // Only do something if the operation is successful, meaning there 1107 // has been a change. 1108 if (modifyDNOperation.getResultCode() == ResultCode.SUCCESS) 1109 { 1110 doPostModifyDN(modifyDNOperation.getOriginalEntry(), 1111 modifyDNOperation.getUpdatedEntry()); 1112 } 1113 1114 // If we've gotten here, then everything is acceptable. 1115 return PluginResult.PostOperation.continueOperationProcessing(); 1116 } 1117 1118 /** {@inheritDoc} */ 1119 @Override 1120 public void doPostSynchronization( 1121 PostSynchronizationAddOperation addOperation) 1122 { 1123 Entry entry = addOperation.getEntryToAdd(); 1124 if (entry != null) 1125 { 1126 doPostAdd(entry); 1127 } 1128 } 1129 1130 /** {@inheritDoc} */ 1131 @Override 1132 public void doPostSynchronization( 1133 PostSynchronizationDeleteOperation deleteOperation) 1134 { 1135 Entry entry = deleteOperation.getEntryToDelete(); 1136 if (entry != null) 1137 { 1138 doPostDelete(entry); 1139 } 1140 } 1141 1142 /** {@inheritDoc} */ 1143 @Override 1144 public void doPostSynchronization( 1145 PostSynchronizationModifyOperation modifyOperation) 1146 { 1147 Entry entry = modifyOperation.getCurrentEntry(); 1148 Entry modEntry = modifyOperation.getModifiedEntry(); 1149 if (entry != null && modEntry != null) 1150 { 1151 doPostModify(entry, modEntry); 1152 } 1153 } 1154 1155 /** {@inheritDoc} */ 1156 @Override 1157 public void doPostSynchronization( 1158 PostSynchronizationModifyDNOperation modifyDNOperation) 1159 { 1160 Entry oldEntry = modifyDNOperation.getOriginalEntry(); 1161 Entry newEntry = modifyDNOperation.getUpdatedEntry(); 1162 if (oldEntry != null && newEntry != null) 1163 { 1164 doPostModifyDN(oldEntry, newEntry); 1165 } 1166 } 1167}