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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.workflowelement.localbackend; 028 029 030 031 import java.util.ArrayList; 032 import java.util.HashSet; 033 import java.util.Iterator; 034 import java.util.LinkedHashSet; 035 import java.util.List; 036 import java.util.Map; 037 import java.util.concurrent.CopyOnWriteArrayList; 038 import java.util.concurrent.locks.Lock; 039 040 import org.opends.messages.Message; 041 import org.opends.messages.MessageBuilder; 042 import org.opends.server.api.AttributeSyntax; 043 import org.opends.server.api.Backend; 044 import org.opends.server.api.ChangeNotificationListener; 045 import org.opends.server.api.ClientConnection; 046 import org.opends.server.api.PasswordStorageScheme; 047 import org.opends.server.api.PasswordValidator; 048 import org.opends.server.api.SynchronizationProvider; 049 import org.opends.server.api.plugin.PluginResult; 050 import org.opends.server.controls.LDAPAssertionRequestControl; 051 import org.opends.server.controls.LDAPPostReadRequestControl; 052 import org.opends.server.controls.LDAPPostReadResponseControl; 053 import org.opends.server.controls.PasswordPolicyErrorType; 054 import org.opends.server.controls.PasswordPolicyResponseControl; 055 import org.opends.server.controls.ProxiedAuthV1Control; 056 import org.opends.server.controls.ProxiedAuthV2Control; 057 import org.opends.server.core.AccessControlConfigManager; 058 import org.opends.server.core.AddOperation; 059 import org.opends.server.core.AddOperationWrapper; 060 import org.opends.server.core.DirectoryServer; 061 import org.opends.server.core.PasswordPolicy; 062 import org.opends.server.core.PluginConfigManager; 063 import org.opends.server.loggers.debug.DebugTracer; 064 import org.opends.server.protocols.asn1.ASN1OctetString; 065 import org.opends.server.schema.AuthPasswordSyntax; 066 import org.opends.server.schema.BooleanSyntax; 067 import org.opends.server.schema.UserPasswordSyntax; 068 import org.opends.server.types.Attribute; 069 import org.opends.server.types.AttributeType; 070 import org.opends.server.types.AttributeValue; 071 import org.opends.server.types.ByteString; 072 import org.opends.server.types.CanceledOperationException; 073 import org.opends.server.types.Control; 074 import org.opends.server.types.DebugLogLevel; 075 import org.opends.server.types.DirectoryException; 076 import org.opends.server.types.DN; 077 import org.opends.server.types.Entry; 078 import org.opends.server.types.LDAPException; 079 import org.opends.server.types.LockManager; 080 import org.opends.server.types.ObjectClass; 081 import org.opends.server.types.Privilege; 082 import org.opends.server.types.RDN; 083 import org.opends.server.types.ResultCode; 084 import org.opends.server.types.SearchFilter; 085 import org.opends.server.types.SearchResultEntry; 086 import org.opends.server.types.SynchronizationProviderResult; 087 import org.opends.server.types.operation.PostOperationAddOperation; 088 import org.opends.server.types.operation.PostResponseAddOperation; 089 import org.opends.server.types.operation.PreOperationAddOperation; 090 import org.opends.server.types.operation.PostSynchronizationAddOperation; 091 import org.opends.server.util.TimeThread; 092 093 import static org.opends.messages.CoreMessages.*; 094 import static org.opends.server.loggers.ErrorLogger.*; 095 import static org.opends.server.loggers.debug.DebugLogger.*; 096 import static org.opends.server.config.ConfigConstants.*; 097 import static org.opends.server.util.ServerConstants.*; 098 import static org.opends.server.util.StaticUtils.*; 099 100 101 102 /** 103 * This class defines an operation used to add an entry in a local backend 104 * of the Directory Server. 105 */ 106 public class LocalBackendAddOperation 107 extends AddOperationWrapper 108 implements PreOperationAddOperation, PostOperationAddOperation, 109 PostResponseAddOperation, PostSynchronizationAddOperation 110 { 111 /** 112 * The tracer object for the debug logger. 113 */ 114 private static final DebugTracer TRACER = getTracer(); 115 116 117 118 // The backend in which the entry is to be added. 119 private Backend backend; 120 121 // Indicates whether the request includes the LDAP no-op control. 122 private boolean noOp; 123 124 // The DN of the entry to be added. 125 private DN entryDN; 126 127 // The entry being added to the server. 128 private Entry entry; 129 130 // The post-read request control included in the request, if applicable. 131 LDAPPostReadRequestControl postReadRequest; 132 133 // The set of object classes for the entry to add. 134 private Map<ObjectClass, String> objectClasses; 135 136 // The set of operational attributes for the entry to add. 137 private Map<AttributeType,List<Attribute>> operationalAttributes; 138 139 // The set of user attributes for the entry to add. 140 private Map<AttributeType,List<Attribute>> userAttributes; 141 142 143 144 /** 145 * Creates a new operation that may be used to add a new entry in a 146 * local backend of the Directory Server. 147 * 148 * @param add The operation to enhance. 149 */ 150 public LocalBackendAddOperation(AddOperation add) 151 { 152 super(add); 153 154 LocalBackendWorkflowElement.attachLocalOperation (add, this); 155 } 156 157 158 159 /** 160 * Retrieves the entry to be added to the server. Note that this will not be 161 * available to pre-parse plugins or during the conflict resolution portion of 162 * the synchronization processing. 163 * 164 * @return The entry to be added to the server, or <CODE>null</CODE> if it is 165 * not yet available. 166 */ 167 public final Entry getEntryToAdd() 168 { 169 return entry; 170 } 171 172 173 174 /** 175 * Process this add operation against a local backend. 176 * 177 * @param backend The backend in which the add operation should be 178 * processed. 179 * 180 * @throws CanceledOperationException if this operation should be 181 * cancelled 182 */ 183 void processLocalAdd(Backend backend) throws CanceledOperationException { 184 boolean executePostOpPlugins = false; 185 186 this.backend = backend; 187 ClientConnection clientConnection = getClientConnection(); 188 189 // Get the plugin config manager that will be used for invoking plugins. 190 PluginConfigManager pluginConfigManager = 191 DirectoryServer.getPluginConfigManager(); 192 193 // Check for a request to cancel this operation. 194 checkIfCanceled(false); 195 196 // Create a labeled block of code that we can break out of if a problem is 197 // detected. 198 addProcessing: 199 { 200 // Process the entry DN and set of attributes to convert them from their 201 // raw forms as provided by the client to the forms required for the rest 202 // of the add processing. 203 entryDN = getEntryDN(); 204 if (entryDN == null) 205 { 206 break addProcessing; 207 } 208 209 objectClasses = getObjectClasses(); 210 userAttributes = getUserAttributes(); 211 operationalAttributes = getOperationalAttributes(); 212 213 if ((objectClasses == null ) || (userAttributes == null) || 214 (operationalAttributes == null)) 215 { 216 break addProcessing; 217 } 218 219 // Check for a request to cancel this operation. 220 checkIfCanceled(false); 221 222 223 // Grab a read lock on the parent entry, if there is one. We need to do 224 // this to ensure that the parent is not deleted or renamed while this add 225 // is in progress, and we could also need it to check the entry against 226 // a DIT structure rule. 227 Lock parentLock = null; 228 Lock entryLock = null; 229 230 DN parentDN = entryDN.getParentDNInSuffix(); 231 try 232 { 233 parentLock = lockParent(parentDN); 234 } 235 catch (DirectoryException de) 236 { 237 if (debugEnabled()) 238 { 239 TRACER.debugCaught(DebugLogLevel.ERROR, de); 240 } 241 242 setResponseData(de); 243 break addProcessing; 244 } 245 246 try 247 { 248 // Check for a request to cancel this operation. 249 checkIfCanceled(false); 250 251 252 // Grab a write lock on the target entry. We'll need to do this 253 // eventually anyway, and we want to make sure that the two locks are 254 // always released when exiting this method, no matter what. Since 255 // the entry shouldn't exist yet, locking earlier than necessary 256 // shouldn't cause a problem. 257 for (int i=0; i < 3; i++) 258 { 259 entryLock = LockManager.lockWrite(entryDN); 260 if (entryLock != null) 261 { 262 break; 263 } 264 } 265 266 if (entryLock == null) 267 { 268 setResultCode(DirectoryServer.getServerErrorResultCode()); 269 appendErrorMessage(ERR_ADD_CANNOT_LOCK_ENTRY.get( 270 String.valueOf(entryDN))); 271 272 break addProcessing; 273 } 274 275 276 // Invoke any conflict resolution processing that might be needed by the 277 // synchronization provider. 278 for (SynchronizationProvider provider : 279 DirectoryServer.getSynchronizationProviders()) 280 { 281 try 282 { 283 SynchronizationProviderResult result = 284 provider.handleConflictResolution(this); 285 if (! result.continueProcessing()) 286 { 287 setResultCode(result.getResultCode()); 288 appendErrorMessage(result.getErrorMessage()); 289 setMatchedDN(result.getMatchedDN()); 290 setReferralURLs(result.getReferralURLs()); 291 break addProcessing; 292 } 293 } 294 catch (DirectoryException de) 295 { 296 if (debugEnabled()) 297 { 298 TRACER.debugCaught(DebugLogLevel.ERROR, de); 299 } 300 301 logError(ERR_ADD_SYNCH_CONFLICT_RESOLUTION_FAILED.get( 302 getConnectionID(), getOperationID(), 303 getExceptionMessage(de))); 304 305 setResponseData(de); 306 break addProcessing; 307 } 308 } 309 310 for (AttributeType at : userAttributes.keySet()) 311 { 312 // If the attribute type is marked "NO-USER-MODIFICATION" then fail 313 // unless this is an internal operation or is related to 314 // synchronization in some way. 315 // This must be done before running the password policy code 316 // and any other code that may add attributes marked as 317 // "NO-USER-MODIFICATION" 318 // 319 // Note that doing this checks at this time 320 // of the processing does not make it possible for pre-parse plugins 321 // to add NO-USER-MODIFICATION attributes to the entry. 322 if (at.isNoUserModification()) 323 { 324 if (! (isInternalOperation() || isSynchronizationOperation())) 325 { 326 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 327 appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get( 328 String.valueOf(entryDN), 329 at.getNameOrOID())); 330 331 break addProcessing; 332 } 333 } 334 } 335 336 for (AttributeType at : operationalAttributes.keySet()) 337 { 338 if (at.isNoUserModification()) 339 { 340 if (! (isInternalOperation() || isSynchronizationOperation())) 341 { 342 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 343 appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get( 344 String.valueOf(entryDN), 345 at.getNameOrOID())); 346 347 break addProcessing; 348 } 349 } 350 } 351 352 // Check to see if the entry already exists. We do this before 353 // checking whether the parent exists to ensure a referral entry 354 // above the parent results in a correct referral. 355 try 356 { 357 if (DirectoryServer.entryExists(entryDN)) 358 { 359 setResultCode(ResultCode.ENTRY_ALREADY_EXISTS); 360 appendErrorMessage(ERR_ADD_ENTRY_ALREADY_EXISTS.get( 361 String.valueOf(entryDN))); 362 break addProcessing; 363 } 364 } 365 catch (DirectoryException de) 366 { 367 if (debugEnabled()) 368 { 369 TRACER.debugCaught(DebugLogLevel.ERROR, de); 370 } 371 372 setResponseData(de); 373 break addProcessing; 374 } 375 376 377 // Get the parent entry, if it exists. 378 Entry parentEntry = null; 379 if (parentDN != null) 380 { 381 try 382 { 383 parentEntry = DirectoryServer.getEntry(parentDN); 384 385 if (parentEntry == null) 386 { 387 DN matchedDN = parentDN.getParentDNInSuffix(); 388 while (matchedDN != null) 389 { 390 try 391 { 392 if (DirectoryServer.entryExists(matchedDN)) 393 { 394 setMatchedDN(matchedDN); 395 break; 396 } 397 } 398 catch (Exception e) 399 { 400 if (debugEnabled()) 401 { 402 TRACER.debugCaught(DebugLogLevel.ERROR, e); 403 } 404 break; 405 } 406 407 matchedDN = matchedDN.getParentDNInSuffix(); 408 } 409 410 411 // The parent doesn't exist, so this add can't be successful. 412 setResultCode(ResultCode.NO_SUCH_OBJECT); 413 appendErrorMessage(ERR_ADD_NO_PARENT.get(String.valueOf(entryDN), 414 String.valueOf(parentDN))); 415 break addProcessing; 416 } 417 } 418 catch (DirectoryException de) 419 { 420 if (debugEnabled()) 421 { 422 TRACER.debugCaught(DebugLogLevel.ERROR, de); 423 } 424 425 setResponseData(de); 426 break addProcessing; 427 } 428 } 429 430 431 // Check to make sure that all of the RDN attributes are included as 432 // attribute values. If not, then either add them or report an error. 433 try 434 { 435 addRDNAttributesIfNecessary(); 436 } 437 catch (DirectoryException de) 438 { 439 if (debugEnabled()) 440 { 441 TRACER.debugCaught(DebugLogLevel.ERROR, de); 442 } 443 444 setResponseData(de); 445 break addProcessing; 446 } 447 448 449 // Check to make sure that all objectclasses have their superior classes 450 // listed in the entry. If not, then add them. 451 HashSet<ObjectClass> additionalClasses = null; 452 for (ObjectClass oc : objectClasses.keySet()) 453 { 454 ObjectClass superiorClass = oc.getSuperiorClass(); 455 if ((superiorClass != null) && 456 (! objectClasses.containsKey(superiorClass))) 457 { 458 if (additionalClasses == null) 459 { 460 additionalClasses = new HashSet<ObjectClass>(); 461 } 462 463 additionalClasses.add(superiorClass); 464 } 465 } 466 467 if (additionalClasses != null) 468 { 469 for (ObjectClass oc : additionalClasses) 470 { 471 addObjectClassChain(oc); 472 } 473 } 474 475 476 // Create an entry object to encapsulate the set of attributes and 477 // objectclasses. 478 entry = new Entry(entryDN, objectClasses, userAttributes, 479 operationalAttributes); 480 481 // Check to see if the entry includes a privilege specification. If so, 482 // then the requester must have the PRIVILEGE_CHANGE privilege. 483 AttributeType privType = 484 DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME, true); 485 if (entry.hasAttribute(privType) && 486 (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, this))) 487 { 488 489 appendErrorMessage( 490 ERR_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES.get()); 491 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 492 break addProcessing; 493 } 494 495 496 // If it's not a synchronization operation, then check 497 // to see if the entry contains one or more passwords and if they 498 // are valid in accordance with the password policies associated with 499 // the user. Also perform any encoding that might be required by 500 // password storage schemes. 501 if (! isSynchronizationOperation()) 502 { 503 try 504 { 505 handlePasswordPolicy(); 506 } 507 catch (DirectoryException de) 508 { 509 if (debugEnabled()) 510 { 511 TRACER.debugCaught(DebugLogLevel.ERROR, de); 512 } 513 514 setResponseData(de); 515 break addProcessing; 516 } 517 } 518 519 520 // If the server is configured to check schema and the 521 // operation is not a synchronization operation, 522 // check to see if the entry is valid according to the server schema, 523 // and also whether its attributes are valid according to their syntax. 524 if ((DirectoryServer.checkSchema()) && (! isSynchronizationOperation())) 525 { 526 try 527 { 528 checkSchema(parentEntry); 529 } 530 catch (DirectoryException de) 531 { 532 if (debugEnabled()) 533 { 534 TRACER.debugCaught(DebugLogLevel.ERROR, de); 535 } 536 537 setResponseData(de); 538 break addProcessing; 539 } 540 } 541 542 543 // Get the backend in which the add is to be performed. 544 if (backend == null) 545 { 546 setResultCode(ResultCode.NO_SUCH_OBJECT); 547 appendErrorMessage(Message.raw("No backend for entry " + 548 entryDN.toString())); // TODO: i18n 549 break addProcessing; 550 } 551 552 553 // Check to see if there are any controls in the request. If so, then 554 // see if there is any special processing required. 555 try 556 { 557 processControls(parentDN); 558 } 559 catch (DirectoryException de) 560 { 561 if (debugEnabled()) 562 { 563 TRACER.debugCaught(DebugLogLevel.ERROR, de); 564 } 565 566 setResponseData(de); 567 break addProcessing; 568 } 569 570 571 // Check to see if the client has permission to perform the add. 572 573 // FIXME: for now assume that this will check all permission 574 // pertinent to the operation. This includes proxy authorization 575 // and any other controls specified. 576 577 // FIXME: earlier checks to see if the entry already exists or 578 // if the parent entry does not exist may have already exposed 579 // sensitive information to the client. 580 if (AccessControlConfigManager.getInstance().getAccessControlHandler(). 581 isAllowed(this) == false) 582 { 583 setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); 584 appendErrorMessage(ERR_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get( 585 String.valueOf(entryDN))); 586 break addProcessing; 587 } 588 589 // Check for a request to cancel this operation. 590 checkIfCanceled(false); 591 592 // If the operation is not a synchronization operation, 593 // Invoke the pre-operation add plugins. 594 if (! isSynchronizationOperation()) 595 { 596 executePostOpPlugins = true; 597 PluginResult.PreOperation preOpResult = 598 pluginConfigManager.invokePreOperationAddPlugins(this); 599 if (!preOpResult.continueProcessing()) 600 { 601 setResultCode(preOpResult.getResultCode()); 602 appendErrorMessage(preOpResult.getErrorMessage()); 603 setMatchedDN(preOpResult.getMatchedDN()); 604 setReferralURLs(preOpResult.getReferralURLs()); 605 break addProcessing; 606 } 607 } 608 609 610 // Check for a request to cancel this operation. 611 checkIfCanceled(true); 612 613 614 // If it is not a private backend, then check to see if the server or 615 // backend is operating in read-only mode. 616 if (! backend.isPrivateBackend()) 617 { 618 switch (DirectoryServer.getWritabilityMode()) 619 { 620 case DISABLED: 621 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 622 appendErrorMessage(ERR_ADD_SERVER_READONLY.get( 623 String.valueOf(entryDN))); 624 break addProcessing; 625 626 case INTERNAL_ONLY: 627 if (! (isInternalOperation() || isSynchronizationOperation())) 628 { 629 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 630 appendErrorMessage(ERR_ADD_SERVER_READONLY.get( 631 String.valueOf(entryDN))); 632 break addProcessing; 633 } 634 break; 635 } 636 637 switch (backend.getWritabilityMode()) 638 { 639 case DISABLED: 640 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 641 appendErrorMessage(ERR_ADD_BACKEND_READONLY.get( 642 String.valueOf(entryDN))); 643 break addProcessing; 644 645 case INTERNAL_ONLY: 646 if (! (isInternalOperation() || isSynchronizationOperation())) 647 { 648 setResultCode(ResultCode.UNWILLING_TO_PERFORM); 649 appendErrorMessage(ERR_ADD_BACKEND_READONLY.get( 650 String.valueOf(entryDN))); 651 break addProcessing; 652 } 653 break; 654 } 655 } 656 657 658 try 659 { 660 if (noOp) 661 { 662 appendErrorMessage(INFO_ADD_NOOP.get()); 663 setResultCode(ResultCode.NO_OPERATION); 664 } 665 else 666 { 667 for (SynchronizationProvider provider : 668 DirectoryServer.getSynchronizationProviders()) 669 { 670 try 671 { 672 SynchronizationProviderResult result = 673 provider.doPreOperation(this); 674 if (! result.continueProcessing()) 675 { 676 setResultCode(result.getResultCode()); 677 appendErrorMessage(result.getErrorMessage()); 678 setMatchedDN(result.getMatchedDN()); 679 setReferralURLs(result.getReferralURLs()); 680 break addProcessing; 681 } 682 } 683 catch (DirectoryException de) 684 { 685 if (debugEnabled()) 686 { 687 TRACER.debugCaught(DebugLogLevel.ERROR, de); 688 } 689 690 logError(ERR_ADD_SYNCH_PREOP_FAILED.get(getConnectionID(), 691 getOperationID(), getExceptionMessage(de))); 692 setResponseData(de); 693 break addProcessing; 694 } 695 } 696 697 backend.addEntry(entry, this); 698 } 699 700 if (postReadRequest != null) 701 { 702 addPostReadResponse(); 703 } 704 705 706 if (! noOp) 707 { 708 setResultCode(ResultCode.SUCCESS); 709 } 710 } 711 catch (DirectoryException de) 712 { 713 if (debugEnabled()) 714 { 715 TRACER.debugCaught(DebugLogLevel.ERROR, de); 716 } 717 718 setResponseData(de); 719 break addProcessing; 720 } 721 } 722 finally 723 { 724 if (entryLock != null) 725 { 726 LockManager.unlock(entryDN, entryLock); 727 } 728 729 if (parentLock != null) 730 { 731 LockManager.unlock(parentDN, parentLock); 732 } 733 } 734 } 735 736 for (SynchronizationProvider provider : 737 DirectoryServer.getSynchronizationProviders()) 738 { 739 try 740 { 741 provider.doPostOperation(this); 742 } 743 catch (DirectoryException de) 744 { 745 if (debugEnabled()) 746 { 747 TRACER.debugCaught(DebugLogLevel.ERROR, de); 748 } 749 750 logError(ERR_ADD_SYNCH_POSTOP_FAILED.get(getConnectionID(), 751 getOperationID(), getExceptionMessage(de))); 752 setResponseData(de); 753 break; 754 } 755 } 756 757 // Invoke the post-operation or post-synchronization add plugins. 758 if (isSynchronizationOperation()) 759 { 760 if (getResultCode() == ResultCode.SUCCESS) 761 { 762 pluginConfigManager.invokePostSynchronizationAddPlugins(this); 763 } 764 } 765 else if (executePostOpPlugins) 766 { 767 // FIXME -- Should this also be done while holding the locks? 768 PluginResult.PostOperation postOpResult = 769 pluginConfigManager.invokePostOperationAddPlugins(this); 770 if(!postOpResult.continueProcessing()) 771 { 772 setResultCode(postOpResult.getResultCode()); 773 appendErrorMessage(postOpResult.getErrorMessage()); 774 setMatchedDN(postOpResult.getMatchedDN()); 775 setReferralURLs(postOpResult.getReferralURLs()); 776 return; 777 } 778 } 779 780 781 // Notify any change notification listeners that might be registered with 782 // the server. 783 if ((getResultCode() == ResultCode.SUCCESS) && (entry != null)) 784 { 785 for (ChangeNotificationListener changeListener : 786 DirectoryServer.getChangeNotificationListeners()) 787 { 788 try 789 { 790 changeListener.handleAddOperation(this, entry); 791 } 792 catch (Exception e) 793 { 794 if (debugEnabled()) 795 { 796 TRACER.debugCaught(DebugLogLevel.ERROR, e); 797 } 798 799 logError(ERR_ADD_ERROR_NOTIFYING_CHANGE_LISTENER.get( 800 getExceptionMessage(e))); 801 } 802 } 803 } 804 } 805 806 807 808 /** 809 * Acquire a read lock on the parent of the entry to add. 810 * 811 * @return The acquired read lock. 812 * 813 * @throws DirectoryException If a problem occurs while attempting to 814 * acquire the lock. 815 */ 816 private Lock lockParent(DN parentDN) 817 throws DirectoryException 818 { 819 Lock parentLock = null; 820 821 if (parentDN == null) 822 { 823 // Either this entry is a suffix or doesn't belong in the directory. 824 if (DirectoryServer.isNamingContext(entryDN)) 825 { 826 // This is fine. This entry is one of the configured suffixes. 827 parentLock = null; 828 } 829 else if (entryDN.isNullDN()) 830 { 831 // This is not fine. The root DSE cannot be added. 832 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 833 ERR_ADD_CANNOT_ADD_ROOT_DSE.get()); 834 } 835 else 836 { 837 // The entry doesn't have a parent but isn't a suffix. This is not 838 // allowed. 839 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, 840 ERR_ADD_ENTRY_NOT_SUFFIX.get( 841 String.valueOf(entryDN))); 842 } 843 } 844 else 845 { 846 for (int i=0; i < 3; i++) 847 { 848 parentLock = LockManager.lockRead(parentDN); 849 if (parentLock != null) 850 { 851 break; 852 } 853 } 854 855 if (parentLock == null) 856 { 857 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 858 ERR_ADD_CANNOT_LOCK_PARENT.get( 859 String.valueOf(entryDN), 860 String.valueOf(parentDN))); 861 } 862 } 863 864 return parentLock; 865 } 866 867 868 869 /** 870 * Adds any missing RDN attributes to the entry. 871 * 872 * @throws DirectoryException If the entry is missing one or more RDN 873 * attributes and the server is configured to 874 * reject such entries. 875 */ 876 private void addRDNAttributesIfNecessary() 877 throws DirectoryException 878 { 879 RDN rdn = entryDN.getRDN(); 880 int numAVAs = rdn.getNumValues(); 881 for (int i=0; i < numAVAs; i++) 882 { 883 AttributeType t = rdn.getAttributeType(i); 884 AttributeValue v = rdn.getAttributeValue(i); 885 String n = rdn.getAttributeName(i); 886 if (t.isOperational()) 887 { 888 List<Attribute> attrList = operationalAttributes.get(t); 889 if (attrList == null) 890 { 891 if (isSynchronizationOperation() || 892 DirectoryServer.addMissingRDNAttributes()) 893 { 894 LinkedHashSet<AttributeValue> valueList = 895 new LinkedHashSet<AttributeValue>(1); 896 valueList.add(v); 897 898 attrList = new ArrayList<Attribute>(); 899 attrList.add(new Attribute(t, n, valueList)); 900 901 operationalAttributes.put(t, attrList); 902 } 903 else 904 { 905 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 906 ERR_ADD_MISSING_RDN_ATTRIBUTE.get( 907 String.valueOf(entryDN), n)); 908 } 909 } 910 else 911 { 912 boolean found = false; 913 for (Attribute a : attrList) 914 { 915 if (a.hasOptions()) 916 { 917 continue; 918 } 919 else 920 { 921 if (! a.hasValue(v)) 922 { 923 a.getValues().add(v); 924 } 925 926 found = true; 927 break; 928 } 929 } 930 931 if (! found) 932 { 933 if (isSynchronizationOperation() || 934 DirectoryServer.addMissingRDNAttributes()) 935 { 936 LinkedHashSet<AttributeValue> valueList = 937 new LinkedHashSet<AttributeValue>(1); 938 valueList.add(v); 939 attrList.add(new Attribute(t, n, valueList)); 940 } 941 else 942 { 943 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 944 ERR_ADD_MISSING_RDN_ATTRIBUTE.get( 945 String.valueOf(entryDN), n)); 946 } 947 } 948 } 949 } 950 else 951 { 952 List<Attribute> attrList = userAttributes.get(t); 953 if (attrList == null) 954 { 955 if (isSynchronizationOperation() || 956 DirectoryServer.addMissingRDNAttributes()) 957 { 958 LinkedHashSet<AttributeValue> valueList = 959 new LinkedHashSet<AttributeValue>(1); 960 valueList.add(v); 961 962 attrList = new ArrayList<Attribute>(); 963 attrList.add(new Attribute(t, n, valueList)); 964 965 userAttributes.put(t, attrList); 966 } 967 else 968 { 969 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 970 ERR_ADD_MISSING_RDN_ATTRIBUTE.get( 971 String.valueOf(entryDN),n)); 972 } 973 } 974 else 975 { 976 boolean found = false; 977 for (Attribute a : attrList) 978 { 979 if (a.hasOptions()) 980 { 981 continue; 982 } 983 else 984 { 985 if (! a.hasValue(v)) 986 { 987 a.getValues().add(v); 988 } 989 990 found = true; 991 break; 992 } 993 } 994 995 if (! found) 996 { 997 if (isSynchronizationOperation() || 998 DirectoryServer.addMissingRDNAttributes()) 999 { 1000 LinkedHashSet<AttributeValue> valueList = 1001 new LinkedHashSet<AttributeValue>(1); 1002 valueList.add(v); 1003 attrList.add(new Attribute(t, n, valueList)); 1004 } 1005 else 1006 { 1007 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1008 ERR_ADD_MISSING_RDN_ATTRIBUTE.get( 1009 String.valueOf(entryDN),n)); 1010 } 1011 } 1012 } 1013 } 1014 } 1015 } 1016 1017 1018 1019 /** 1020 * Adds the provided objectClass to the entry, along with its superior classes 1021 * if appropriate. 1022 * 1023 * @param objectClass The objectclass to add to the entry. 1024 */ 1025 public final void addObjectClassChain(ObjectClass objectClass) 1026 { 1027 Map<ObjectClass, String> objectClasses = getObjectClasses(); 1028 if (objectClasses != null){ 1029 if (! objectClasses.containsKey(objectClass)) 1030 { 1031 objectClasses.put(objectClass, objectClass.getNameOrOID()); 1032 } 1033 1034 ObjectClass superiorClass = objectClass.getSuperiorClass(); 1035 if ((superiorClass != null) && 1036 (! objectClasses.containsKey(superiorClass))) 1037 { 1038 addObjectClassChain(superiorClass); 1039 } 1040 } 1041 } 1042 1043 1044 1045 /** 1046 * Performs all password policy processing necessary for the provided add 1047 * operation. 1048 * 1049 * @throws DirectoryException If a problem occurs while performing password 1050 * policy processing for the add operation. 1051 */ 1052 public final void handlePasswordPolicy() 1053 throws DirectoryException 1054 { 1055 // FIXME -- We need to check to see if the password policy subentry 1056 // might be specified virtually rather than as a real 1057 // attribute. 1058 PasswordPolicy passwordPolicy = null; 1059 List<Attribute> pwAttrList = 1060 entry.getAttribute(OP_ATTR_PWPOLICY_POLICY_DN); 1061 if ((pwAttrList != null) && (! pwAttrList.isEmpty())) 1062 { 1063 Attribute a = pwAttrList.get(0); 1064 LinkedHashSet<AttributeValue> valueSet = a.getValues(); 1065 Iterator<AttributeValue> iterator = valueSet.iterator(); 1066 if (iterator.hasNext()) 1067 { 1068 DN policyDN; 1069 try 1070 { 1071 policyDN = DN.decode(iterator.next().getValue()); 1072 } 1073 catch (DirectoryException de) 1074 { 1075 if (debugEnabled()) 1076 { 1077 TRACER.debugCaught(DebugLogLevel.ERROR, de); 1078 } 1079 1080 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 1081 ERR_ADD_INVALID_PWPOLICY_DN_SYNTAX.get( 1082 String.valueOf(entryDN), 1083 de.getMessageObject())); 1084 } 1085 1086 passwordPolicy = DirectoryServer.getPasswordPolicy(policyDN); 1087 if (passwordPolicy == null) 1088 { 1089 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1090 ERR_ADD_NO_SUCH_PWPOLICY.get( 1091 String.valueOf(entryDN), 1092 String.valueOf(policyDN))); 1093 } 1094 } 1095 } 1096 1097 if (passwordPolicy == null) 1098 { 1099 passwordPolicy = DirectoryServer.getDefaultPasswordPolicy(); 1100 } 1101 1102 // See if a password was specified. 1103 AttributeType passwordAttribute = passwordPolicy.getPasswordAttribute(); 1104 List<Attribute> attrList = entry.getAttribute(passwordAttribute); 1105 if ((attrList == null) || attrList.isEmpty()) 1106 { 1107 // The entry doesn't have a password, so no action is required. 1108 return; 1109 } 1110 else if (attrList.size() > 1) 1111 { 1112 // This must mean there are attribute options, which we won't allow for 1113 // passwords. 1114 Message message = ERR_PWPOLICY_ATTRIBUTE_OPTIONS_NOT_ALLOWED.get( 1115 passwordAttribute.getNameOrOID()); 1116 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1117 } 1118 1119 Attribute passwordAttr = attrList.get(0); 1120 if (passwordAttr.hasOptions()) 1121 { 1122 Message message = ERR_PWPOLICY_ATTRIBUTE_OPTIONS_NOT_ALLOWED.get( 1123 passwordAttribute.getNameOrOID()); 1124 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1125 } 1126 1127 LinkedHashSet<AttributeValue> values = passwordAttr.getValues(); 1128 if (values.isEmpty()) 1129 { 1130 // This will be treated the same as not having a password. 1131 return; 1132 } 1133 1134 if ((! passwordPolicy.allowMultiplePasswordValues()) && (values.size() > 1)) 1135 { 1136 // FIXME -- What if they're pre-encoded and might all be the same? 1137 addPWPolicyControl(PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED); 1138 1139 Message message = ERR_PWPOLICY_MULTIPLE_PW_VALUES_NOT_ALLOWED.get( 1140 passwordAttribute.getNameOrOID()); 1141 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1142 } 1143 1144 CopyOnWriteArrayList<PasswordStorageScheme> defaultStorageSchemes = 1145 passwordPolicy.getDefaultStorageSchemes(); 1146 LinkedHashSet<AttributeValue> newValues = 1147 new LinkedHashSet<AttributeValue>(defaultStorageSchemes.size()); 1148 for (AttributeValue v : values) 1149 { 1150 ByteString value = v.getValue(); 1151 1152 // See if the password is pre-encoded. 1153 if (passwordPolicy.usesAuthPasswordSyntax()) 1154 { 1155 if (AuthPasswordSyntax.isEncoded(value)) 1156 { 1157 if (passwordPolicy.allowPreEncodedPasswords()) 1158 { 1159 newValues.add(v); 1160 continue; 1161 } 1162 else 1163 { 1164 addPWPolicyControl( 1165 PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY); 1166 1167 Message message = ERR_PWPOLICY_PREENCODED_NOT_ALLOWED.get( 1168 passwordAttribute.getNameOrOID()); 1169 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1170 message); 1171 } 1172 } 1173 } 1174 else 1175 { 1176 if (UserPasswordSyntax.isEncoded(value)) 1177 { 1178 if (passwordPolicy.allowPreEncodedPasswords()) 1179 { 1180 newValues.add(v); 1181 continue; 1182 } 1183 else 1184 { 1185 addPWPolicyControl( 1186 PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY); 1187 1188 Message message = ERR_PWPOLICY_PREENCODED_NOT_ALLOWED.get( 1189 passwordAttribute.getNameOrOID()); 1190 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1191 message); 1192 } 1193 } 1194 } 1195 1196 1197 // See if the password passes validation. We should only do this if 1198 // validation should be performed for administrators. 1199 if (! passwordPolicy.skipValidationForAdministrators()) 1200 { 1201 // There are never any current passwords for an add operation. 1202 HashSet<ByteString> currentPasswords = new HashSet<ByteString>(0); 1203 MessageBuilder invalidReason = new MessageBuilder(); 1204 for (PasswordValidator<?> validator : 1205 passwordPolicy.getPasswordValidators().values()) 1206 { 1207 if (! validator.passwordIsAcceptable(value, currentPasswords, this, 1208 entry, invalidReason)) 1209 { 1210 addPWPolicyControl( 1211 PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY); 1212 1213 Message message = ERR_PWPOLICY_VALIDATION_FAILED. 1214 get(passwordAttribute.getNameOrOID(), 1215 String.valueOf(invalidReason)); 1216 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1217 message); 1218 } 1219 } 1220 } 1221 1222 1223 // Encode the password. 1224 if (passwordPolicy.usesAuthPasswordSyntax()) 1225 { 1226 for (PasswordStorageScheme s : defaultStorageSchemes) 1227 { 1228 ByteString encodedValue = s.encodeAuthPassword(value); 1229 newValues.add(new AttributeValue(passwordAttribute, encodedValue)); 1230 } 1231 } 1232 else 1233 { 1234 for (PasswordStorageScheme s : defaultStorageSchemes) 1235 { 1236 ByteString encodedValue = s.encodePasswordWithScheme(value); 1237 newValues.add(new AttributeValue(passwordAttribute, encodedValue)); 1238 } 1239 } 1240 } 1241 1242 1243 // Put the new encoded values in the entry. 1244 passwordAttr.setValues(newValues); 1245 1246 1247 // Set the password changed time attribute. 1248 ByteString timeString = 1249 new ASN1OctetString(TimeThread.getGeneralizedTime()); 1250 AttributeType changedTimeType = 1251 DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC); 1252 if (changedTimeType == null) 1253 { 1254 changedTimeType = DirectoryServer.getDefaultAttributeType( 1255 OP_ATTR_PWPOLICY_CHANGED_TIME); 1256 } 1257 1258 LinkedHashSet<AttributeValue> changedTimeValues = 1259 new LinkedHashSet<AttributeValue>(1); 1260 changedTimeValues.add(new AttributeValue(changedTimeType, timeString)); 1261 1262 ArrayList<Attribute> changedTimeList = new ArrayList<Attribute>(1); 1263 changedTimeList.add(new Attribute(changedTimeType, 1264 OP_ATTR_PWPOLICY_CHANGED_TIME, 1265 changedTimeValues)); 1266 1267 entry.putAttribute(changedTimeType, changedTimeList); 1268 1269 1270 // If we should force change on add, then set the appropriate flag. 1271 if (passwordPolicy.forceChangeOnAdd()) 1272 { 1273 addPWPolicyControl(PasswordPolicyErrorType.CHANGE_AFTER_RESET); 1274 1275 AttributeType resetType = 1276 DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC); 1277 if (resetType == null) 1278 { 1279 resetType = DirectoryServer.getDefaultAttributeType( 1280 OP_ATTR_PWPOLICY_RESET_REQUIRED); 1281 } 1282 1283 LinkedHashSet<AttributeValue> resetValues = new 1284 LinkedHashSet<AttributeValue>(1); 1285 resetValues.add(BooleanSyntax.createBooleanValue(true)); 1286 1287 ArrayList<Attribute> resetList = new ArrayList<Attribute>(1); 1288 resetList.add(new Attribute(resetType, OP_ATTR_PWPOLICY_RESET_REQUIRED, 1289 resetValues)); 1290 entry.putAttribute(resetType, resetList); 1291 } 1292 } 1293 1294 1295 1296 /** 1297 * Adds a password policy response control if the corresponding request 1298 * control was included. 1299 * 1300 * @param errorType The error type to use for the response control. 1301 */ 1302 private void addPWPolicyControl(PasswordPolicyErrorType errorType) 1303 { 1304 for (Control c : getRequestControls()) 1305 { 1306 if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL)) 1307 { 1308 addResponseControl(new PasswordPolicyResponseControl(null, 0, 1309 errorType)); 1310 } 1311 } 1312 } 1313 1314 1315 1316 /** 1317 * Verifies that the entry to be added conforms to the server schema. 1318 * 1319 * @param parentEntry The parent of the entry to add. 1320 * 1321 * @throws DirectoryException If the entry violates the server schema 1322 * configuration. 1323 */ 1324 private void checkSchema(Entry parentEntry) 1325 throws DirectoryException 1326 { 1327 MessageBuilder invalidReason = new MessageBuilder(); 1328 if (! entry.conformsToSchema(parentEntry, true, true, true, invalidReason)) 1329 { 1330 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, 1331 invalidReason.toMessage()); 1332 } 1333 else 1334 { 1335 switch (DirectoryServer.getSyntaxEnforcementPolicy()) 1336 { 1337 case REJECT: 1338 invalidReason = new MessageBuilder(); 1339 for (List<Attribute> attrList : userAttributes.values()) 1340 { 1341 for (Attribute a : attrList) 1342 { 1343 AttributeSyntax syntax = a.getAttributeType().getSyntax(); 1344 if (syntax != null) 1345 { 1346 for (AttributeValue v : a.getValues()) 1347 { 1348 if (! syntax.valueIsAcceptable(v.getValue(), invalidReason)) 1349 { 1350 Message message = WARN_ADD_OP_INVALID_SYNTAX.get( 1351 String.valueOf(entryDN), 1352 String.valueOf(v.getStringValue()), 1353 String.valueOf(a.getName()), 1354 String.valueOf(invalidReason)); 1355 1356 throw new DirectoryException( 1357 ResultCode.INVALID_ATTRIBUTE_SYNTAX, 1358 message); 1359 } 1360 } 1361 } 1362 } 1363 } 1364 1365 for (List<Attribute> attrList : 1366 operationalAttributes.values()) 1367 { 1368 for (Attribute a : attrList) 1369 { 1370 AttributeSyntax syntax = a.getAttributeType().getSyntax(); 1371 if (syntax != null) 1372 { 1373 for (AttributeValue v : a.getValues()) 1374 { 1375 if (! syntax.valueIsAcceptable(v.getValue(), 1376 invalidReason)) 1377 { 1378 Message message = WARN_ADD_OP_INVALID_SYNTAX. 1379 get(String.valueOf(entryDN), 1380 String.valueOf(v.getStringValue()), 1381 String.valueOf(a.getName()), 1382 String.valueOf(invalidReason)); 1383 1384 throw new DirectoryException( 1385 ResultCode.INVALID_ATTRIBUTE_SYNTAX, 1386 message); 1387 } 1388 } 1389 } 1390 } 1391 } 1392 1393 break; 1394 1395 1396 case WARN: 1397 invalidReason = new MessageBuilder(); 1398 for (List<Attribute> attrList : userAttributes.values()) 1399 { 1400 for (Attribute a : attrList) 1401 { 1402 AttributeSyntax syntax = a.getAttributeType().getSyntax(); 1403 if (syntax != null) 1404 { 1405 for (AttributeValue v : a.getValues()) 1406 { 1407 if (! syntax.valueIsAcceptable(v.getValue(), 1408 invalidReason)) 1409 { 1410 logError(WARN_ADD_OP_INVALID_SYNTAX.get( 1411 String.valueOf(entryDN), 1412 String.valueOf(v.getStringValue()), 1413 String.valueOf(a.getName()), 1414 String.valueOf(invalidReason))); 1415 } 1416 } 1417 } 1418 } 1419 } 1420 1421 for (List<Attribute> attrList : operationalAttributes.values()) 1422 { 1423 for (Attribute a : attrList) 1424 { 1425 AttributeSyntax syntax = a.getAttributeType().getSyntax(); 1426 if (syntax != null) 1427 { 1428 for (AttributeValue v : a.getValues()) 1429 { 1430 if (! syntax.valueIsAcceptable(v.getValue(), 1431 invalidReason)) 1432 { 1433 logError(WARN_ADD_OP_INVALID_SYNTAX.get( 1434 String.valueOf(entryDN), 1435 String.valueOf(v.getStringValue()), 1436 String.valueOf(a.getName()), 1437 String.valueOf(invalidReason))); 1438 } 1439 } 1440 } 1441 } 1442 } 1443 1444 break; 1445 } 1446 } 1447 1448 1449 // See if the entry contains any attributes or object classes marked 1450 // OBSOLETE. If so, then reject the entry. 1451 for (AttributeType at : userAttributes.keySet()) 1452 { 1453 if (at.isObsolete()) 1454 { 1455 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1456 WARN_ADD_ATTR_IS_OBSOLETE.get( 1457 String.valueOf(entryDN), 1458 at.getNameOrOID())); 1459 } 1460 } 1461 1462 for (AttributeType at : operationalAttributes.keySet()) 1463 { 1464 if (at.isObsolete()) 1465 { 1466 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1467 WARN_ADD_ATTR_IS_OBSOLETE.get( 1468 String.valueOf(entryDN), 1469 at.getNameOrOID())); 1470 } 1471 } 1472 1473 for (ObjectClass oc : objectClasses.keySet()) 1474 { 1475 if (oc.isObsolete()) 1476 { 1477 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1478 WARN_ADD_OC_IS_OBSOLETE.get( 1479 String.valueOf(entryDN), 1480 oc.getNameOrOID())); 1481 } 1482 } 1483 } 1484 1485 1486 1487 /** 1488 * Processes the set of controls contained in the add request. 1489 * 1490 * @param parentDN The DN of the parent of the entry to add. 1491 * 1492 * @throws DirectoryException If there is a problem with any of the 1493 * request controls. 1494 */ 1495 private void processControls(DN parentDN) 1496 throws DirectoryException 1497 { 1498 List<Control> requestControls = getRequestControls(); 1499 if ((requestControls != null) && (! requestControls.isEmpty())) 1500 { 1501 for (int i=0; i < requestControls.size(); i++) 1502 { 1503 Control c = requestControls.get(i); 1504 String oid = c.getOID(); 1505 1506 if (!AccessControlConfigManager.getInstance(). 1507 getAccessControlHandler().isAllowed(parentDN, this, c)) 1508 { 1509 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 1510 ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid)); 1511 } 1512 1513 if (oid.equals(OID_LDAP_ASSERTION)) 1514 { 1515 LDAPAssertionRequestControl assertControl; 1516 if (c instanceof LDAPAssertionRequestControl) 1517 { 1518 assertControl = (LDAPAssertionRequestControl) c; 1519 } 1520 else 1521 { 1522 try 1523 { 1524 assertControl = LDAPAssertionRequestControl.decodeControl(c); 1525 requestControls.set(i, assertControl); 1526 } 1527 catch (LDAPException le) 1528 { 1529 if (debugEnabled()) 1530 { 1531 TRACER.debugCaught(DebugLogLevel.ERROR, le); 1532 } 1533 1534 throw new DirectoryException( 1535 ResultCode.valueOf(le.getResultCode()), 1536 le.getMessageObject()); 1537 } 1538 } 1539 1540 try 1541 { 1542 // FIXME -- We need to determine whether the current user has 1543 // permission to make this determination. 1544 SearchFilter filter = assertControl.getSearchFilter(); 1545 if (! filter.matchesEntry(entry)) 1546 { 1547 throw new DirectoryException(ResultCode.ASSERTION_FAILED, 1548 ERR_ADD_ASSERTION_FAILED.get( 1549 String.valueOf(entryDN))); 1550 } 1551 } 1552 catch (DirectoryException de) 1553 { 1554 if (de.getResultCode() == ResultCode.ASSERTION_FAILED) 1555 { 1556 throw de; 1557 } 1558 1559 if (debugEnabled()) 1560 { 1561 TRACER.debugCaught(DebugLogLevel.ERROR, de); 1562 } 1563 1564 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, 1565 ERR_ADD_CANNOT_PROCESS_ASSERTION_FILTER.get( 1566 String.valueOf(entryDN), 1567 de.getMessageObject())); 1568 } 1569 } 1570 else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED)) 1571 { 1572 noOp = true; 1573 } 1574 else if (oid.equals(OID_LDAP_READENTRY_POSTREAD)) 1575 { 1576 if (c instanceof LDAPPostReadRequestControl) 1577 { 1578 postReadRequest = (LDAPPostReadRequestControl) c; 1579 } 1580 else 1581 { 1582 try 1583 { 1584 postReadRequest = LDAPPostReadRequestControl.decodeControl(c); 1585 requestControls.set(i, postReadRequest); 1586 } 1587 catch (LDAPException le) 1588 { 1589 if (debugEnabled()) 1590 { 1591 TRACER.debugCaught(DebugLogLevel.ERROR, le); 1592 } 1593 1594 throw new DirectoryException( 1595 ResultCode.valueOf(le.getResultCode()), 1596 le.getMessageObject()); 1597 } 1598 } 1599 } 1600 else if (oid.equals(OID_PROXIED_AUTH_V1)) 1601 { 1602 // The requester must have the PROXIED_AUTH privilige in order to 1603 // be able to use this control. 1604 if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH, 1605 this)) 1606 { 1607 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 1608 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 1609 } 1610 1611 1612 ProxiedAuthV1Control proxyControl; 1613 if (c instanceof ProxiedAuthV1Control) 1614 { 1615 proxyControl = (ProxiedAuthV1Control) c; 1616 } 1617 else 1618 { 1619 try 1620 { 1621 proxyControl = ProxiedAuthV1Control.decodeControl(c); 1622 } 1623 catch (LDAPException le) 1624 { 1625 if (debugEnabled()) 1626 { 1627 TRACER.debugCaught(DebugLogLevel.ERROR, le); 1628 } 1629 1630 throw new DirectoryException( 1631 ResultCode.valueOf(le.getResultCode()), 1632 le.getMessageObject()); 1633 } 1634 } 1635 1636 1637 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 1638 setAuthorizationEntry(authorizationEntry); 1639 if (authorizationEntry == null) 1640 { 1641 setProxiedAuthorizationDN(DN.nullDN()); 1642 } 1643 else 1644 { 1645 setProxiedAuthorizationDN(authorizationEntry.getDN()); 1646 } 1647 } 1648 else if (oid.equals(OID_PROXIED_AUTH_V2)) 1649 { 1650 // The requester must have the PROXIED_AUTH privilige in order to 1651 // be able to use this control. 1652 if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH, 1653 this)) 1654 { 1655 throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, 1656 ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get()); 1657 } 1658 1659 1660 ProxiedAuthV2Control proxyControl; 1661 if (c instanceof ProxiedAuthV2Control) 1662 { 1663 proxyControl = (ProxiedAuthV2Control) c; 1664 } 1665 else 1666 { 1667 try 1668 { 1669 proxyControl = ProxiedAuthV2Control.decodeControl(c); 1670 } 1671 catch (LDAPException le) 1672 { 1673 if (debugEnabled()) 1674 { 1675 TRACER.debugCaught(DebugLogLevel.ERROR, le); 1676 } 1677 1678 throw new DirectoryException( 1679 ResultCode.valueOf(le.getResultCode()), 1680 le.getMessageObject()); 1681 } 1682 } 1683 1684 1685 Entry authorizationEntry = proxyControl.getAuthorizationEntry(); 1686 setAuthorizationEntry(authorizationEntry); 1687 if (authorizationEntry == null) 1688 { 1689 setProxiedAuthorizationDN(DN.nullDN()); 1690 } 1691 else 1692 { 1693 setProxiedAuthorizationDN(authorizationEntry.getDN()); 1694 } 1695 } 1696 else if (oid.equals(OID_PASSWORD_POLICY_CONTROL)) 1697 { 1698 // We don't need to do anything here because it's already handled 1699 // in LocalBackendAddOperation.handlePasswordPolicy(). 1700 } 1701 1702 // NYI -- Add support for additional controls. 1703 else if (c.isCritical()) 1704 { 1705 if ((backend == null) || (! backend.supportsControl(oid))) 1706 { 1707 throw new DirectoryException( 1708 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION, 1709 ERR_ADD_UNSUPPORTED_CRITICAL_CONTROL.get( 1710 String.valueOf(entryDN), oid)); 1711 } 1712 } 1713 } 1714 } 1715 } 1716 1717 1718 1719 /** 1720 * Adds the post-read response control to the response. 1721 */ 1722 private void addPostReadResponse() 1723 { 1724 Entry addedEntry = entry.duplicate(true); 1725 1726 if (! postReadRequest.allowsAttribute( 1727 DirectoryServer.getObjectClassAttributeType())) 1728 { 1729 addedEntry.removeAttribute(DirectoryServer.getObjectClassAttributeType()); 1730 } 1731 1732 if (! postReadRequest.returnAllUserAttributes()) 1733 { 1734 Iterator<AttributeType> iterator = 1735 addedEntry.getUserAttributes().keySet().iterator(); 1736 while (iterator.hasNext()) 1737 { 1738 AttributeType attrType = iterator.next(); 1739 if (! postReadRequest.allowsAttribute(attrType)) 1740 { 1741 iterator.remove(); 1742 } 1743 } 1744 } 1745 1746 if (! postReadRequest.returnAllOperationalAttributes()) 1747 { 1748 Iterator<AttributeType> iterator = 1749 addedEntry.getOperationalAttributes().keySet().iterator(); 1750 while (iterator.hasNext()) 1751 { 1752 AttributeType attrType = iterator.next(); 1753 if (! postReadRequest.allowsAttribute(attrType)) 1754 { 1755 iterator.remove(); 1756 } 1757 } 1758 } 1759 1760 // FIXME -- Check access controls on the entry to see if it should 1761 // be returned or if any attributes need to be stripped 1762 // out.. 1763 SearchResultEntry searchEntry = new SearchResultEntry(addedEntry); 1764 LDAPPostReadResponseControl responseControl = 1765 new LDAPPostReadResponseControl(postReadRequest.getOID(), 1766 postReadRequest.isCritical(), 1767 searchEntry); 1768 addResponseControl(responseControl); 1769 } 1770 } 1771