john pfeiffer
  • Home
  • Categories
  • Tags
  • Archives

LdapSearchCLI

// 2012-10-17 johnpfeiffer requires validation-api, bval-core, bval-jsr303, commons-beanutils-core, commons-lang3
// TODO: check for number of arguments passed?
// TODO: ensure LDAPS with self signed
// TODO: self root CA + intermediate + ssl

import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Set;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapName;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

public class LdapSearch
{
    public static final String CLASSVERSION = "0.4";
    private static final String CONNECTIONTIMEOUTMILLISECONDS = "15000";
    private static final String READQUERYTIMEOUTMILLISECONDS = "15000";
    private static final String CORRECTUSAGE = "java -jar LdapSearch-" + CLASSVERSION + " hostname port baseDN binduserDN binduserPassword userAttribute searchBase";

    // @NotNull( message = "ERROR: Directory Context cannot be null" )
    DirContext ctx = null;
    @NotNull( message = "ERROR: hostname cannot be null" )
    private String hostname;

    @Min( value = 0 , message = "ERROR: port number too small, must be between 0 and 65535" )
    @Max( value = 65535 , message = "ERROR: port number too large, must be between 0 and 65535" )
    private int port;

    @NotNull( message = "ERROR: baseDN cannot be null" )
    private LdapName baseDN;

    @NotNull( message = "ERROR: binding user DN cannot be null" )
    private LdapName bindUserDN;

    @NotNull( message = "ERROR: binding user password cannot be null" )
    private String bindUserPassword;

    @NotNull( message = "ERROR: user attribute cannot be null" )
    private String userAttribute;// = "sAMAccountName"; // ldap uses uid

    @NotNull( message = "ERROR: search base cannot be null" )
    private LdapName searchBase;

    public static class Builder
    {
        DirContext ctx = null;
        private String hostname;
        private int port;
        private LdapName baseDN;
        private LdapName bindUserDN;
        private String bindUserPassword;
        private String userAttribute;
        private LdapName searchBase;

        public Builder hostname( String value )
        {
            this.hostname = value;
            return this;
        }

        public Builder port( int value )
        {
            this.port = value;
            return this;
        }

        public Builder bindUserDN( LdapName value )
        {
            this.bindUserDN = value;
            return this;
        }

        public Builder password( String value )
        {
            this.bindUserPassword = value;
            return this;
        }

        public Builder baseDN( LdapName value )
        {
            this.baseDN = value;
            return this;
        }

        public Builder userAttribute( String value )
        {
            this.userAttribute = value;
            return this;
        }

        public Builder searchBase( LdapName value )
        {
            this.searchBase = value;
            return this;
        }

        public LdapSearch build() throws IllegalArgumentException
        {
            LdapSearch ldap = new LdapSearch( this ); // object may exist in an incomplete or illegal state
            LdapSearchValidator( ldap );
            return ldap;
        }

        private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

        private void LdapSearchValidator( LdapSearch ldap ) throws IllegalArgumentException
        {
            Set <ConstraintViolation <LdapSearch>> violations = validator.validate( ldap );
            if( !violations.isEmpty() )
            {
                for( ConstraintViolation <?> v : violations )
                {
                    throw new IllegalArgumentException( v.getMessage() );
                }
            }
            // ConstraintViolations ensure all of the values are non null

            if( this.hostname.isEmpty() )
            {
                throw new IllegalArgumentException( "ERROR: hostname cannot be empty" );
            }
            try
            {
                InetAddress.getByName( this.hostname );
            }catch( Exception e )
            {
                throw new IllegalArgumentException( "ERROR: hostname " + e.getMessage() );
            }

            if( this.userAttribute.isEmpty() )
            {
                throw new IllegalArgumentException( "ERROR: userAttribute cannot be empty" );
            }

            if( !this.userAttribute.equalsIgnoreCase( "uid" ) && !this.userAttribute.toLowerCase().equalsIgnoreCase( "samaccountname" ) )
            {
                throw new IllegalArgumentException( "ERROR: userAttribute is unsupported: " + this.userAttribute );
            }
        }

    } // end inner class Builder

    private LdapSearch( Builder builder ) throws IllegalArgumentException
    {
        this.hostname = builder.hostname;
        this.port = builder.port;
        this.baseDN = builder.baseDN;
        this.bindUserDN = builder.bindUserDN;
        this.bindUserPassword = builder.bindUserPassword;
        this.baseDN = builder.baseDN;
        this.searchBase = builder.searchBase;
        this.userAttribute = builder.userAttribute;
    }

    private long calculateRuntime( long start )
    {
        return ( System.currentTimeMillis() - start );
    }

    public ArrayList <String> queryForLdapName( LdapName ldapName ) throws SocketTimeoutException
    {
        long startTimeMilliseconds = System.currentTimeMillis();
        Hashtable <String , String> env = buildEnvironment();
        ArrayList <String> results = new ArrayList <String>();
        try
        {
            ctx = new InitialDirContext( env );
            results = searchWithFilterLdapName( ctx , ldapName );
            results.add( " SUCCESSFUL QUERY!  " );
        }catch( NamingException ne )
        {
            if( ne.getRootCause() != null )
            {
                results.add( "ERROR: " + ne.getRootCause() + " " );
            }
            results.add( ne.getMessage() );

            long connectionTimeoutMax = Long.parseLong( CONNECTIONTIMEOUTMILLISECONDS );
            if( connectionTimeoutMax < calculateRuntime( startTimeMilliseconds ) )
            {
                results.add( " " + calculateRuntime( startTimeMilliseconds ) + " exceeds timeout max " + connectionTimeoutMax );
            }
        }
        results.add( "Query took " + calculateRuntime( startTimeMilliseconds ) + " ms" );
        if( ctx != null )
        {
            try
            {
                ctx.close();
            }catch( Exception e )
            {
            }
        }
        return results;
    }

    public ArrayList <String> queryForBindingUser()
    {
        long startTimeMilliseconds = System.currentTimeMillis();
        Hashtable <String , String> env = buildEnvironment();
        ArrayList <String> results = new ArrayList <String>();
        try
        {
            ctx = new InitialDirContext( env );
            String userName = getUserNameFromBindingUserDN();
            LdapName ldapName = new LdapName( userName );
            results = searchWithFilterLdapName( ctx , ldapName );
            results.add( " SUCCESSFUL QUERY!  " );
        }catch( SocketTimeoutException ste )
        {
            results.add( "ERROR: SocketTimeoutException: " + ste.getMessage() );
        }catch( NamingException ne )
        {
            if( ne.getRootCause() != null )
            {
                results.add( "ERROR: " + ne.getRootCause() + " " );
            }
            results.add( ne.getMessage() );
            long connectionTimeoutMax = Long.parseLong( CONNECTIONTIMEOUTMILLISECONDS );
            if( connectionTimeoutMax < calculateRuntime( startTimeMilliseconds ) )
            {
                results.add( " " + calculateRuntime( startTimeMilliseconds ) + " exceeds timeout max " + connectionTimeoutMax );
            }
        }
        results.add( calculateRuntime( startTimeMilliseconds ) + " ms" );
        if( ctx != null )
        {
            try
            {
                ctx.close();
            }catch( Exception e )
            {
            }
        }
        return results;
    }

    private Hashtable <String , String> buildEnvironment()
    {
        Hashtable <String , String> env = new Hashtable <String , String>();
        env.put( Context.INITIAL_CONTEXT_FACTORY , "com.sun.jndi.ldap.LdapCtxFactory" );
        env.put( "com.sun.jndi.ldap.connect.pool" , "true" );
        env.put( "com.sun.jndi.ldap.connect.timeout" , CONNECTIONTIMEOUTMILLISECONDS );
        env.put( "com.sun.jndi.ldap.read.timeout" , READQUERYTIMEOUTMILLISECONDS ); // time that a query can take
        env.put( Context.REFERRAL , "follow" );
        env.put( Context.SECURITY_AUTHENTICATION , "simple" );
        env.put( Context.SECURITY_PRINCIPAL , bindUserDN.toString() );
        env.put( Context.SECURITY_CREDENTIALS , bindUserPassword );
        if( port == 636 )
        {
            env.put( Context.PROVIDER_URL , "ldaps://" + hostname + ":" + port + "/" + baseDN );
        }else
        {
            env.put( Context.PROVIDER_URL , "ldap://" + hostname + ":" + port + "/" + baseDN );
        }
        return env;
    }

    private String getUserNameFromBindingUserDN()
    {
        String userName = "";
        for( Enumeration <String> names = this.bindUserDN.getAll() ; names.hasMoreElements() ; )
        {
            userName = names.nextElement();
        }
        return userName;
    }

    private ArrayList <String> searchWithFilterLdapName( DirContext ctx , LdapName ldapName ) throws NamingException , SocketTimeoutException
    {
        ArrayList <String> resultList = new ArrayList <String>();
        String[] attributeFilter =
        { "distinguishedname", this.userAttribute };
        SearchControls searchcontrols = buildSearchControls( attributeFilter );
        String searchFilter = "(" + ldapName + ")";
        NamingEnumeration <SearchResult> results = null;
        try
        {
            results = ctx.search( this.searchBase , searchFilter , searchcontrols );
            while( results.hasMore() )
            {
                SearchResult sr = results.next();
                Attributes attrs = sr.getAttributes();
                resultList.add( attrs.toString() );

                // resultList.add( sr.getNameInNamespace() );
                // System.out.println( attrs.getIDs() );
                // Attribute attr = attrs.getIDs()
                // resultList.add( )
                // Attribute attr = attrs.get( userAttribute );
                // System.out.println( "RETRIEVED: " + attr.get() );
            }
        }catch( PartialResultException e )
        { // System.out.println( "ignoring partial result exception" );
        }

        if( results != null )
        {
            try
            {
                results.close();
            }catch( Exception e )
            {
            }
        }
        return resultList;
    }

    private SearchControls buildSearchControls( String[] attributeFilter )
    {
        SearchControls searchcontrols = new SearchControls(); // tree limit, count limit, time limit, attribs to return
        searchcontrols.setSearchScope( SearchControls.SUBTREE_SCOPE );
        searchcontrols.setReturningAttributes( attributeFilter );
        return searchcontrols;
    }

    public static void main( String[] args ) throws Exception
    {
        if( args.length != 7)
        {
            System.err.println( CORRECTUSAGE );
            System.exit(1);
        }

        String hostname = args[0];
        int port = Integer.parseInt( args[1] );
        LdapName baseDN = new LdapName( args[2] );
        LdapName bindUserDN = new LdapName( args[3] );
        String password = args[4];
        String userAttribute = args[5];
//      LdapName constructorSearchBase = new LdapName( args[6] );
        LdapName searchBase = new LdapName( "" );
        LdapSearch main = new LdapSearch.Builder().hostname( hostname ).port( port ).baseDN( baseDN )
                .bindUserDN( bindUserDN ).password( password ).userAttribute( userAttribute ).searchBase( searchBase )
                .build();
        LdapName targetLdapName = new LdapName( args[3] );
        ArrayList <String> results = main.queryForLdapName( targetLdapName );
        System.out.println( "LDAP User Query Test: " + results.toString() );

    }

} // end class

  • « Report CloudStorageGatewayTest
  • wc »

Published

Oct 17, 2012

Category

java-classes

~879 words

Tags

  • classes 92
  • java 252
  • ldapsearchcli 1