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 */
019package org.apache.directory.server.core.integ;
020
021
022import java.io.File;
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.List;
026
027import javax.naming.NamingException;
028import javax.naming.ldap.LdapContext;
029import javax.naming.ldap.LdapName;
030
031import org.apache.directory.api.ldap.model.constants.AuthenticationLevel;
032import org.apache.directory.api.ldap.model.constants.SchemaConstants;
033import org.apache.directory.api.ldap.model.entry.Attribute;
034import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
035import org.apache.directory.api.ldap.model.entry.DefaultEntry;
036import org.apache.directory.api.ldap.model.entry.DefaultModification;
037import org.apache.directory.api.ldap.model.entry.Modification;
038import org.apache.directory.api.ldap.model.entry.ModificationOperation;
039import org.apache.directory.api.ldap.model.exception.LdapException;
040import org.apache.directory.api.ldap.model.ldif.ChangeType;
041import org.apache.directory.api.ldap.model.ldif.LdifEntry;
042import org.apache.directory.api.ldap.model.ldif.LdifReader;
043import org.apache.directory.api.ldap.model.name.Dn;
044import org.apache.directory.api.ldap.model.name.Rdn;
045import org.apache.directory.api.ldap.model.schema.registries.Schema;
046import org.apache.directory.api.util.FileUtils;
047import org.apache.directory.api.util.Network;
048import org.apache.directory.api.util.Strings;
049import org.apache.directory.ldap.client.api.LdapConnection;
050import org.apache.directory.ldap.client.api.LdapNetworkConnection;
051import org.apache.directory.server.constants.ServerDNConstants;
052import org.apache.directory.server.core.api.CoreSession;
053import org.apache.directory.server.core.api.DirectoryService;
054import org.apache.directory.server.core.api.LdapCoreSessionConnection;
055import org.apache.directory.server.core.api.LdapPrincipal;
056import org.apache.directory.server.core.jndi.ServerLdapContext;
057import org.apache.directory.server.i18n.I18n;
058import org.apache.directory.server.ldap.LdapServer;
059import org.slf4j.Logger;
060import org.slf4j.LoggerFactory;
061
062
063/**
064 * Integration test utility methods.
065 *
066 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
067 */
068public class IntegrationUtils
069{
070    /** The class logger */
071    private static final Logger LOG = LoggerFactory.getLogger( IntegrationUtils.class );
072
073    private static final List<LdapConnection> OPEN_CONNECTIONS = new ArrayList<>();
074
075
076    /**
077     * Deletes the working directory.
078     *
079     * @param wkdir the working directory to delete
080     * @throws IOException if the working directory cannot be deleted
081     */
082    public static void doDelete( File wkdir ) throws IOException
083    {
084        if ( wkdir.exists() )
085        {
086            try
087            {
088                FileUtils.deleteDirectory( wkdir );
089            }
090            catch ( IOException e )
091            {
092                LOG.error( I18n.err( I18n.ERR_115 ), e );
093            }
094        }
095        if ( wkdir.exists() )
096        {
097            throw new IOException( I18n.err( I18n.ERR_116, wkdir ) );
098        }
099    }
100
101
102    /**
103     * Inject an ldif String into the server. Dn must be relative to the
104     * root.
105     *
106     * @param service the directory service to use
107     * @param ldif the ldif containing entries to add to the server.
108     * @throws NamingException if there is a problem adding the entries from the LDIF
109     */
110    public static void injectEntries( DirectoryService service, String ldif ) throws Exception
111    {
112        try ( LdifReader reader = new LdifReader() )
113        {
114            List<LdifEntry> entries = reader.parseLdif( ldif );
115    
116            for ( LdifEntry entry : entries )
117            {
118                if ( entry.isEntry() )
119                {
120                    service.getAdminSession().add(
121                        new DefaultEntry( service.getSchemaManager(), entry.getEntry() ) );
122                }
123                else if ( entry.isChangeModify() )
124                {
125                    service.getAdminSession().modify(
126                        entry.getDn(), entry.getModifications() );
127                }
128                else
129                {
130                    String message = I18n.err( I18n.ERR_117, entry.getChangeType() );
131                    LOG.error( message );
132                    throw new NamingException( message );
133                }
134            }
135        }
136    }
137
138
139    public static LdifEntry getUserAddLdif() throws LdapException
140    {
141        return getUserAddLdif( "uid=akarasulu,ou=users,ou=system", Strings.getBytesUtf8( "test" ),
142            "Alex Karasulu", "Karasulu" );
143    }
144
145
146    public static LdapContext getContext( String principalDn, DirectoryService service, String dn )
147        throws Exception
148    {
149        if ( principalDn == null )
150        {
151            principalDn = "";
152        }
153
154        Dn userDn = new Dn( service.getSchemaManager(), principalDn );
155        LdapPrincipal principal = new LdapPrincipal( service.getSchemaManager(), userDn, AuthenticationLevel.SIMPLE );
156
157        if ( dn == null )
158        {
159            dn = "";
160        }
161
162        CoreSession session = service.getSession( principal );
163
164        return new ServerLdapContext( service, session, new LdapName( dn ) );
165    }
166
167
168    public static CoreSession getCoreSession( String principalDn, DirectoryService service, String dn )
169        throws LdapException
170    {
171        if ( principalDn == null )
172        {
173            principalDn = "";
174        }
175
176        Dn userDn = new Dn( service.getSchemaManager(), principalDn );
177        LdapPrincipal principal = new LdapPrincipal( service.getSchemaManager(), userDn, AuthenticationLevel.SIMPLE );
178
179        return service.getSession( principal );
180    }
181
182
183    public static LdapContext getSystemContext( DirectoryService service ) throws Exception
184    {
185        return getContext( ServerDNConstants.ADMIN_SYSTEM_DN, service, ServerDNConstants.SYSTEM_DN );
186    }
187
188
189    public static LdapContext getSchemaContext( DirectoryService service ) throws Exception
190    {
191        return getContext( ServerDNConstants.ADMIN_SYSTEM_DN, service, SchemaConstants.OU_SCHEMA );
192    }
193
194
195    public static LdapContext getRootContext( DirectoryService service ) throws Exception
196    {
197        return getContext( ServerDNConstants.ADMIN_SYSTEM_DN, service, "" );
198    }
199
200
201    public static void apply( DirectoryService service, LdifEntry entry ) throws LdapException
202    {
203        Dn dn = entry.getDn();
204        CoreSession session = service.getAdminSession();
205
206        switch ( entry.getChangeType().getChangeType() )
207        {
208            case ( ChangeType.ADD_ORDINAL ):
209                session.add(
210                    new DefaultEntry( service.getSchemaManager(), entry.getEntry() ) );
211                break;
212
213            case ( ChangeType.DELETE_ORDINAL ):
214                session.delete( dn );
215                break;
216
217            case ( ChangeType.MODDN_ORDINAL ):
218            case ( ChangeType.MODRDN_ORDINAL ):
219                Rdn newRdn = new Rdn( entry.getNewRdn() );
220
221                if ( entry.getNewSuperior() != null )
222                {
223                    // It's a move. The superior have changed
224                    // Let's see if it's a rename too
225                    Rdn oldRdn = dn.getRdn();
226                    Dn newSuperior = new Dn( entry.getNewSuperior() );
227
228                    if ( dn.size() == 0 )
229                    {
230                        throw new IllegalStateException( I18n.err( I18n.ERR_475 ) );
231                    }
232                    else if ( oldRdn.equals( newRdn ) )
233                    {
234                        // Same rdn : it's a move
235                        session.move( dn, newSuperior );
236                    }
237                    else
238                    {
239                        // it's a move and rename
240                        session.moveAndRename( dn, newSuperior, newRdn, entry.isDeleteOldRdn() );
241                    }
242                }
243                else
244                {
245                    // it's a rename
246                    session.rename( dn, newRdn, entry.isDeleteOldRdn() );
247                }
248
249                break;
250
251            case ( ChangeType.MODIFY_ORDINAL ):
252                session.modify( dn, entry.getModifications() );
253                break;
254
255            default:
256                throw new IllegalStateException( I18n.err( I18n.ERR_476, entry.getChangeType() ) );
257        }
258    }
259
260
261    public static LdifEntry getUserAddLdif( String dnstr, byte[] password, String cn, String sn )
262        throws LdapException
263    {
264        Dn dn = new Dn( dnstr );
265        LdifEntry ldif = new LdifEntry();
266        ldif.setDn( dnstr );
267        ldif.setChangeType( ChangeType.Add );
268
269        Attribute attr = new DefaultAttribute( "objectClass",
270            "top", "person", "organizationalPerson", "inetOrgPerson" );
271        ldif.addAttribute( attr );
272
273        attr = new DefaultAttribute( "ou", "Engineering", "People" );
274        ldif.addAttribute( attr );
275
276        String uid = dn.getRdn().getValue();
277        ldif.putAttribute( "uid", uid );
278
279        ldif.putAttribute( "l", "Bogusville" );
280        ldif.putAttribute( "cn", cn );
281        ldif.putAttribute( "sn", sn );
282        ldif.putAttribute( "mail", uid + "@apache.org" );
283        ldif.putAttribute( "telephoneNumber", "+1 408 555 4798" );
284        ldif.putAttribute( "facsimileTelephoneNumber", "+1 408 555 9751" );
285        ldif.putAttribute( "roomnumber", "4612" );
286        ldif.putAttribute( "userPassword", password );
287
288        String givenName = cn.split( " " )[0];
289        ldif.putAttribute( "givenName", givenName );
290
291        return ldif;
292    }
293
294
295    // -----------------------------------------------------------------------
296    // Enable/Disable Schema Tests
297    // -----------------------------------------------------------------------
298
299    public static void enableSchema( DirectoryService service, String schemaName ) throws LdapException
300    {
301        LdapConnection connection = getAdminConnection( service );
302
303        // now enable the test schema
304        connection.modify( "cn=" + schemaName + ",ou=schema",
305            new DefaultModification(
306                ModificationOperation.REPLACE_ATTRIBUTE, "m-disabled", "FALSE" ) );
307    }
308
309
310    public static void disableSchema( DirectoryService service, String schemaName ) throws LdapException
311    {
312        LdapConnection connection = getAdminConnection( service );
313
314        // now enable the test schema
315        Modification mod = new DefaultModification(
316            ModificationOperation.REPLACE_ATTRIBUTE, "m-disabled", "TRUE" );
317
318        connection.modify( "cn=" + schemaName + ",ou=schema", mod );
319    }
320
321
322    /**
323     * A helper method which tells if a schema is disabled.
324     * 
325     * @param service The Directory Service 
326     * @param schemaName The name of the Schema to check
327     * @return <tt>true</tt> if the schema is enabled
328     */
329    public static boolean isDisabled( DirectoryService service, String schemaName )
330    {
331        Schema schema = service.getSchemaManager().getLoadedSchema( schemaName );
332
333        return ( schema == null ) || schema.isDisabled();
334    }
335
336
337    /**
338     * A helper method which tells if a schema is loaded.
339     * 
340     * @param service The Directory Service 
341     * @param schemaName The schema to check
342     * @return <tt>true</tt> if the schema is loaded
343     */
344    public static boolean isLoaded( DirectoryService service, String schemaName )
345    {
346        Schema schema = service.getSchemaManager().getLoadedSchema( schemaName );
347
348        return ( schema != null );
349    }
350
351
352    /**
353     * A helper method which tells if a schema is enabled. A shema must be
354     * loaded and enabled.
355     * 
356     * @param service The Directory Service 
357     * @param schemaName The name of the Schema to check
358     * @return <tt>true</tt> if the schema is enabled
359     */
360    public static boolean isEnabled( DirectoryService service, String schemaName )
361    {
362        Schema schema = service.getSchemaManager().getLoadedSchema( schemaName );
363
364        return ( schema != null ) && schema.isEnabled();
365    }
366
367
368    /**
369     * Gets a LdapCoreSessionConnection bound using the default admin Dn uid=admin,ou=system and password "secret"
370     * 
371     * @param dirService The Directory Service to be connected to
372     * @return A LdapCoreSessionConnection instance
373     * @exception LdapException If the connection could not be established.
374     */
375    public static LdapConnection getAdminConnection( DirectoryService dirService ) throws LdapException
376    {
377        return getConnectionAs( dirService, ServerDNConstants.ADMIN_SYSTEM_DN, "secret" );
378    }
379
380
381    /**
382     * Gets a LdapCoreSessionConnection bound using a user's DN and a password. We will bind using those
383     * credentials.
384     * 
385     * @param dirService The Directory Service to be connected to
386     * @param dn The User's DN as a String
387     * @param password The User's password as a String
388     * @return A LdapCoreSessionConnection instance
389     * @exception LdapException If the connection could not be established.
390     */
391    public static LdapConnection getConnectionAs( DirectoryService dirService, String dn, String password )
392        throws LdapException
393    {
394        return getConnectionAs( dirService, new Dn( dn ), password );
395    }
396
397
398    /**
399     * Gets a LdapCoreSessionConnection bound using a user's DN and a password. We will bind using those
400     * credentials.
401     * 
402     * @param dirService The Directory Service to be connected to
403     * @param dn The User's DN
404     * @param password The User's password as a String
405     * @return A LdapCoreSessionConnection instance
406     * @exception LdapException If the connection could not be established.
407     */
408    public static LdapConnection getConnectionAs( DirectoryService dirService, Dn dn, String password )
409        throws LdapException
410    {
411        LdapCoreSessionConnection connection = new LdapCoreSessionConnection();
412
413        connection.setDirectoryService( dirService );
414
415        connection.bind( dn, password );
416
417        return connection;
418    }
419
420
421    /**
422     * Gets a LdapNetworkConnection bound using a user's DN and a password. We will bind using those
423     * credentials.
424     * 
425     * @param host The server to connect to
426     * @param port The port to connect on
427     * @param dn The User's DN as a String
428     * @param password The User's password as a String
429     * @return A LdapNetworkConnection instance
430     * @exception LdapException If the connection could not be established.
431     */
432    public static LdapConnection getNetworkConnectionAs( String host, int port, String dn, String password )
433        throws LdapException
434    {
435        LdapConnection connection = new LdapNetworkConnection( host, port );
436
437        connection.bind( dn, password );
438        OPEN_CONNECTIONS.add( connection );
439        
440        return connection;
441    }
442
443
444    /**
445     * Gets an anonymous LdapNetworkConnection
446     * 
447     * @param ldapServer The LdapServer to be connected to
448     * @return A LdapNetworkConnection instance
449     * @exception LdapException If the connection could not be established.
450     */
451    public static LdapConnection getAnonymousNetworkConnection( LdapServer ldapServer ) throws LdapException
452    {
453        return getAnonymousNetworkConnection( Network.LOOPBACK_HOSTNAME, ldapServer.getPort() );
454    }
455
456
457    /**
458     * Gets an anonymous LdapNetworkConnection
459     * 
460     * @param host The server to connect to
461     * @param port The port to connect on
462     * @return A LdapNetworkConnection instance
463     * @exception LdapException If the connection could not be established.
464     */
465    public static LdapConnection getAnonymousNetworkConnection( String host, int port ) throws LdapException
466    {
467        LdapConnection connection = new LdapNetworkConnection( host, port );
468        connection.bind();
469
470        OPEN_CONNECTIONS.add( connection );
471
472        return connection;
473    }
474
475
476    /**
477     * Gets a LdapNetworkConnection bound to the Admin user (uid=admin,ou=system).
478     * 
479     * @param ldapServer The LdapServer to be connected to
480     * @return A LdapNetworkConnection instance
481     * @exception LdapException If the connection could not be established.
482     */
483    public static LdapConnection getAdminNetworkConnection( LdapServer ldapServer ) throws LdapException
484    {
485        LdapConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, ldapServer.getPort() );
486
487        connection.setTimeOut( 30000L );
488        connection.bind( ServerDNConstants.ADMIN_SYSTEM_DN, "secret" );
489
490        OPEN_CONNECTIONS.add( connection );
491
492        return connection;
493    }
494
495
496    /**
497     * Gets a LdapNetworkConnection bound using a user's DN and a password. We will bind using those
498     * credentials. We specify a LdapServer instance too.
499     * 
500     * @param ldapServer The LdapServer to be connected to
501     * @param userDn The User's DN as a String
502     * @param password The User's password as a String
503     * @return A LdapNetworkConnection instance
504     * @exception LdapException If the connection could not be established.
505     */
506    public static LdapConnection getNetworkConnectionAs( LdapServer ldapServer, String userDn, String password )
507        throws LdapException
508    {
509        return getNetworkConnectionAs( Network.LOOPBACK_HOSTNAME, ldapServer.getPort(), userDn, password );
510    }
511
512
513    public static void closeConnections()
514    {
515
516        for ( LdapConnection con : OPEN_CONNECTIONS )
517        {
518            if ( con == null )
519            {
520                continue;
521            }
522
523            try
524            {
525                if ( con.isConnected() )
526                {
527                    con.close();
528                }
529            }
530            catch ( Exception e )
531            {
532                // shouldn't happen, but print the stacktrace so that less pain during development to find the cause
533                e.printStackTrace();
534            }
535        }
536
537        OPEN_CONNECTIONS.clear();
538    }
539}