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 */
020package org.apache.directory.server.core.shared;
021
022
023import java.io.File;
024import java.io.IOException;
025import java.net.SocketAddress;
026import java.nio.file.Files;
027import java.text.ParseException;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034import java.util.concurrent.atomic.AtomicLong;
035
036import jdbm.recman.BaseRecordManager;
037
038import org.apache.directory.api.ldap.extras.controls.syncrepl.syncRequest.SyncRequestValue;
039import org.apache.directory.api.ldap.model.constants.AuthenticationLevel;
040import org.apache.directory.api.ldap.model.constants.SchemaConstants;
041import org.apache.directory.api.ldap.model.cursor.Cursor;
042import org.apache.directory.api.ldap.model.cursor.CursorException;
043import org.apache.directory.api.ldap.model.cursor.EmptyCursor;
044import org.apache.directory.api.ldap.model.entry.DefaultModification;
045import org.apache.directory.api.ldap.model.entry.Entry;
046import org.apache.directory.api.ldap.model.entry.Modification;
047import org.apache.directory.api.ldap.model.entry.Value;
048import org.apache.directory.api.ldap.model.exception.LdapException;
049import org.apache.directory.api.ldap.model.exception.LdapInvalidSearchFilterException;
050import org.apache.directory.api.ldap.model.filter.ExprNode;
051import org.apache.directory.api.ldap.model.filter.FilterParser;
052import org.apache.directory.api.ldap.model.filter.PresenceNode;
053import org.apache.directory.api.ldap.model.message.AddRequest;
054import org.apache.directory.api.ldap.model.message.AliasDerefMode;
055import org.apache.directory.api.ldap.model.message.CompareRequest;
056import org.apache.directory.api.ldap.model.message.Control;
057import org.apache.directory.api.ldap.model.message.DeleteRequest;
058import org.apache.directory.api.ldap.model.message.LdapResult;
059import org.apache.directory.api.ldap.model.message.ModifyDnRequest;
060import org.apache.directory.api.ldap.model.message.ModifyRequest;
061import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
062import org.apache.directory.api.ldap.model.message.ResultResponse;
063import org.apache.directory.api.ldap.model.message.SearchRequest;
064import org.apache.directory.api.ldap.model.message.SearchScope;
065import org.apache.directory.api.ldap.model.message.UnbindRequest;
066import org.apache.directory.api.ldap.model.message.controls.SortKey;
067import org.apache.directory.api.ldap.model.message.controls.SortRequest;
068import org.apache.directory.api.ldap.model.message.controls.SortResponse;
069import org.apache.directory.api.ldap.model.message.controls.SortResponseImpl;
070import org.apache.directory.api.ldap.model.message.controls.SortResultCode;
071import org.apache.directory.api.ldap.model.name.Dn;
072import org.apache.directory.api.ldap.model.name.Rdn;
073import org.apache.directory.api.ldap.model.schema.AttributeType;
074import org.apache.directory.api.ldap.model.schema.MatchingRule;
075import org.apache.directory.api.ldap.model.schema.SchemaManager;
076import org.apache.directory.api.util.Strings;
077import org.apache.directory.server.constants.ServerDNConstants;
078import org.apache.directory.server.core.api.CoreSession;
079import org.apache.directory.server.core.api.DirectoryService;
080import org.apache.directory.server.core.api.LdapPrincipal;
081import org.apache.directory.server.core.api.OperationManager;
082import org.apache.directory.server.core.api.changelog.LogChange;
083import org.apache.directory.server.core.api.interceptor.context.AbstractOperationContext;
084import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
085import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext;
086import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
087import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
088import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
089import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
090import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
091import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
092import org.apache.directory.server.core.api.interceptor.context.OperationContext;
093import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
094import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
095import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext;
096import org.apache.directory.server.core.api.partition.Partition;
097import org.apache.directory.server.core.api.partition.PartitionTxn;
098import org.apache.directory.server.i18n.I18n;
099import org.apache.mina.core.session.IoSession;
100import org.slf4j.Logger;
101import org.slf4j.LoggerFactory;
102
103
104/**
105 * The default CoreSession implementation.
106 * 
107 * TODO - has not been completed yet
108 * TODO - need to supply controls and other parameters to setup opContexts
109 *
110 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
111 */
112public class DefaultCoreSession implements CoreSession
113{
114    /** A logger for this class */
115    private static final Logger LOG = LoggerFactory.getLogger( DefaultCoreSession.class );
116
117    /** The DirectoryService we are connected to */
118    private final DirectoryService directoryService;
119
120    /** The Principal used to process operations */
121    private final LdapPrincipal authenticatedPrincipal;
122
123    /** The anonymous principal, if we have to process operation as anonymous */
124    private final LdapPrincipal anonymousPrincipal;
125
126    /** The authorized principal, which will be used when a user has been authorized */
127    private LdapPrincipal authorizedPrincipal;
128
129    /** A reference to the ObjectClass AT */
130    protected AttributeType objectClassAT;
131
132    /** The associated IoSession */
133    private IoSession ioSession;
134
135    /** flag to indicate if the password must be changed */
136    private boolean pwdMustChange;
137
138    /** A flag set when the startTransaction extended operation has been received */
139    private boolean hasSessionTransaction;
140    
141    /** The Map containing the transactions associated with each partition */
142    private Map<String, PartitionTxn> transactionMap = new HashMap<>();
143    
144    /** The transaction ID */
145    private AtomicLong transactionId = new AtomicLong( 0 );
146
147    /**
148     * Creates a new instance of a DefaultCoreSession
149     * @param principal The principal to use to process operation for this session
150     * @param directoryService The DirectoryService to which we will send requests
151     */
152    public DefaultCoreSession( LdapPrincipal principal, DirectoryService directoryService )
153    {
154        this.directoryService = directoryService;
155        authenticatedPrincipal = principal;
156
157        if ( principal.getAuthenticationLevel() == AuthenticationLevel.NONE )
158        {
159            anonymousPrincipal = principal;
160        }
161        else
162        {
163            anonymousPrincipal = new LdapPrincipal( directoryService.getSchemaManager() );
164        }
165
166        // setup attribute type value
167        objectClassAT = directoryService.getSchemaManager().getAttributeType( SchemaConstants.OBJECT_CLASS_AT );
168    }
169
170
171    /**
172     * Gets the IoSession from the CoreSession. This is only useful when the server is not embedded.
173     * 
174     * @return ioSession The IoSession for this CoreSession
175     */
176    public IoSession getIoSession()
177    {
178        return ioSession;
179    }
180
181
182    /**
183     * Stores the IoSession into the CoreSession. This is only useful when the server is not embedded.
184     * 
185     * @param ioSession The IoSession for this CoreSession
186     */
187    public void setIoSession( IoSession ioSession )
188    {
189        this.ioSession = ioSession;
190    }
191
192
193    /**
194     * Set the ignoreRefferal flag for the current operationContext.
195     *
196     * @param opContext The current operationContext
197     * @param ignoreReferral The flag
198     */
199    private void setReferralHandling( AbstractOperationContext opContext, boolean ignoreReferral )
200    {
201        if ( ignoreReferral )
202        {
203            opContext.ignoreReferral();
204        }
205        else
206        {
207            opContext.throwReferral();
208        }
209    }
210
211
212    /**
213     * {@inheritDoc}
214     */
215    @Override
216    public void add( Entry entry ) throws LdapException
217    {
218        add( entry, LogChange.TRUE );
219    }
220
221
222    /**
223     * {@inheritDoc}
224     */
225    @Override
226    public void add( Entry entry, boolean ignoreReferral ) throws LdapException
227    {
228        add( entry, ignoreReferral, LogChange.TRUE );
229    }
230
231
232    /**
233     * {@inheritDoc}
234     */
235    @Override
236    public void add( Entry entry, LogChange log ) throws LdapException
237    {
238        AddOperationContext addContext = new AddOperationContext( this, entry );
239
240        addContext.setLogChange( log );
241
242        OperationManager operationManager = directoryService.getOperationManager();
243        operationManager.add( addContext );
244    }
245
246
247    /**
248     * {@inheritDoc}
249     */
250    @Override
251    public void add( Entry entry, boolean ignoreReferral, LogChange log ) throws LdapException
252    {
253        AddOperationContext addContext = new AddOperationContext( this, entry );
254
255        addContext.setLogChange( log );
256        setReferralHandling( addContext, ignoreReferral );
257
258        OperationManager operationManager = directoryService.getOperationManager();
259        operationManager.add( addContext );
260    }
261
262
263    /**
264     * {@inheritDoc}
265     */
266    @Override
267    public void add( AddRequest addRequest ) throws LdapException
268    {
269        add( addRequest, LogChange.TRUE );
270    }
271
272
273    /**
274     * {@inheritDoc}
275     */
276    @Override
277    public void add( AddRequest addRequest, LogChange log ) throws LdapException
278    {
279        AddOperationContext addContext = new AddOperationContext( this, addRequest );
280
281        addContext.setLogChange( log );
282
283        OperationManager operationManager = directoryService.getOperationManager();
284        
285        try
286        {
287            operationManager.add( addContext );
288        }
289        catch ( LdapException e )
290        {
291            addRequest.getResultResponse().addAllControls( addContext.getResponseControls() );
292            throw e;
293        }
294        
295        addRequest.getResultResponse().addAllControls( addContext.getResponseControls() );
296    }
297
298
299    private Value convertToValue( String oid, Object value ) throws LdapException
300    {
301        Value val;
302
303        AttributeType attributeType = directoryService.getSchemaManager().lookupAttributeTypeRegistry( oid );
304
305        // make sure we add the request controls to operation
306        if ( attributeType.getSyntax().isHumanReadable() )
307        {
308            if ( value instanceof String )
309            {
310                val = new Value( attributeType, ( String ) value );
311            }
312            else if ( value instanceof byte[] )
313            {
314                val = new Value( attributeType, Strings.utf8ToString( ( byte[] ) value ) );
315            }
316            else
317            {
318                throw new LdapException( I18n.err( I18n.ERR_309, oid ) );
319            }
320        }
321        else
322        {
323            if ( value instanceof String )
324            {
325                val = new Value( attributeType, Strings.getBytesUtf8( ( String ) value ) );
326            }
327            else if ( value instanceof byte[] )
328            {
329                val = new Value( attributeType, ( byte[] ) value );
330            }
331            else
332            {
333                throw new LdapException( I18n.err( I18n.ERR_309, oid ) );
334            }
335        }
336
337        return val;
338    }
339
340
341    /**
342     * {@inheritDoc}
343     */
344    @Override
345    public boolean compare( Dn dn, String oid, Object value ) throws LdapException
346    {
347        OperationManager operationManager = directoryService.getOperationManager();
348
349        return operationManager.compare( new CompareOperationContext( this, dn, oid, convertToValue( oid, value ) ) );
350    }
351
352
353    /**
354     * {@inheritDoc}
355     */
356    @Override
357    public boolean compare( Dn dn, String oid, Object value, boolean ignoreReferral ) throws LdapException
358    {
359        CompareOperationContext compareContext = new CompareOperationContext( this, dn, oid,
360            convertToValue( oid, value ) );
361
362        setReferralHandling( compareContext, ignoreReferral );
363
364        OperationManager operationManager = directoryService.getOperationManager();
365        return operationManager.compare( compareContext );
366    }
367
368
369    /**
370     * {@inheritDoc}
371     */
372    @Override
373    public void delete( Dn dn ) throws LdapException
374    {
375        delete( dn, LogChange.TRUE );
376    }
377
378
379    /**
380     * {@inheritDoc}
381     */
382    @Override
383    public void delete( Dn dn, LogChange log ) throws LdapException
384    {
385        DeleteOperationContext deleteContext = new DeleteOperationContext( this, dn );
386
387        deleteContext.setLogChange( log );
388
389        OperationManager operationManager = directoryService.getOperationManager();
390        operationManager.delete( deleteContext );
391    }
392
393
394    /**
395     * {@inheritDoc}
396     */
397    @Override
398    public void delete( Dn dn, boolean ignoreReferral ) throws LdapException
399    {
400        delete( dn, ignoreReferral, LogChange.TRUE );
401    }
402
403
404    /**
405     * {@inheritDoc}
406     */
407    @Override
408    public void delete( Dn dn, boolean ignoreReferral, LogChange log ) throws LdapException
409    {
410        DeleteOperationContext deleteContext = new DeleteOperationContext( this, dn );
411
412        deleteContext.setLogChange( log );
413        setReferralHandling( deleteContext, ignoreReferral );
414
415        OperationManager operationManager = directoryService.getOperationManager();
416        operationManager.delete( deleteContext );
417    }
418
419
420    /**
421     * {@inheritDoc}
422     */
423    @Override
424    public LdapPrincipal getAnonymousPrincipal()
425    {
426        return anonymousPrincipal;
427    }
428
429
430    /**
431     * {@inheritDoc}
432     */
433    @Override
434    public LdapPrincipal getAuthenticatedPrincipal()
435    {
436        return authenticatedPrincipal;
437    }
438
439
440    /**
441     * {@inheritDoc}
442     */
443    @Override
444    public AuthenticationLevel getAuthenticationLevel()
445    {
446        return getEffectivePrincipal().getAuthenticationLevel();
447    }
448
449
450    /**
451     * {@inheritDoc}
452     */
453    @Override
454    public SocketAddress getClientAddress()
455    {
456        if ( ioSession != null )
457        {
458            return ioSession.getRemoteAddress();
459        }
460        else
461        {
462            return null;
463        }
464    }
465
466
467    /**
468     * {@inheritDoc}
469     */
470    @Override
471    public Set<Control> getControls()
472    {
473        // TODO Auto-generated method stub
474        return null;
475    }
476
477
478    /**
479     * {@inheritDoc}
480     */
481    @Override
482    public DirectoryService getDirectoryService()
483    {
484        return directoryService;
485    }
486
487
488    /**
489     * {@inheritDoc}
490     */
491    @Override
492    public LdapPrincipal getEffectivePrincipal()
493    {
494        if ( authorizedPrincipal == null )
495        {
496            return authenticatedPrincipal;
497        }
498
499        return authorizedPrincipal;
500    }
501
502
503    /**
504     * {@inheritDoc}
505     */
506    @Override
507    public Set<OperationContext> getOutstandingOperations()
508    {
509        // TODO Auto-generated method stub
510        return null;
511    }
512
513
514    /**
515     * {@inheritDoc}
516     */
517    @Override
518    public SocketAddress getServiceAddress()
519    {
520        if ( ioSession != null )
521        {
522            return ioSession.getServiceAddress();
523        }
524        else
525        {
526            return null;
527        }
528    }
529
530
531    /**
532     * {@inheritDoc}
533     */
534    @Override
535    public boolean isConfidential()
536    {
537        // TODO Auto-generated method stub
538        return false;
539    }
540
541
542    /**
543     * {@inheritDoc}
544     */
545    @Override
546    public boolean isVirtual()
547    {
548        // TODO Auto-generated method stub
549        return true;
550    }
551
552
553    /**
554     * TODO - perhaps we should just use a flag that is calculated on creation
555     * of this session
556     * 
557     * @see org.apache.directory.server.core.api.CoreSession#isAdministrator()
558     */
559    @Override
560    public boolean isAdministrator()
561    {
562        String normName = getEffectivePrincipal().getName();
563
564        return normName.equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
565    }
566
567
568    /**
569     * TODO - this method impl does not check to see if the principal is in
570     * the administrators group - it only returns true of the principal is
571     * the actual admin user.  need to make it check groups.
572     * 
573     * TODO - perhaps we should just use a flag that is calculated on creation
574     * of this session
575     * 
576     * @see org.apache.directory.server.core.api.CoreSession#isAnAdministrator()
577     */
578    @Override
579    public boolean isAnAdministrator()
580    {
581        if ( isAdministrator() )
582        {
583            return true;
584        }
585
586        // TODO fix this so it checks groups
587        return false;
588    }
589
590
591    /**
592     * {@inheritDoc}
593     */
594    @Override
595    public Cursor<Entry> list( Dn dn, AliasDerefMode aliasDerefMode,
596        String... returningAttributes ) throws LdapException
597    {
598        OperationManager operationManager = directoryService.getOperationManager();
599
600        PresenceNode filter = new PresenceNode( objectClassAT );
601        SearchOperationContext searchContext = new SearchOperationContext( this, dn, SearchScope.ONELEVEL, filter,
602            returningAttributes );
603        searchContext.setAliasDerefMode( aliasDerefMode );
604
605        return operationManager.search( searchContext );
606    }
607
608
609    /**
610     * {@inheritDoc}
611     */
612    @Override
613    public Entry lookup( Dn dn, String... attrIds ) throws LdapException
614    {
615        OperationManager operationManager = directoryService.getOperationManager();
616        LookupOperationContext lookupContext = new LookupOperationContext( this, dn, attrIds );
617
618        return operationManager.lookup( lookupContext );
619    }
620
621
622    /**
623     * {@inheritDoc}
624     */
625    @Override
626    public Entry lookup( Dn dn, Control[] controls, String... attrIds ) throws LdapException
627    {
628        OperationManager operationManager = directoryService.getOperationManager();
629        LookupOperationContext lookupContext = new LookupOperationContext( this, dn, attrIds );
630
631        if ( controls != null )
632        {
633            lookupContext.addRequestControls( controls );
634        }
635
636        return operationManager.lookup( lookupContext );
637    }
638
639
640    /**
641     * {@inheritDoc}
642     */
643    @Override
644    public void modify( Dn dn, Modification... mods ) throws LdapException
645    {
646        modify( dn, Arrays.asList( mods ), LogChange.TRUE );
647    }
648
649
650    /**
651     * {@inheritDoc}
652     */
653    @Override
654    public void modify( Dn dn, List<Modification> mods ) throws LdapException
655    {
656        modify( dn, mods, LogChange.TRUE );
657    }
658
659
660    /**
661     * {@inheritDoc}
662     */
663    @Override
664    public void modify( Dn dn, List<Modification> mods, LogChange log ) throws LdapException
665    {
666        if ( mods == null )
667        {
668            return;
669        }
670
671        List<Modification> serverModifications = new ArrayList<>( mods.size() );
672
673        for ( Modification mod : mods )
674        {
675            serverModifications.add( new DefaultModification( directoryService.getSchemaManager(), mod ) );
676        }
677
678        ModifyOperationContext modifyContext = new ModifyOperationContext( this, dn, serverModifications );
679
680        modifyContext.setLogChange( log );
681
682        OperationManager operationManager = directoryService.getOperationManager();
683        
684        operationManager.modify( modifyContext );
685    }
686
687
688    /**
689     * {@inheritDoc}
690     */
691    @Override
692    public void modify( Dn dn, List<Modification> mods, boolean ignoreReferral ) throws LdapException
693    {
694        modify( dn, mods, ignoreReferral, LogChange.TRUE );
695    }
696
697
698    /**
699     * {@inheritDoc}
700     */
701    @Override
702    public void modify( Dn dn, List<Modification> mods, boolean ignoreReferral, LogChange log ) throws LdapException
703    {
704        if ( mods == null )
705        {
706            return;
707        }
708
709        List<Modification> serverModifications = new ArrayList<>( mods.size() );
710
711        for ( Modification mod : mods )
712        {
713            serverModifications.add( new DefaultModification( directoryService.getSchemaManager(), mod ) );
714        }
715
716        ModifyOperationContext modifyContext = new ModifyOperationContext( this, dn, serverModifications );
717
718        setReferralHandling( modifyContext, ignoreReferral );
719        modifyContext.setLogChange( log );
720
721        OperationManager operationManager = directoryService.getOperationManager();
722        operationManager.modify( modifyContext );
723    }
724
725
726    /**
727     * {@inheritDoc}
728     */
729    @Override
730    public void move( Dn dn, Dn newParent ) throws LdapException
731    {
732        move( dn, newParent, LogChange.TRUE );
733    }
734
735
736    /**
737     * {@inheritDoc}
738     */
739    @Override
740    public void move( Dn dn, Dn newParent, LogChange log ) throws LdapException
741    {
742        MoveOperationContext moveContext = new MoveOperationContext( this, dn, newParent );
743        moveContext.setLogChange( log );
744
745        OperationManager operationManager = directoryService.getOperationManager();
746        operationManager.move( moveContext );
747    }
748
749
750    /**
751     * {@inheritDoc}
752     */
753    @Override
754    public void move( Dn dn, Dn newParent, boolean ignoreReferral ) throws Exception
755    {
756        move( dn, newParent, ignoreReferral, LogChange.TRUE );
757    }
758
759
760    /**
761     * {@inheritDoc}
762     */
763    @Override
764    public void move( Dn dn, Dn newParent, boolean ignoreReferral, LogChange log ) throws LdapException
765    {
766        OperationManager operationManager = directoryService.getOperationManager();
767        MoveOperationContext moveContext = new MoveOperationContext( this, dn, newParent );
768
769        setReferralHandling( moveContext, ignoreReferral );
770        moveContext.setLogChange( log );
771
772        operationManager.move( moveContext );
773    }
774
775
776    /**
777     * {@inheritDoc}
778     */
779    @Override
780    public void moveAndRename( Dn dn, Dn newParent, Rdn newRdn, boolean deleteOldRdn ) throws LdapException
781    {
782        moveAndRename( dn, newParent, newRdn, deleteOldRdn, LogChange.TRUE );
783    }
784
785
786    /**
787     * {@inheritDoc}
788     */
789    @Override
790    public void moveAndRename( Dn dn, Dn newSuperiorDn, Rdn newRdn, boolean deleteOldRdn, LogChange log )
791        throws LdapException
792    {
793        MoveAndRenameOperationContext moveAndRenameContext = new MoveAndRenameOperationContext( this, dn,
794            newSuperiorDn, newRdn, deleteOldRdn );
795
796        moveAndRenameContext.setLogChange( log );
797
798        OperationManager operationManager = directoryService.getOperationManager();
799        operationManager.moveAndRename( moveAndRenameContext );
800    }
801
802
803    /**
804     * {@inheritDoc}
805     */
806    @Override
807    public void moveAndRename( Dn dn, Dn newParent, Rdn newRdn, boolean deleteOldRdn, boolean ignoreReferral )
808        throws LdapException
809    {
810        moveAndRename( dn, newParent, newRdn, deleteOldRdn, ignoreReferral, LogChange.TRUE );
811    }
812
813
814    /**
815     * {@inheritDoc}
816     */
817    @Override
818    public void moveAndRename( Dn dn, Dn newParent, Rdn newRdn, boolean deleteOldRdn, boolean ignoreReferral,
819        LogChange log ) throws LdapException
820    {
821        OperationManager operationManager = directoryService.getOperationManager();
822        MoveAndRenameOperationContext moveAndRenameContext = new MoveAndRenameOperationContext( this, dn, newParent,
823            newRdn, deleteOldRdn );
824
825        moveAndRenameContext.setLogChange( log );
826        setReferralHandling( moveAndRenameContext, ignoreReferral );
827
828        operationManager.moveAndRename( moveAndRenameContext );
829    }
830
831
832    /**
833     * {@inheritDoc}
834     */
835    @Override
836    public void rename( Dn dn, Rdn newRdn, boolean deleteOldRdn ) throws LdapException
837    {
838        rename( dn, newRdn, deleteOldRdn, LogChange.TRUE );
839    }
840
841
842    /**
843     * {@inheritDoc}
844     */
845    @Override
846    public void rename( Dn dn, Rdn newRdn, boolean deleteOldRdn, LogChange log ) throws LdapException
847    {
848        RenameOperationContext renameContext = new RenameOperationContext( this, dn, newRdn, deleteOldRdn );
849
850        renameContext.setLogChange( log );
851
852        OperationManager operationManager = directoryService.getOperationManager();
853
854        operationManager.rename( renameContext );
855    }
856
857
858    /**
859     * {@inheritDoc}
860     */
861    @Override
862    public void rename( Dn dn, Rdn newRdn, boolean deleteOldRdn, boolean ignoreReferral ) throws LdapException
863    {
864        rename( dn, newRdn, deleteOldRdn, ignoreReferral, LogChange.TRUE );
865    }
866
867
868    /**
869     * {@inheritDoc}
870     */
871    @Override
872    public void rename( Dn dn, Rdn newRdn, boolean deleteOldRdn, boolean ignoreReferral, LogChange log )
873        throws LdapException
874    {
875        OperationManager operationManager = directoryService.getOperationManager();
876        RenameOperationContext renameContext = new RenameOperationContext( this, dn, newRdn, deleteOldRdn );
877
878        renameContext.setLogChange( log );
879        setReferralHandling( renameContext, ignoreReferral );
880
881        operationManager.rename( renameContext );
882    }
883
884
885    /**
886     * {@inheritDoc}
887     */
888    @Override
889    public Cursor<Entry> search( Dn dn, String filter ) throws LdapException
890    {
891        return search( dn, filter, true );
892    }
893
894
895    /**
896     * {@inheritDoc}
897     */
898    @Override
899    public Cursor<Entry> search( Dn dn, String filter, boolean ignoreReferrals ) throws LdapException
900    {
901        OperationManager operationManager = directoryService.getOperationManager();
902        ExprNode filterNode = null;
903
904        try
905        {
906            filterNode = FilterParser.parse( directoryService.getSchemaManager(), filter );
907        }
908        catch ( ParseException pe )
909        {
910            throw new LdapInvalidSearchFilterException( pe.getMessage() );
911        }
912
913        SearchOperationContext searchContext = new SearchOperationContext( this, dn, SearchScope.OBJECT, filterNode,
914            ( String ) null );
915        searchContext.setAliasDerefMode( AliasDerefMode.DEREF_ALWAYS );
916        setReferralHandling( searchContext, ignoreReferrals );
917
918        return operationManager.search( searchContext );
919    }
920
921
922    /**
923     * {@inheritDoc}
924     */
925    @Override
926    public Cursor<Entry> search( Dn dn, SearchScope scope, ExprNode filter, AliasDerefMode aliasDerefMode,
927        String... returningAttributes ) throws LdapException
928    {
929        OperationManager operationManager = directoryService.getOperationManager();
930
931        SearchOperationContext searchContext = new SearchOperationContext( this, dn, scope, filter, returningAttributes );
932        searchContext.setAliasDerefMode( aliasDerefMode );
933
934        return operationManager.search( searchContext );
935    }
936
937
938    /**
939     * {@inheritDoc}
940     */
941    @Override
942    public boolean isAnonymous()
943    {
944        if ( ( authorizedPrincipal == null ) && ( authenticatedPrincipal == null ) )
945        {
946            return true;
947        }
948        else
949        {
950            return authenticatedPrincipal.getAuthenticationLevel() == AuthenticationLevel.NONE;
951        }
952    }
953
954
955    /**
956     * {@inheritDoc}
957     */
958    @Override
959    public boolean compare( CompareRequest compareRequest ) throws LdapException
960    {
961        CompareOperationContext compareContext = new CompareOperationContext( this, compareRequest );
962        OperationManager operationManager = directoryService.getOperationManager();
963        boolean result = false;
964        try
965        {
966            result = operationManager.compare( compareContext );
967        }
968        catch ( LdapException e )
969        {
970            compareRequest.getResultResponse().addAllControls( compareContext.getResponseControls() );
971            throw e;
972        }
973
974        compareRequest.getResultResponse().addAllControls( compareContext.getResponseControls() );
975        return result;
976    }
977
978
979    /**
980     * {@inheritDoc}
981     */
982    @Override
983    public void delete( DeleteRequest deleteRequest ) throws LdapException
984    {
985        delete( deleteRequest, LogChange.TRUE );
986    }
987
988
989    /**
990     * {@inheritDoc}
991     */
992    @Override
993    public void delete( DeleteRequest deleteRequest, LogChange log ) throws LdapException
994    {
995        DeleteOperationContext deleteContext = new DeleteOperationContext( this, deleteRequest );
996
997        deleteContext.setLogChange( log );
998
999        OperationManager operationManager = directoryService.getOperationManager();
1000
1001        try
1002        {
1003            operationManager.delete( deleteContext );
1004        }
1005        catch ( LdapException e )
1006        {
1007            deleteRequest.getResultResponse().addAllControls( deleteContext.getResponseControls() );
1008            throw e;
1009        }
1010
1011        deleteRequest.getResultResponse().addAllControls( deleteContext.getResponseControls() );
1012    }
1013
1014
1015    /**
1016     * {@inheritDoc}
1017     */
1018    @Override
1019    public boolean exists( String dn ) throws LdapException
1020    {
1021        return exists( new Dn( dn ) );
1022    }
1023
1024
1025    /**
1026     * {@inheritDoc}
1027     */
1028    @Override
1029    public boolean exists( Dn dn ) throws LdapException
1030    {
1031        HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( this, dn );
1032        OperationManager operationManager = directoryService.getOperationManager();
1033
1034        return operationManager.hasEntry( hasEntryContext );
1035    }
1036
1037
1038    /**
1039     * {@inheritDoc}
1040     */
1041    @Override
1042    public void modify( ModifyRequest modifyRequest ) throws LdapException
1043    {
1044        modify( modifyRequest, LogChange.TRUE );
1045    }
1046
1047
1048    /**
1049     * {@inheritDoc}
1050     */
1051    @Override
1052    public void modify( ModifyRequest modifyRequest, LogChange log ) throws LdapException
1053    {
1054        ModifyOperationContext modifyContext = new ModifyOperationContext( this, modifyRequest );
1055
1056        modifyContext.setLogChange( log );
1057
1058        OperationManager operationManager = directoryService.getOperationManager();
1059
1060        try
1061        {
1062            operationManager.modify( modifyContext );
1063        }
1064        catch ( LdapException e )
1065        {
1066            modifyRequest.getResultResponse().addAllControls( modifyContext.getResponseControls() );
1067            throw e;
1068        }
1069
1070        modifyRequest.getResultResponse().addAllControls( modifyContext.getResponseControls() );
1071    }
1072
1073
1074    /**
1075     * {@inheritDoc}
1076     */
1077    @Override
1078    public void move( ModifyDnRequest modifyDnRequest ) throws LdapException
1079    {
1080        move( modifyDnRequest, LogChange.TRUE );
1081    }
1082
1083
1084    /**
1085     * {@inheritDoc}
1086     */
1087    @Override
1088    public void move( ModifyDnRequest modifyDnRequest, LogChange log ) throws LdapException
1089    {
1090        MoveOperationContext moveContext = new MoveOperationContext( this, modifyDnRequest );
1091
1092        moveContext.setLogChange( log );
1093
1094        OperationManager operationManager = directoryService.getOperationManager();
1095
1096        try
1097        {
1098            operationManager.move( moveContext );
1099        }
1100        catch ( LdapException e )
1101        {
1102            modifyDnRequest.getResultResponse().addAllControls( moveContext.getResponseControls() );
1103            throw e;
1104        }
1105
1106        modifyDnRequest.getResultResponse().addAllControls( moveContext.getResponseControls() );
1107    }
1108
1109
1110    /**
1111     * {@inheritDoc}
1112     */
1113    @Override
1114    public void moveAndRename( ModifyDnRequest modifyDnRequest ) throws LdapException
1115    {
1116        moveAndRename( modifyDnRequest, LogChange.TRUE );
1117    }
1118
1119
1120    /**
1121     * {@inheritDoc}
1122     */
1123    @Override
1124    public void moveAndRename( ModifyDnRequest modifyDnRequest, LogChange log ) throws LdapException
1125    {
1126        MoveAndRenameOperationContext moveAndRenameContext = new MoveAndRenameOperationContext( this, modifyDnRequest );
1127
1128        moveAndRenameContext.setLogChange( log );
1129
1130        OperationManager operationManager = directoryService.getOperationManager();
1131
1132        try
1133        {
1134            operationManager.moveAndRename( moveAndRenameContext );
1135        }
1136        catch ( LdapException e )
1137        {
1138            modifyDnRequest.getResultResponse().addAllControls( moveAndRenameContext.getResponseControls() );
1139            throw e;
1140        }
1141
1142        modifyDnRequest.getResultResponse().addAllControls( moveAndRenameContext.getResponseControls() );
1143    }
1144
1145
1146    /**
1147     * {@inheritDoc}
1148     */
1149    @Override
1150    public void rename( ModifyDnRequest modifyDnRequest ) throws LdapException
1151    {
1152        rename( modifyDnRequest, LogChange.TRUE );
1153    }
1154
1155
1156    /**
1157     * {@inheritDoc}
1158     */
1159    @Override
1160    public void rename( ModifyDnRequest modifyDnRequest, LogChange log ) throws LdapException
1161    {
1162        RenameOperationContext renameContext = new RenameOperationContext( this, modifyDnRequest );
1163
1164        renameContext.setLogChange( log );
1165
1166        OperationManager operationManager = directoryService.getOperationManager();
1167
1168        try
1169        {
1170            operationManager.rename( renameContext );
1171        }
1172        catch ( LdapException e )
1173        {
1174            modifyDnRequest.getResultResponse().addAllControls( renameContext.getResponseControls() );
1175            throw e;
1176        }
1177
1178        modifyDnRequest.getResultResponse().addAllControls( renameContext.getResponseControls() );
1179    }
1180
1181
1182    /**
1183     * {@inheritDoc}
1184     */
1185    @Override
1186    public Cursor<Entry> search( SearchRequest searchRequest ) throws LdapException
1187    {
1188        SearchOperationContext searchContext = new SearchOperationContext( this, searchRequest );
1189        searchContext.setSyncreplSearch( searchRequest.getControls().containsKey( SyncRequestValue.OID ) );
1190
1191        OperationManager operationManager = directoryService.getOperationManager();
1192
1193        // Check if we received serverside sort Control
1194        SortRequest sortControl = ( SortRequest ) searchRequest.getControls().get( SortRequest.OID );
1195
1196        SortResponse sortRespCtrl = null;
1197
1198        ResultResponse done = searchRequest.getResultResponse();
1199
1200        LdapResult ldapResult = done.getLdapResult();
1201
1202        if ( sortControl != null )
1203        {
1204            sortRespCtrl = canSort( sortControl, ldapResult, getDirectoryService().getSchemaManager() );
1205
1206            if ( sortControl.isCritical() && ( sortRespCtrl.getSortResult() != SortResultCode.SUCCESS ) )
1207            {
1208                ldapResult.setResultCode( ResultCodeEnum.UNAVAILABLE_CRITICAL_EXTENSION );
1209                done.addControl( sortRespCtrl );
1210
1211                return new EmptyCursor<>();
1212            }
1213        }
1214
1215        Cursor<Entry> cursor = null;
1216
1217        try
1218        {
1219            cursor = operationManager.search( searchContext );
1220
1221            if ( ( sortRespCtrl != null ) && ( sortRespCtrl.getSortResult() == SortResultCode.SUCCESS ) )
1222            {
1223                cursor = sortResults( cursor, sortControl, getDirectoryService().getSchemaManager() );
1224            }
1225
1226            // the below condition is to satisfy the scenario 6 in section 2 of rfc2891
1227            if ( sortRespCtrl != null )
1228            {
1229                cursor.beforeFirst();
1230
1231                if ( !cursor.next() )
1232                {
1233                    sortRespCtrl = null;
1234                }
1235                else
1236                {
1237                    // move the cursor back
1238                    cursor.previous();
1239                }
1240            }
1241        }
1242        catch ( LdapException e )
1243        {
1244            done.addAllControls( searchContext.getResponseControls() );
1245            throw e;
1246        }
1247        catch ( Exception e )
1248        {
1249            done.addAllControls( searchContext.getResponseControls() );
1250            throw new LdapException( e );
1251        }
1252        finally
1253        {
1254            // Don't close the transaction !!!
1255            LOG.debug( "Search done, the transaction is still opened" );
1256        }
1257
1258        if ( sortRespCtrl != null )
1259        {
1260            done.addControl( sortRespCtrl );
1261        }
1262
1263        done.addAllControls( searchContext.getResponseControls() );
1264
1265        return cursor;
1266    }
1267
1268
1269    /**
1270     * {@inheritDoc}
1271     */
1272    @Override
1273    public void unbind() throws LdapException
1274    {
1275        UnbindOperationContext unbindContext = new UnbindOperationContext( this );
1276
1277        OperationManager operationManager = directoryService.getOperationManager();
1278        operationManager.unbind( unbindContext );
1279    }
1280
1281
1282    /**
1283     * {@inheritDoc}
1284     */
1285    @Override
1286    public void unbind( UnbindRequest unbindRequest ) throws LdapException
1287    {
1288        UnbindOperationContext unbindContext = new UnbindOperationContext( this, unbindRequest );
1289
1290        OperationManager operationManager = directoryService.getOperationManager();
1291        operationManager.unbind( unbindContext );
1292    }
1293
1294
1295    /**
1296     * Checks if the requested search results can be sorted
1297     * 
1298     * @param sortControl the sort control
1299     * @param ldapResult the refrence to the LDAP result of the ongoing search operation
1300     * @param session the current session
1301     * @return a sort response control
1302     */
1303    private SortResponse canSort( SortRequest sortControl, LdapResult ldapResult, SchemaManager schemaManager )
1304    {
1305        SortResponse resp = new SortResponseImpl();
1306
1307        List<SortKey> keys = sortControl.getSortKeys();
1308
1309        // only ONE key is supported by the server for now
1310        if ( keys.size() > 1 )
1311        {
1312            ldapResult.setDiagnosticMessage( "Cannot sort results based on more than one attribute" );
1313            resp.setSortResult( SortResultCode.UNWILLINGTOPERFORM );
1314            return resp;
1315        }
1316
1317        SortKey sk = keys.get( 0 );
1318
1319        AttributeType at = schemaManager.getAttributeType( sk.getAttributeTypeDesc() );
1320
1321        if ( at == null )
1322        {
1323            ldapResult.setDiagnosticMessage( "No attribute with the name " + sk.getAttributeTypeDesc()
1324                + " exists in the server's schema" );
1325            resp.setSortResult( SortResultCode.NOSUCHATTRIBUTE );
1326            resp.setAttributeName( sk.getAttributeTypeDesc() );
1327            return resp;
1328        }
1329
1330        String mrOid = sk.getMatchingRuleId();
1331
1332        if ( mrOid != null )
1333        {
1334            MatchingRule mr = at.getOrdering();
1335
1336            if ( ( mr != null ) && ( !mrOid.equals( mr.getOid() ) ) )
1337            {
1338                ldapResult.setDiagnosticMessage( "Given matchingrule " + mrOid
1339                    + " is not applicable for the attribute " + sk.getAttributeTypeDesc() );
1340                resp.setSortResult( SortResultCode.INAPPROPRIATEMATCHING );
1341                resp.setAttributeName( sk.getAttributeTypeDesc() );
1342                return resp;
1343            }
1344
1345            try
1346            {
1347                schemaManager.lookupComparatorRegistry( mrOid );
1348            }
1349            catch ( LdapException e )
1350            {
1351                ldapResult.setDiagnosticMessage( "Given matchingrule " + mrOid + " is not supported" );
1352                resp.setSortResult( SortResultCode.INAPPROPRIATEMATCHING );
1353                resp.setAttributeName( sk.getAttributeTypeDesc() );
1354                return resp;
1355            }
1356        }
1357        else
1358        {
1359            MatchingRule mr = at.getOrdering();
1360
1361            if ( mr == null )
1362            {
1363                mr = at.getEquality();
1364            }
1365
1366            ldapResult.setDiagnosticMessage( "Matchingrule is required for sorting by the attribute "
1367                + sk.getAttributeTypeDesc() );
1368            resp.setSortResult( SortResultCode.INAPPROPRIATEMATCHING );
1369            resp.setAttributeName( sk.getAttributeTypeDesc() );
1370
1371            if ( mr == null )
1372            {
1373                return resp;
1374            }
1375
1376            try
1377            {
1378                schemaManager.lookupComparatorRegistry( mr.getOid() );
1379            }
1380            catch ( LdapException e )
1381            {
1382                return resp;
1383            }
1384        }
1385
1386        resp.setSortResult( SortResultCode.SUCCESS );
1387
1388        return resp;
1389    }
1390
1391
1392    /**
1393     * Sorts the entries based on the given sortkey and returns the cursor
1394     * 
1395     * @param unsortedEntries the cursor containing un-sorted entries
1396     * @param control the sort control
1397     * @param schemaManager schema manager
1398     * @return a cursor containing sorted entries
1399     * @throws CursorException
1400     * @throws LdapException
1401     * @throws IOException
1402     * @throws KeyNotFoundException 
1403     */
1404    private Cursor<Entry> sortResults( Cursor<Entry> unsortedEntries, SortRequest control, SchemaManager schemaManager )
1405        throws CursorException, LdapException, IOException
1406    {
1407        unsortedEntries.beforeFirst();
1408
1409        Entry first = null;
1410
1411        if ( unsortedEntries.next() )
1412        {
1413            first = unsortedEntries.get();
1414        }
1415
1416        if ( !unsortedEntries.next() )
1417        {
1418            unsortedEntries.beforeFirst();
1419
1420            return unsortedEntries;
1421        }
1422
1423        SortKey sk = control.getSortKeys().get( 0 );
1424
1425        AttributeType at = schemaManager.getAttributeType( sk.getAttributeTypeDesc() );
1426
1427        SortedEntryComparator comparator = new SortedEntryComparator( at, sk.getMatchingRuleId(), sk.isReverseOrder(),
1428            schemaManager );
1429
1430        SortedEntrySerializer keySerializer = new SortedEntrySerializer();
1431        SortedEntrySerializer.setSchemaManager( schemaManager );
1432        
1433        File file = null;
1434        
1435        try 
1436        {
1437            file = Files.createTempFile( "replica", ".sorted-data" ).toFile();    // see DIRSERVER-2007
1438        } 
1439        catch ( IOException e ) 
1440        {
1441            // see DIRSERVER-2091
1442            LOG.error( "Error creating temp file in directory {} for sorting: {}",  
1443                System.getProperty( "java.io.tmpdir" ),  e.getMessage(), e );
1444            throw e;
1445        }
1446
1447        BaseRecordManager recMan = new BaseRecordManager( file.getAbsolutePath() );
1448
1449        jdbm.btree.BTree<Entry, String> btree = new jdbm.btree.BTree<>( recMan, comparator, keySerializer, NullStringSerializer.INSTANCE );
1450        
1451
1452        btree.insert( first, "", false );
1453
1454        // at this stage the cursor will be _on_ the next element, so read it
1455        btree.insert( unsortedEntries.get(), "", false );
1456
1457        while ( unsortedEntries.next() )
1458        {
1459            Entry entry = unsortedEntries.get();
1460            btree.insert( entry, "", false );
1461        }
1462
1463        unsortedEntries.close();
1464
1465        return new SortedEntryCursor( btree, recMan, file );
1466    }
1467
1468
1469    /**
1470     * {@inheritDoc}
1471     */
1472    @Override
1473    public boolean isPwdMustChange() 
1474    {
1475        return pwdMustChange;
1476    }
1477
1478
1479    /**
1480     * {@inheritDoc}
1481     */
1482    @Override
1483    public void setPwdMustChange( boolean pwdMustChange ) 
1484    {
1485        this.pwdMustChange = pwdMustChange;
1486    }
1487
1488    
1489    /**
1490     * {@inheritDoc}
1491     */
1492    @Override
1493    public boolean hasSessionTransaction()
1494    {
1495        return hasSessionTransaction;
1496    }
1497    
1498    
1499    /**
1500     * {@inheritDoc}
1501     */
1502    @Override
1503    public long beginSessionTransaction()
1504    {
1505        hasSessionTransaction = true;
1506        
1507        return transactionId.getAndIncrement();
1508    }
1509    
1510    
1511    /**
1512     * {@inheritDoc}
1513     */
1514    @Override
1515    public void endSessionTransaction( boolean commit ) throws IOException
1516    {
1517        if ( commit )
1518        {
1519            for ( Map.Entry<String, PartitionTxn> partitionTxn : transactionMap.entrySet() )
1520            {
1521                partitionTxn.getValue().commit();
1522            }
1523        }
1524        else
1525        {
1526            for ( Map.Entry<String, PartitionTxn> partitionTxn : transactionMap.entrySet() )
1527            {
1528                partitionTxn.getValue().abort();
1529            }
1530        }
1531        
1532        hasSessionTransaction = false;
1533    }
1534
1535
1536    /**
1537     * {@inheritDoc}
1538     */
1539    @Override
1540    public PartitionTxn getTransaction( Partition partition ) 
1541    {
1542        return transactionMap.get( partition.getId() );
1543    }
1544
1545
1546    /**
1547     * {@inheritDoc}
1548     */
1549    @Override
1550    public void addTransaction( Partition partition, PartitionTxn transaction )
1551    {
1552        if ( !transactionMap.containsKey( partition.getId() ) )
1553        {
1554            transactionMap.put( partition.getId(), transaction );
1555        }
1556    }
1557}