001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.server.core; 021 022 023 import java.io.BufferedReader; 024 import java.io.File; 025 import java.io.IOException; 026 import java.io.StringReader; 027 import java.util.ArrayList; 028 import java.util.Arrays; 029 import java.util.HashSet; 030 import java.util.List; 031 import java.util.Map; 032 import java.util.Set; 033 import java.util.UUID; 034 035 import javax.naming.directory.Attributes; 036 037 import org.apache.directory.server.constants.ServerDNConstants; 038 import org.apache.directory.server.core.authn.AuthenticationInterceptor; 039 import org.apache.directory.server.core.authz.AciAuthorizationInterceptor; 040 import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor; 041 import org.apache.directory.server.core.changelog.ChangeLog; 042 import org.apache.directory.server.core.changelog.ChangeLogEvent; 043 import org.apache.directory.server.core.changelog.ChangeLogInterceptor; 044 import org.apache.directory.server.core.changelog.DefaultChangeLog; 045 import org.apache.directory.server.core.changelog.Tag; 046 import org.apache.directory.server.core.changelog.TaggableSearchableChangeLogStore; 047 import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor; 048 import org.apache.directory.server.core.event.EventInterceptor; 049 import org.apache.directory.server.core.event.EventService; 050 import org.apache.directory.server.core.exception.ExceptionInterceptor; 051 import org.apache.directory.server.core.interceptor.Interceptor; 052 import org.apache.directory.server.core.interceptor.InterceptorChain; 053 import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext; 054 import org.apache.directory.server.core.interceptor.context.AddOperationContext; 055 import org.apache.directory.server.core.interceptor.context.BindOperationContext; 056 import org.apache.directory.server.core.interceptor.context.EntryOperationContext; 057 import org.apache.directory.server.core.interceptor.context.LookupOperationContext; 058 import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext; 059 import org.apache.directory.server.core.journal.DefaultJournal; 060 import org.apache.directory.server.core.journal.Journal; 061 import org.apache.directory.server.core.journal.JournalInterceptor; 062 import org.apache.directory.server.core.normalization.NormalizationInterceptor; 063 import org.apache.directory.server.core.operational.OperationalAttributeInterceptor; 064 import org.apache.directory.server.core.partition.DefaultPartitionNexus; 065 import org.apache.directory.server.core.partition.Partition; 066 import org.apache.directory.server.core.partition.PartitionNexus; 067 import org.apache.directory.server.core.referral.ReferralInterceptor; 068 import org.apache.directory.server.core.replication.ReplicationConfiguration; 069 import org.apache.directory.server.core.schema.DefaultSchemaService; 070 import org.apache.directory.server.core.schema.SchemaInterceptor; 071 import org.apache.directory.server.core.schema.SchemaService; 072 import org.apache.directory.server.core.security.TlsKeyGenerator; 073 import org.apache.directory.server.core.subtree.SubentryInterceptor; 074 import org.apache.directory.server.core.trigger.TriggerInterceptor; 075 import org.apache.directory.server.i18n.I18n; 076 import org.apache.directory.shared.ldap.NotImplementedException; 077 import org.apache.directory.shared.ldap.constants.AuthenticationLevel; 078 import org.apache.directory.shared.ldap.constants.SchemaConstants; 079 import org.apache.directory.shared.ldap.csn.Csn; 080 import org.apache.directory.shared.ldap.csn.CsnFactory; 081 import org.apache.directory.shared.ldap.cursor.Cursor; 082 import org.apache.directory.shared.ldap.entry.DefaultServerEntry; 083 import org.apache.directory.shared.ldap.entry.Entry; 084 import org.apache.directory.shared.ldap.entry.EntryAttribute; 085 import org.apache.directory.shared.ldap.entry.Modification; 086 import org.apache.directory.shared.ldap.entry.ServerEntry; 087 import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry; 088 import org.apache.directory.shared.ldap.exception.LdapException; 089 import org.apache.directory.shared.ldap.exception.LdapNoPermissionException; 090 import org.apache.directory.shared.ldap.ldif.ChangeType; 091 import org.apache.directory.shared.ldap.ldif.LdifEntry; 092 import org.apache.directory.shared.ldap.ldif.LdifReader; 093 import org.apache.directory.shared.ldap.name.DN; 094 import org.apache.directory.shared.ldap.name.RDN; 095 import org.apache.directory.shared.ldap.schema.SchemaManager; 096 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer; 097 import org.apache.directory.shared.ldap.util.DateUtils; 098 import org.apache.directory.shared.ldap.util.StringTools; 099 import org.slf4j.Logger; 100 import org.slf4j.LoggerFactory; 101 102 103 /** 104 * Default implementation of {@link DirectoryService}. 105 * 106 * @org.apache.xbean.XBean 107 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 108 */ 109 public class DefaultDirectoryService implements DirectoryService 110 { 111 /** The logger */ 112 private static final Logger LOG = LoggerFactory.getLogger( DefaultDirectoryService.class ); 113 114 private SchemaService schemaService; 115 116 /** A reference on the SchemaManager */ 117 private SchemaManager schemaManager; 118 119 /** the root nexus */ 120 private DefaultPartitionNexus partitionNexus; 121 122 /** whether or not server is started for the first time */ 123 private boolean firstStart; 124 125 /** The interceptor (or interceptor chain) for this service */ 126 private InterceptorChain interceptorChain; 127 128 /** whether or not this instance has been shutdown */ 129 private boolean started; 130 131 /** the change log service */ 132 private ChangeLog changeLog; 133 134 /** the journal service */ 135 private Journal journal; 136 137 /** 138 * the interface used to perform various operations on this 139 * DirectoryService 140 */ 141 private OperationManager operationManager = new DefaultOperationManager( this ); 142 143 /** the distinguished name of the administrative user */ 144 private DN adminDn; 145 146 /** session used as admin for internal operations */ 147 private CoreSession adminSession; 148 149 /** The referral manager */ 150 private ReferralManager referralManager; 151 152 /** A flag to tell if the userPassword attribute's value must be hidden */ 153 private boolean passwordHidden = false; 154 155 /** The service's CSN factory */ 156 private CsnFactory csnFactory; 157 158 /** The directory instance replication ID */ 159 private int replicaId; 160 161 /** The replication configuration structure */ 162 private ReplicationConfiguration replicationConfig; 163 164 /** remove me after implementation is completed */ 165 private static final String PARTIAL_IMPL_WARNING = 166 "WARNING: the changelog is only partially operational and will revert\n" + 167 "state without consideration of who made the original change. All reverting " + 168 "changes are made by the admin user.\n Furthermore the used controls are not at " + 169 "all taken into account"; 170 171 172 /** The delay to wait between each sync on disk */ 173 private long syncPeriodMillis; 174 175 /** The default delay to wait between sync on disk : 15 seconds */ 176 private static final long DEFAULT_SYNC_PERIOD = 15000; 177 178 /** */ 179 private Thread workerThread; 180 181 /** The sync worker thread */ 182 private SynchWorker worker = new SynchWorker(); 183 184 185 /** The default timeLimit : 100 entries */ 186 public static final int MAX_SIZE_LIMIT_DEFAULT = 100; 187 188 /** The default timeLimit : 10 seconds */ 189 public static final int MAX_TIME_LIMIT_DEFAULT = 10000; 190 191 /** The instance Id */ 192 private String instanceId; 193 194 /** The server working directory */ 195 private File workingDirectory = new File( "server-work" ); 196 197 /** 198 * A flag used to shutdown the VM when stopping the server. Useful 199 * when the server is standalone. If the server is embedded, we don't 200 * want to shutdown the VM 201 */ 202 private boolean exitVmOnShutdown = true; // allow by default 203 204 /** A flag used to indicate that a shutdown hook has been installed */ 205 private boolean shutdownHookEnabled = true; // allow by default 206 207 /** Manage anonymous access to entries other than the RootDSE */ 208 private boolean allowAnonymousAccess = true; // allow by default 209 210 /** Manage the basic access control checks */ 211 private boolean accessControlEnabled; // off by default 212 213 /** Manage the operational attributes denormalization */ 214 private boolean denormalizeOpAttrsEnabled; // off by default 215 216 /** The list of declared interceptors */ 217 private List<Interceptor> interceptors; 218 219 /** The System partition */ 220 private Partition systemPartition; 221 222 /** The set of all declared partitions */ 223 private Set<Partition> partitions = new HashSet<Partition>(); 224 225 /** A list of LDIF entries to inject at startup */ 226 private List<? extends LdifEntry> testEntries = new ArrayList<LdifEntry>(); // List<Attributes> 227 228 /** The event service */ 229 private EventService eventService; 230 231 /** The maximum size for an incoming PDU */ 232 private int maxPDUSize = Integer.MAX_VALUE; 233 234 235 236 /** 237 * The synchronizer thread. It flush data on disk periodically. 238 */ 239 class SynchWorker implements Runnable 240 { 241 final Object lock = new Object(); 242 243 /** A flag to stop the thread */ 244 boolean stop; 245 246 247 /** 248 * The main loop 249 */ 250 public void run() 251 { 252 while ( !stop ) 253 { 254 synchronized ( lock ) 255 { 256 try 257 { 258 lock.wait( syncPeriodMillis ); 259 } 260 catch ( InterruptedException e ) 261 { 262 LOG.warn( "SynchWorker failed to wait on lock.", e ); 263 } 264 } 265 266 try 267 { 268 partitionNexus.sync(); 269 } 270 catch ( Exception e ) 271 { 272 LOG.error( I18n.err( I18n.ERR_74 ), e ); 273 } 274 } 275 } 276 } 277 278 279 // ------------------------------------------------------------------------ 280 // Constructor 281 // ------------------------------------------------------------------------ 282 283 284 /** 285 * Creates a new instance of the directory service. 286 */ 287 public DefaultDirectoryService() throws Exception 288 { 289 setDefaultInterceptorConfigurations(); 290 changeLog = new DefaultChangeLog(); 291 journal = new DefaultJournal(); 292 syncPeriodMillis = DEFAULT_SYNC_PERIOD; 293 csnFactory = new CsnFactory( replicaId ); 294 schemaService = new DefaultSchemaService(); 295 } 296 297 298 // ------------------------------------------------------------------------ 299 // C O N F I G U R A T I O N M E T H O D S 300 // ------------------------------------------------------------------------ 301 302 303 public void setInstanceId( String instanceId ) 304 { 305 this.instanceId = instanceId; 306 } 307 308 309 public String getInstanceId() 310 { 311 return instanceId; 312 } 313 314 315 /** 316 * Gets the {@link Partition}s used by this DirectoryService. 317 * 318 * @return the set of partitions used 319 */ 320 public Set<? extends Partition> getPartitions() 321 { 322 Set<Partition> cloned = new HashSet<Partition>(); 323 cloned.addAll( partitions ); 324 return cloned; 325 } 326 327 328 /** 329 * Sets {@link Partition}s used by this DirectoryService. 330 * 331 * @param partitions the partitions to used 332 */ 333 public void setPartitions( Set<? extends Partition> partitions ) 334 { 335 Set<Partition> cloned = new HashSet<Partition>(); 336 cloned.addAll( partitions ); 337 Set<String> names = new HashSet<String>(); 338 339 for ( Partition partition : cloned ) 340 { 341 String id = partition.getId(); 342 343 if ( names.contains( id ) ) 344 { 345 LOG.warn( "Encountered duplicate partition {} identifier.", id ); 346 } 347 348 names.add( id ); 349 } 350 351 this.partitions = cloned; 352 } 353 354 355 /** 356 * Returns <tt>true</tt> if access control checks are enabled. 357 * 358 * @return true if access control checks are enabled, false otherwise 359 */ 360 public boolean isAccessControlEnabled() 361 { 362 return accessControlEnabled; 363 } 364 365 366 /** 367 * Sets whether to enable basic access control checks or not. 368 * 369 * @param accessControlEnabled true to enable access control checks, false otherwise 370 */ 371 public void setAccessControlEnabled( boolean accessControlEnabled ) 372 { 373 this.accessControlEnabled = accessControlEnabled; 374 } 375 376 377 /** 378 * Returns <tt>true</tt> if anonymous access is allowed on entries besides the RootDSE. 379 * If the access control subsystem is enabled then access to some entries may not be 380 * allowed even when full anonymous access is enabled. 381 * 382 * @return true if anonymous access is allowed on entries besides the RootDSE, false 383 * if anonymous access is allowed to all entries. 384 */ 385 public boolean isAllowAnonymousAccess() 386 { 387 return allowAnonymousAccess; 388 } 389 390 391 /** 392 * Sets whether to allow anonymous access to entries other than the RootDSE. If the 393 * access control subsystem is enabled then access to some entries may not be allowed 394 * even when full anonymous access is enabled. 395 * 396 * @param enableAnonymousAccess true to enable anonymous access, false to disable it 397 */ 398 public void setAllowAnonymousAccess( boolean enableAnonymousAccess ) 399 { 400 this.allowAnonymousAccess = enableAnonymousAccess; 401 } 402 403 404 /** 405 * Returns interceptors in the server. 406 * 407 * @return the interceptors in the server. 408 */ 409 public List<Interceptor> getInterceptors() 410 { 411 List<Interceptor> cloned = new ArrayList<Interceptor>(); 412 cloned.addAll( interceptors ); 413 return cloned; 414 } 415 416 417 /** 418 * Sets the interceptors in the server. 419 * 420 * @param interceptors the interceptors to be used in the server. 421 */ 422 public void setInterceptors( List<Interceptor> interceptors ) 423 { 424 Set<String> names = new HashSet<String>(); 425 426 for ( Interceptor interceptor : interceptors ) 427 { 428 String name = interceptor.getName(); 429 430 if ( names.contains( name ) ) 431 { 432 LOG.warn( "Encountered duplicate definitions for {} interceptor", interceptor.getName() ); 433 } 434 names.add( name ); 435 } 436 437 this.interceptors = interceptors; 438 } 439 440 441 /** 442 * Returns test directory entries({@link LdifEntry}) to be loaded while 443 * bootstrapping. 444 * 445 * @return test entries to load during bootstrapping 446 */ 447 public List<LdifEntry> getTestEntries() 448 { 449 List<LdifEntry> cloned = new ArrayList<LdifEntry>(); 450 cloned.addAll( testEntries ); 451 return cloned; 452 } 453 454 455 /** 456 * Sets test directory entries({@link Attributes}) to be loaded while 457 * bootstrapping. 458 * 459 * @param testEntries the test entries to load while bootstrapping 460 */ 461 public void setTestEntries( List<? extends LdifEntry> testEntries ) 462 { 463 //noinspection MismatchedQueryAndUpdateOfCollection 464 List<LdifEntry> cloned = new ArrayList<LdifEntry>(); 465 cloned.addAll( testEntries ); 466 this.testEntries = testEntries; 467 } 468 469 470 /** 471 * Returns working directory (counterpart of <tt>var/lib</tt>) where partitions are 472 * stored by default. 473 * 474 * @return the directory where partition's are stored. 475 */ 476 public File getWorkingDirectory() 477 { 478 return workingDirectory; 479 } 480 481 482 /** 483 * Sets working directory (counterpart of <tt>var/lib</tt>) where partitions are stored 484 * by default. 485 * 486 * @param workingDirectory the directory where the server's partitions are stored by default. 487 */ 488 public void setWorkingDirectory( File workingDirectory ) 489 { 490 this.workingDirectory = workingDirectory; 491 } 492 493 494 public void setShutdownHookEnabled( boolean shutdownHookEnabled ) 495 { 496 this.shutdownHookEnabled = shutdownHookEnabled; 497 } 498 499 500 public boolean isShutdownHookEnabled() 501 { 502 return shutdownHookEnabled; 503 } 504 505 506 public void setExitVmOnShutdown( boolean exitVmOnShutdown ) 507 { 508 this.exitVmOnShutdown = exitVmOnShutdown; 509 } 510 511 512 public boolean isExitVmOnShutdown() 513 { 514 return exitVmOnShutdown; 515 } 516 517 518 public void setSystemPartition( Partition systemPartition ) 519 { 520 this.systemPartition = systemPartition; 521 } 522 523 524 public Partition getSystemPartition() 525 { 526 return systemPartition; 527 } 528 529 530 /** 531 * return true if the operational attributes must be normalized when returned 532 */ 533 public boolean isDenormalizeOpAttrsEnabled() 534 { 535 return denormalizeOpAttrsEnabled; 536 } 537 538 539 /** 540 * Sets whether the operational attributes are denormalized when returned 541 * @param denormalizeOpAttrsEnabled The flag value 542 */ 543 public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled ) 544 { 545 this.denormalizeOpAttrsEnabled = denormalizeOpAttrsEnabled; 546 } 547 548 549 /** 550 * {@inheritDoc} 551 */ 552 public ChangeLog getChangeLog() 553 { 554 return changeLog; 555 } 556 557 558 /** 559 * {@inheritDoc} 560 */ 561 public Journal getJournal() 562 { 563 return journal; 564 } 565 566 567 /** 568 * {@inheritDoc} 569 */ 570 public void setChangeLog( ChangeLog changeLog ) 571 { 572 this.changeLog = changeLog; 573 } 574 575 576 /** 577 * {@inheritDoc} 578 */ 579 public void setJournal( Journal journal ) 580 { 581 this.journal = journal; 582 } 583 584 585 public void addPartition( Partition partition ) throws Exception 586 { 587 partition.setSchemaManager( schemaManager ); 588 partitions.add( partition ); 589 590 if ( ! started ) 591 { 592 return; 593 } 594 595 AddContextPartitionOperationContext addPartitionCtx = 596 new AddContextPartitionOperationContext( adminSession, partition ); 597 partitionNexus.addContextPartition( addPartitionCtx ); 598 } 599 600 601 public void removePartition( Partition partition ) throws Exception 602 { 603 partitions.remove( partition ); 604 605 if ( ! started ) 606 { 607 return; 608 } 609 610 RemoveContextPartitionOperationContext removePartitionCtx = 611 new RemoveContextPartitionOperationContext( adminSession, partition.getSuffixDn() ); 612 partitionNexus.removeContextPartition( removePartitionCtx ); 613 } 614 615 616 // ------------------------------------------------------------------------ 617 // BackendSubsystem Interface Method Implementations 618 // ------------------------------------------------------------------------ 619 620 621 private void setDefaultInterceptorConfigurations() 622 { 623 // Set default interceptor chains 624 List<Interceptor> list = new ArrayList<Interceptor>(); 625 626 list.add( new NormalizationInterceptor() ); 627 list.add( new AuthenticationInterceptor() ); 628 list.add( new ReferralInterceptor() ); 629 list.add( new AciAuthorizationInterceptor() ); 630 list.add( new DefaultAuthorizationInterceptor() ); 631 list.add( new ExceptionInterceptor() ); 632 list.add( new ChangeLogInterceptor() ); 633 list.add( new OperationalAttributeInterceptor() ); 634 list.add( new SchemaInterceptor() ); 635 list.add( new SubentryInterceptor() ); 636 list.add( new CollectiveAttributeInterceptor() ); 637 list.add( new EventInterceptor() ); 638 list.add( new TriggerInterceptor() ); 639 list.add( new JournalInterceptor() ); 640 641 setInterceptors( list ); 642 } 643 644 645 public CoreSession getAdminSession() 646 { 647 return adminSession; 648 } 649 650 651 public CoreSession getSession() 652 { 653 return new DefaultCoreSession( new LdapPrincipal(), this ); 654 } 655 656 657 public CoreSession getSession( LdapPrincipal principal ) 658 { 659 return new DefaultCoreSession( principal, this ); 660 } 661 662 663 public CoreSession getSession( DN principalDn, byte[] credentials ) 664 throws Exception 665 { 666 if ( ! started ) 667 { 668 throw new IllegalStateException( "Service has not started." ); 669 } 670 671 BindOperationContext bindContext = new BindOperationContext( null ); 672 bindContext.setCredentials( credentials ); 673 bindContext.setDn( principalDn ); 674 operationManager.bind( bindContext ); 675 676 return bindContext.getSession(); 677 } 678 679 680 public CoreSession getSession( DN principalDn, byte[] credentials, String saslMechanism, String saslAuthId ) 681 throws Exception 682 { 683 if ( ! started ) 684 { 685 throw new IllegalStateException( "Service has not started." ); 686 } 687 688 BindOperationContext bindContext = new BindOperationContext( null ); 689 bindContext.setCredentials( credentials ); 690 bindContext.setDn( principalDn ); 691 bindContext.setSaslMechanism( saslMechanism ); 692 operationManager.bind( bindContext ); 693 694 return bindContext.getSession(); 695 } 696 697 698 public long revert() throws Exception 699 { 700 if ( changeLog == null || ! changeLog.isEnabled() ) 701 { 702 throw new IllegalStateException( I18n.err( I18n.ERR_310 ) ); 703 } 704 705 Tag latest = changeLog.getLatest(); 706 707 if ( null != latest ) 708 { 709 if ( latest.getRevision() < changeLog.getCurrentRevision() ) 710 { 711 return revert( latest.getRevision() ); 712 } 713 else 714 { 715 LOG.info( "Ignoring request to revert without changes since the latest tag." ); 716 return changeLog.getCurrentRevision(); 717 } 718 } 719 720 throw new IllegalStateException( I18n.err( I18n.ERR_311 ) ); 721 } 722 723 724 /** 725 * We handle the ModDN/ModRDN operation for the revert here. 726 */ 727 private void moddn( DN oldDn, DN newDn, boolean delOldRdn ) throws Exception 728 { 729 if ( oldDn.size() == 0 ) 730 { 731 throw new LdapNoPermissionException( I18n.err( I18n.ERR_312 ) ); 732 } 733 734 // calculate parents 735 DN oldBase = ( DN ) oldDn.clone(); 736 oldBase.remove( oldDn.size() - 1 ); 737 DN newBase = ( DN ) newDn.clone(); 738 newBase.remove( newDn.size() - 1 ); 739 740 // Compute the RDN for each of the DN 741 RDN newRdn = newDn.getRdn( newDn.size() - 1 ); 742 RDN oldRdn = oldDn.getRdn( oldDn.size() - 1 ); 743 744 /* 745 * We need to determine if this rename operation corresponds to a simple 746 * RDN name change or a move operation. If the two names are the same 747 * except for the RDN then it is a simple modifyRdn operation. If the 748 * names differ in size or have a different baseDN then the operation is 749 * a move operation. Furthermore if the RDN in the move operation 750 * changes it is both an RDN change and a move operation. 751 */ 752 if ( ( oldDn.size() == newDn.size() ) && oldBase.equals( newBase ) ) 753 { 754 adminSession.rename( oldDn, newRdn, delOldRdn ); 755 } 756 else 757 { 758 DN target = ( DN ) newDn.clone(); 759 target.remove( newDn.size() - 1 ); 760 761 if ( newRdn.equals( oldRdn ) ) 762 { 763 adminSession.move( oldDn, target ); 764 } 765 else 766 { 767 adminSession.moveAndRename( oldDn, target, new RDN( newRdn ), delOldRdn ); 768 } 769 } 770 } 771 772 773 public long revert( long revision ) throws Exception 774 { 775 if ( changeLog == null || ! changeLog.isEnabled() ) 776 { 777 throw new IllegalStateException( I18n.err( I18n.ERR_310 ) ); 778 } 779 780 if ( revision < 0 ) 781 { 782 throw new IllegalArgumentException( I18n.err( I18n.ERR_239 ) ); 783 } 784 785 if ( revision >= changeLog.getChangeLogStore().getCurrentRevision() ) 786 { 787 throw new IllegalArgumentException( I18n.err( I18n.ERR_314 ) ); 788 } 789 790 Cursor<ChangeLogEvent> cursor = changeLog.getChangeLogStore().findAfter( revision ); 791 792 /* 793 * BAD, BAD, BAD!!! 794 * 795 * No synchronization no nothing. Just getting this to work for now 796 * so we can revert tests. Any production grade use of this feature 797 * needs to synchronize on all changes while the revert is in progress. 798 * 799 * How about making this operation transactional? 800 * 801 * First of all just stop using JNDI and construct the operations to 802 * feed into the interceptor pipeline. 803 * 804 * TODO review this code. 805 */ 806 807 try 808 { 809 LOG.warn( PARTIAL_IMPL_WARNING ); 810 cursor.afterLast(); 811 812 while ( cursor.previous() ) // apply ldifs in reverse order 813 { 814 ChangeLogEvent event = cursor.get(); 815 List<LdifEntry> reverses = event.getReverseLdifs(); 816 817 for ( LdifEntry reverse:reverses ) 818 { 819 switch( reverse.getChangeType().getChangeType() ) 820 { 821 case ChangeType.ADD_ORDINAL : 822 adminSession.add( 823 new DefaultServerEntry( schemaManager, reverse.getEntry() ), true ); 824 break; 825 826 case ChangeType.DELETE_ORDINAL : 827 adminSession.delete( reverse.getDn(), true ); 828 break; 829 830 case ChangeType.MODIFY_ORDINAL : 831 List<Modification> mods = reverse.getModificationItems(); 832 833 adminSession.modify( reverse.getDn(), mods, true ); 834 break; 835 836 case ChangeType.MODDN_ORDINAL : 837 // NO BREAK - both ModDN and ModRDN handling is the same 838 839 case ChangeType.MODRDN_ORDINAL : 840 DN forwardDn = event.getForwardLdif().getDn(); 841 DN reverseDn = reverse.getDn(); 842 843 moddn( reverseDn, forwardDn, reverse.isDeleteOldRdn() ); 844 845 break; 846 847 default: 848 LOG.error( I18n.err( I18n.ERR_75 ) ); 849 throw new NotImplementedException( I18n.err( I18n.ERR_76, reverse.getChangeType() ) ); 850 } 851 } 852 } 853 } 854 catch ( IOException e ) 855 { 856 String message = I18n.err( I18n.ERR_77, revision ); 857 LOG.error( message ); 858 throw new LdapException( message ); 859 } 860 861 return changeLog.getCurrentRevision(); 862 } 863 864 865 public OperationManager getOperationManager() 866 { 867 return operationManager; 868 } 869 870 871 /** 872 * @throws Exception if the LDAP server cannot be started 873 */ 874 public synchronized void startup() throws Exception 875 { 876 if ( started ) 877 { 878 return; 879 } 880 881 if ( shutdownHookEnabled ) 882 { 883 Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() 884 { 885 public void run() 886 { 887 try 888 { 889 shutdown(); 890 } 891 catch ( Exception e ) 892 { 893 LOG.warn( "Failed to shut down the directory service: " 894 + DefaultDirectoryService.this.instanceId, e ); 895 } 896 } 897 }, "ApacheDS Shutdown Hook (" + instanceId + ')' ) ); 898 899 LOG.info( "ApacheDS shutdown hook has been registered with the runtime." ); 900 } 901 else if ( LOG.isWarnEnabled() ) 902 { 903 LOG.warn( "ApacheDS shutdown hook has NOT been registered with the runtime." 904 + " This default setting for standalone operation has been overriden." ); 905 } 906 907 initialize(); 908 showSecurityWarnings(); 909 910 // Start the sync thread if required 911 if ( syncPeriodMillis > 0 ) 912 { 913 workerThread = new Thread( worker, "SynchWorkerThread" ); 914 workerThread.start(); 915 } 916 917 918 started = true; 919 920 if ( !testEntries.isEmpty() ) 921 { 922 createTestEntries(); 923 } 924 } 925 926 927 public synchronized void sync() throws Exception 928 { 929 if ( !started ) 930 { 931 return; 932 } 933 934 this.changeLog.sync(); 935 this.partitionNexus.sync(); 936 } 937 938 939 public synchronized void shutdown() throws Exception 940 { 941 if ( !started ) 942 { 943 return; 944 } 945 946 // -------------------------------------------------------------------- 947 // Shutdown the changelog 948 // -------------------------------------------------------------------- 949 changeLog.sync(); 950 changeLog.destroy(); 951 952 // -------------------------------------------------------------------- 953 // Shutdown the journal if enabled 954 // -------------------------------------------------------------------- 955 if ( journal.isEnabled() ) 956 { 957 journal.destroy(); 958 } 959 960 // -------------------------------------------------------------------- 961 // Shutdown the partition 962 // -------------------------------------------------------------------- 963 964 partitionNexus.sync(); 965 partitionNexus.destroy(); 966 967 // -------------------------------------------------------------------- 968 // Shutdown the sync thread 969 // -------------------------------------------------------------------- 970 if ( workerThread != null ) 971 { 972 worker.stop = true; 973 974 synchronized ( worker.lock ) 975 { 976 worker.lock.notify(); 977 } 978 979 while ( workerThread.isAlive() ) 980 { 981 LOG.info( "Waiting for SynchWorkerThread to die." ); 982 workerThread.join( 500 ); 983 } 984 } 985 986 987 // -------------------------------------------------------------------- 988 // And shutdown the server 989 // -------------------------------------------------------------------- 990 interceptorChain.destroy(); 991 started = false; 992 setDefaultInterceptorConfigurations(); 993 } 994 995 996 /** 997 * @return The referral manager 998 */ 999 public ReferralManager getReferralManager() 1000 { 1001 return referralManager; 1002 } 1003 1004 /** 1005 * Set the referralManager 1006 * @param referralManager The initialized referralManager 1007 */ 1008 public void setReferralManager( ReferralManager referralManager ) 1009 { 1010 this.referralManager = referralManager; 1011 } 1012 1013 1014 /** 1015 * @return the SchemaManager 1016 */ 1017 public SchemaManager getSchemaManager() 1018 { 1019 return schemaManager; 1020 } 1021 1022 1023 /** 1024 * Set the SchemaManager instance. 1025 * 1026 * @param schemaManager The schemaManager 1027 */ 1028 public void setSchemaManager( SchemaManager schemaManager ) 1029 { 1030 this.schemaManager = schemaManager; 1031 } 1032 1033 1034 public SchemaService getSchemaService() 1035 { 1036 return schemaService; 1037 } 1038 1039 1040 public void setSchemaService( SchemaService schemaService ) 1041 { 1042 this.schemaService = schemaService; 1043 } 1044 1045 1046 public DefaultPartitionNexus getPartitionNexus() 1047 { 1048 return partitionNexus; 1049 } 1050 1051 1052 public InterceptorChain getInterceptorChain() 1053 { 1054 return interceptorChain; 1055 } 1056 1057 1058 public boolean isFirstStart() 1059 { 1060 return firstStart; 1061 } 1062 1063 1064 public boolean isStarted() 1065 { 1066 return started; 1067 } 1068 1069 1070 public ServerEntry newEntry( DN dn ) 1071 { 1072 return new DefaultServerEntry( schemaManager, dn ); 1073 } 1074 1075 1076 /** 1077 * Returns true if we had to create the bootstrap entries on the first 1078 * start of the server. Otherwise if all entries exist, meaning none 1079 * had to be created, then we are not starting for the first time. 1080 * 1081 * @return true if the bootstrap entries had to be created, false otherwise 1082 * @throws Exception if entries cannot be created 1083 */ 1084 private boolean createBootstrapEntries() throws Exception 1085 { 1086 boolean firstStart = false; 1087 1088 // ------------------------------------------------------------------- 1089 // create admin entry 1090 // ------------------------------------------------------------------- 1091 1092 /* 1093 * If the admin entry is there, then the database was already created 1094 */ 1095 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, adminDn ) ) ) 1096 { 1097 firstStart = true; 1098 1099 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, adminDn ); 1100 1101 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 1102 SchemaConstants.TOP_OC, 1103 SchemaConstants.PERSON_OC, 1104 SchemaConstants.ORGANIZATIONAL_PERSON_OC, 1105 SchemaConstants.INET_ORG_PERSON_OC ); 1106 1107 serverEntry.put( SchemaConstants.UID_AT, PartitionNexus.ADMIN_UID ); 1108 serverEntry.put( SchemaConstants.USER_PASSWORD_AT, PartitionNexus.ADMIN_PASSWORD_BYTES ); 1109 serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" ); 1110 serverEntry.put( SchemaConstants.CN_AT, "system administrator" ); 1111 serverEntry.put( SchemaConstants.SN_AT, "administrator" ); 1112 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1113 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1114 serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" ); 1115 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1116 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1117 1118 TlsKeyGenerator.addKeyPair( serverEntry ); 1119 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1120 } 1121 1122 // ------------------------------------------------------------------- 1123 // create system users area 1124 // ------------------------------------------------------------------- 1125 1126 Map<String,OidNormalizer> oidsMap = schemaManager.getNormalizerMapping(); 1127 DN userDn = new DN( ServerDNConstants.USERS_SYSTEM_DN ); 1128 userDn.normalize( oidsMap ); 1129 1130 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, userDn ) ) ) 1131 { 1132 firstStart = true; 1133 1134 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, userDn ); 1135 1136 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 1137 SchemaConstants.TOP_OC, 1138 SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 1139 1140 serverEntry.put( SchemaConstants.OU_AT, "users" ); 1141 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1142 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1143 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1144 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1145 1146 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1147 } 1148 1149 // ------------------------------------------------------------------- 1150 // create system groups area 1151 // ------------------------------------------------------------------- 1152 1153 DN groupDn = new DN( ServerDNConstants.GROUPS_SYSTEM_DN ); 1154 groupDn.normalize( oidsMap ); 1155 1156 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, groupDn ) ) ) 1157 { 1158 firstStart = true; 1159 1160 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, groupDn ); 1161 1162 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 1163 SchemaConstants.TOP_OC, 1164 SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 1165 1166 serverEntry.put( SchemaConstants.OU_AT, "groups" ); 1167 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1168 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1169 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1170 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1171 1172 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1173 } 1174 1175 // ------------------------------------------------------------------- 1176 // create administrator group 1177 // ------------------------------------------------------------------- 1178 1179 DN name = new DN( ServerDNConstants.ADMINISTRATORS_GROUP_DN ); 1180 name.normalize( oidsMap ); 1181 1182 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, name ) ) ) 1183 { 1184 firstStart = true; 1185 1186 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, name ); 1187 1188 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 1189 SchemaConstants.TOP_OC, 1190 SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC ); 1191 1192 serverEntry.put( SchemaConstants.CN_AT, "Administrators" ); 1193 serverEntry.put( SchemaConstants.UNIQUE_MEMBER_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1194 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1195 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1196 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1197 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1198 1199 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1200 1201 // TODO - confirm if we need this at all since the 1202 // group cache on initialization after this stage will 1203 // search the directory for all the groups anyway 1204 1205 // Interceptor authzInterceptor = interceptorChain.get( AciAuthorizationInterceptor.class.getName() ); 1206 // 1207 // if ( authzInterceptor == null ) 1208 // { 1209 // LOG.error( "The Authorization service is null : this is not allowed" ); 1210 // throw new NamingException( "The Authorization service is null" ); 1211 // } 1212 // 1213 // if ( !( authzInterceptor instanceof AciAuthorizationInterceptor ) ) 1214 // { 1215 // LOG.error( "The Authorization service is not set correctly : '{}' is an incorect interceptor", 1216 // authzInterceptor.getClass().getName() ); 1217 // throw new NamingException( "The Authorization service is incorrectly set" ); 1218 // 1219 // } 1220 // 1221 // AciAuthorizationInterceptor authzSrvc = ( AciAuthorizationInterceptor ) authzInterceptor; 1222 // authzSrvc.cacheNewGroup( name, serverEntry ); 1223 } 1224 1225 // ------------------------------------------------------------------- 1226 // create system configuration area 1227 // ------------------------------------------------------------------- 1228 1229 DN configurationDn = new DN( "ou=configuration,ou=system" ); 1230 configurationDn.normalize( oidsMap ); 1231 1232 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, configurationDn ) ) ) 1233 { 1234 firstStart = true; 1235 1236 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, configurationDn ); 1237 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 1238 1239 serverEntry.put( SchemaConstants.OU_AT, "configuration" ); 1240 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1241 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1242 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1243 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1244 1245 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1246 } 1247 1248 // ------------------------------------------------------------------- 1249 // create system configuration area for partition information 1250 // ------------------------------------------------------------------- 1251 1252 DN partitionsDn = new DN( "ou=partitions,ou=configuration,ou=system" ); 1253 partitionsDn.normalize( oidsMap ); 1254 1255 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, partitionsDn ) ) ) 1256 { 1257 firstStart = true; 1258 1259 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, partitionsDn ); 1260 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 1261 serverEntry.put( SchemaConstants.OU_AT, "partitions" ); 1262 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1263 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1264 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1265 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1266 1267 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1268 } 1269 1270 // ------------------------------------------------------------------- 1271 // create system configuration area for services 1272 // ------------------------------------------------------------------- 1273 1274 DN servicesDn = new DN( "ou=services,ou=configuration,ou=system" ); 1275 servicesDn.normalize( oidsMap ); 1276 1277 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, servicesDn ) ) ) 1278 { 1279 firstStart = true; 1280 1281 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, servicesDn ); 1282 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 1283 1284 serverEntry.put( SchemaConstants.OU_AT, "services" ); 1285 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1286 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1287 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1288 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1289 1290 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1291 } 1292 1293 // ------------------------------------------------------------------- 1294 // create system configuration area for interceptors 1295 // ------------------------------------------------------------------- 1296 1297 DN interceptorsDn = new DN( "ou=interceptors,ou=configuration,ou=system" ); 1298 interceptorsDn.normalize( oidsMap ); 1299 1300 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, interceptorsDn ) ) ) 1301 { 1302 firstStart = true; 1303 1304 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, interceptorsDn ); 1305 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 1306 1307 serverEntry.put( SchemaConstants.OU_AT, "interceptors" ); 1308 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1309 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1310 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1311 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1312 1313 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1314 } 1315 1316 // ------------------------------------------------------------------- 1317 // create system preferences area 1318 // ------------------------------------------------------------------- 1319 1320 DN sysPrefRootDn = new DN( ServerDNConstants.SYSPREFROOT_SYSTEM_DN ); 1321 sysPrefRootDn.normalize( oidsMap ); 1322 1323 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, sysPrefRootDn ) ) ) 1324 { 1325 firstStart = true; 1326 1327 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, sysPrefRootDn ); 1328 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, 1329 SchemaConstants.TOP_OC, 1330 SchemaConstants.ORGANIZATIONAL_UNIT_OC, 1331 SchemaConstants.EXTENSIBLE_OBJECT_OC ); 1332 1333 serverEntry.put( "prefNodeName", "sysPrefRoot" ); 1334 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 1335 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 1336 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() ); 1337 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 1338 1339 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) ); 1340 } 1341 1342 return firstStart; 1343 } 1344 1345 1346 /** 1347 * Displays security warning messages if any possible secutiry issue is found. 1348 * @throws Exception if there are failures parsing and accessing internal structures 1349 */ 1350 private void showSecurityWarnings() throws Exception 1351 { 1352 // Warn if the default password is not changed. 1353 boolean needToChangeAdminPassword = false; 1354 1355 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN ); 1356 adminDn.normalize( schemaManager.getNormalizerMapping() ); 1357 1358 ServerEntry adminEntry = partitionNexus.lookup( new LookupOperationContext( adminSession, adminDn ) ); 1359 Object userPassword = adminEntry.get( SchemaConstants.USER_PASSWORD_AT ).get(); 1360 1361 if ( userPassword instanceof byte[] ) 1362 { 1363 needToChangeAdminPassword = Arrays.equals( PartitionNexus.ADMIN_PASSWORD_BYTES, ( byte[] ) userPassword ); 1364 } 1365 else if ( userPassword.toString().equals( PartitionNexus.ADMIN_PASSWORD_STRING ) ) 1366 { 1367 needToChangeAdminPassword = PartitionNexus.ADMIN_PASSWORD_STRING.equals( userPassword.toString() ); 1368 } 1369 1370 if ( needToChangeAdminPassword ) 1371 { 1372 LOG.warn( "You didn't change the admin password of directory service " + "instance '" + instanceId + "'. " 1373 + "Please update the admin password as soon as possible " + "to prevent a possible security breach." ); 1374 } 1375 } 1376 1377 1378 /** 1379 * Adds test entries into the core. 1380 * 1381 * @todo this may no longer be needed when JNDI is not used for bootstrapping 1382 * 1383 * @throws Exception if the creation of test entries fails. 1384 */ 1385 private void createTestEntries() throws Exception 1386 { 1387 for ( LdifEntry testEntry : testEntries ) 1388 { 1389 try 1390 { 1391 LdifEntry ldifEntry = testEntry.clone(); 1392 Entry entry = ldifEntry.getEntry(); 1393 String dn = ldifEntry.getDn().getName(); 1394 1395 try 1396 { 1397 getAdminSession().add( new DefaultServerEntry( schemaManager, entry ) ); 1398 } 1399 catch ( Exception e ) 1400 { 1401 LOG.warn( dn + " test entry already exists.", e ); 1402 } 1403 } 1404 catch ( CloneNotSupportedException cnse ) 1405 { 1406 LOG.warn( "Cannot clone the entry ", cnse ); 1407 } 1408 } 1409 } 1410 1411 1412 /** 1413 * Kicks off the initialization of the entire system. 1414 * 1415 * @throws Exception if there are problems along the way 1416 */ 1417 private void initialize() throws Exception 1418 { 1419 if ( LOG.isDebugEnabled() ) 1420 { 1421 LOG.debug( "---> Initializing the DefaultDirectoryService " ); 1422 } 1423 1424 // triggers partition to load schema fully from schema partition 1425 schemaService.initialize(); 1426 schemaService.getSchemaPartition().initialize(); 1427 partitions.add( schemaService.getSchemaPartition() ); 1428 systemPartition.getSuffixDn().normalize( schemaManager.getNormalizerMapping() ); 1429 1430 adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN ).normalize( schemaManager.getNormalizerMapping() ); 1431 adminDn.normalize( schemaManager.getNormalizerMapping() ); 1432 adminSession = new DefaultCoreSession( new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), this ); 1433 1434 // @TODO - NOTE: Need to find a way to instantiate without dependency on DPN 1435 partitionNexus = new DefaultPartitionNexus( new DefaultServerEntry( schemaManager, DN.EMPTY_DN ) ); 1436 partitionNexus.setDirectoryService( this ); 1437 partitionNexus.initialize( ); 1438 //partitionNexus.addContextPartition( new AddContextPartitionOperationContext( adminSession, schemaService.getSchemaPartition() ) ); 1439 1440 // -------------------------------------------------------------------- 1441 // Create all the bootstrap entries before initializing chain 1442 // -------------------------------------------------------------------- 1443 1444 firstStart = createBootstrapEntries(); 1445 1446 interceptorChain = new InterceptorChain(); 1447 interceptorChain.init( this ); 1448 1449 // -------------------------------------------------------------------- 1450 // Initialize the changeLog if it's enabled 1451 // -------------------------------------------------------------------- 1452 1453 if ( changeLog.isEnabled() ) 1454 { 1455 changeLog.init( this ); 1456 1457 if( changeLog.isExposed() && changeLog.isTagSearchSupported() ) 1458 { 1459 String clSuffix = ( ( TaggableSearchableChangeLogStore ) changeLog.getChangeLogStore() ).getPartition().getSuffixDn().getName(); 1460 partitionNexus.getRootDSE( null ).getOriginalEntry().add( SchemaConstants.CHANGELOG_CONTEXT_AT, clSuffix ); 1461 } 1462 } 1463 1464 // -------------------------------------------------------------------- 1465 // Initialize the journal if it's enabled 1466 // -------------------------------------------------------------------- 1467 if ( journal.isEnabled() ) 1468 { 1469 journal.init( this ); 1470 } 1471 1472 if ( LOG.isDebugEnabled() ) 1473 { 1474 LOG.debug( "<--- DefaultDirectoryService initialized" ); 1475 } 1476 } 1477 1478 1479 /** 1480 * Read an entry (without DN) 1481 * 1482 * @param text The ldif format file 1483 * @return An entry. 1484 */ 1485 private Entry readEntry( String text ) 1486 { 1487 StringReader strIn = new StringReader( text ); 1488 BufferedReader in = new BufferedReader( strIn ); 1489 1490 String line = null; 1491 Entry entry = new DefaultClientEntry(); 1492 1493 try 1494 { 1495 while ( ( line = in.readLine() ) != null ) 1496 { 1497 if ( line.length() == 0 ) 1498 { 1499 continue; 1500 } 1501 1502 String addedLine = line.trim(); 1503 1504 if ( StringTools.isEmpty( addedLine ) ) 1505 { 1506 continue; 1507 } 1508 1509 EntryAttribute attribute = LdifReader.parseAttributeValue( addedLine ); 1510 EntryAttribute oldAttribute = entry.get( attribute.getId() ); 1511 1512 if ( oldAttribute != null ) 1513 { 1514 try 1515 { 1516 oldAttribute.add( attribute.get() ); 1517 entry.put( oldAttribute ); 1518 } 1519 catch ( LdapException ne ) 1520 { 1521 // Do nothing 1522 } 1523 } 1524 else 1525 { 1526 try 1527 { 1528 entry.put( attribute ); 1529 } 1530 catch ( LdapException ne ) 1531 { 1532 // TODO do nothing ... 1533 } 1534 } 1535 } 1536 } 1537 catch (IOException ioe) 1538 { 1539 // Do nothing : we can't reach this point ! 1540 } 1541 1542 return entry; 1543 } 1544 1545 1546 /** 1547 * Create a new ServerEntry 1548 * 1549 * @param ldif The String representing the attributes, as a LDIF file 1550 * @param dn The DN for this new entry 1551 */ 1552 public ServerEntry newEntry( String ldif, String dn ) 1553 { 1554 try 1555 { 1556 Entry entry = readEntry( ldif ); 1557 DN newDn = new DN( dn ); 1558 1559 entry.setDn( newDn ); 1560 1561 // TODO Let's get rid of this Attributes crap 1562 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, entry ); 1563 return serverEntry; 1564 } 1565 catch ( Exception e ) 1566 { 1567 LOG.error( I18n.err( I18n.ERR_78, ldif, dn ) ); 1568 // do nothing 1569 return null; 1570 } 1571 } 1572 1573 1574 public EventService getEventService() 1575 { 1576 return eventService; 1577 } 1578 1579 1580 public void setEventService( EventService eventService ) 1581 { 1582 this.eventService = eventService; 1583 } 1584 1585 1586 /** 1587 * {@inheritDoc} 1588 */ 1589 public boolean isPasswordHidden() 1590 { 1591 return passwordHidden; 1592 } 1593 1594 1595 /** 1596 * {@inheritDoc} 1597 */ 1598 public void setPasswordHidden( boolean passwordHidden ) 1599 { 1600 this.passwordHidden = passwordHidden; 1601 } 1602 1603 1604 /** 1605 * @return The maximum allowed size for an incoming PDU 1606 */ 1607 public int getMaxPDUSize() 1608 { 1609 return maxPDUSize; 1610 } 1611 1612 1613 /** 1614 * Set the maximum allowed size for an incoming PDU 1615 * @param maxPDUSize A positive number of bytes for the PDU. A negative or 1616 * null value will be transformed to {@link Integer#MAX_VALUE} 1617 */ 1618 public void setMaxPDUSize( int maxPDUSize ) 1619 { 1620 if ( maxPDUSize <= 0 ) 1621 { 1622 maxPDUSize = Integer.MAX_VALUE; 1623 } 1624 1625 this.maxPDUSize = maxPDUSize; 1626 } 1627 1628 1629 /** 1630 * {@inheritDoc} 1631 */ 1632 public Interceptor getInterceptor( String interceptorName ) 1633 { 1634 for ( Interceptor interceptor:interceptors ) 1635 { 1636 if ( interceptor.getName().equalsIgnoreCase( interceptorName ) ) 1637 { 1638 return interceptor; 1639 } 1640 } 1641 1642 return null; 1643 } 1644 1645 1646 /** 1647 * Get a new CSN 1648 * @return The CSN generated for this directory service 1649 */ 1650 public Csn getCSN() 1651 { 1652 return csnFactory.newInstance(); 1653 } 1654 1655 1656 /** 1657 * @return the replicaId 1658 */ 1659 public int getReplicaId() 1660 { 1661 return replicaId; 1662 } 1663 1664 1665 /** 1666 * @param replicaId the replicaId to set 1667 */ 1668 public void setReplicaId( int replicaId ) 1669 { 1670 if ( ( replicaId < 0 ) || ( replicaId > 999 ) ) 1671 { 1672 LOG.error( I18n.err( I18n.ERR_79 ) ); 1673 this.replicaId = 0; 1674 } 1675 else 1676 { 1677 this.replicaId = replicaId; 1678 } 1679 } 1680 1681 1682 public void setReplicationConfiguration( ReplicationConfiguration replicationConfig ) 1683 { 1684 this.replicationConfig = replicationConfig; 1685 1686 } 1687 1688 1689 /** 1690 * @return the replication configuration for this DirectoryService 1691 */ 1692 public ReplicationConfiguration getReplicationConfiguration() 1693 { 1694 return replicationConfig; 1695 } 1696 1697 1698 /** 1699 * @return the syncPeriodMillis 1700 */ 1701 public long getSyncPeriodMillis() 1702 { 1703 return syncPeriodMillis; 1704 } 1705 1706 1707 /** 1708 * @param syncPeriodMillis the syncPeriodMillis to set 1709 */ 1710 public void setSyncPeriodMillis( long syncPeriodMillis ) 1711 { 1712 this.syncPeriodMillis = syncPeriodMillis; 1713 } 1714 }