1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.server.core.operations.bind;
21  
22  
23  import java.util.Hashtable;
24  
25  import javax.naming.Context;
26  import javax.naming.InvalidNameException;
27  import javax.naming.NamingEnumeration;
28  import javax.naming.NamingException;
29  import javax.naming.OperationNotSupportedException;
30  import javax.naming.directory.DirContext;
31  import javax.naming.directory.InitialDirContext;
32  import javax.naming.directory.SearchControls;
33  import javax.naming.directory.SearchResult;
34  
35  import org.apache.directory.server.core.DirectoryService;
36  import org.apache.directory.server.core.integ.CiRunner;
37  import org.apache.directory.server.core.jndi.CoreContextFactory;
38  import org.apache.directory.shared.ldap.constants.JndiPropertyConstants;
39  import org.apache.directory.shared.ldap.exception.LdapAuthenticationException;
40  import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
41  import org.apache.directory.shared.ldap.message.AliasDerefMode;
42  import org.junit.Test;
43  import org.junit.runner.RunWith;
44  import static org.junit.Assert.assertEquals;
45  import static org.junit.Assert.assertFalse;
46  import static org.junit.Assert.assertNotNull;
47  import static org.junit.Assert.assertTrue;
48  import static org.junit.Assert.fail;
49  
50  
51  /**
52   * Test the Simple BindRequest
53   *
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev$
56   */
57  @RunWith ( CiRunner.class )
58  public class SimpleBindIT
59  {
60  	/** The directory service */
61      public static DirectoryService service;
62      
63      /**
64       * A method to do a search
65       */
66      private NamingEnumeration<SearchResult> search( DirContext ctx, String baseDn, String filter, int scope ) throws NamingException
67      {
68          SearchControls controls = new SearchControls();
69          controls.setSearchScope( scope );
70          controls.setDerefLinkFlag( false );
71          controls.setReturningAttributes( new String[]{ "*", "+"} );
72          ctx.addToEnvironment( JndiPropertyConstants.JNDI_LDAP_DAP_DEREF_ALIASES,
73                  AliasDerefMode.NEVER_DEREF_ALIASES.getJndiValue() );
74  
75          NamingEnumeration<SearchResult> list = ctx.search( baseDn, filter, controls );
76          return list;
77      }
78      
79      
80      /**
81       * try to connect using a known user/password and read an entry.
82       * 
83       * @throws Exception on error
84       */
85      @Test
86      public void testSimpleBindUserPassword()
87      {
88      	// We will bind using JNDI
89      	// Set up the environment for creating the initial context
90          Hashtable<String, Object> env = new Hashtable<String, Object>();
91          env.put( DirectoryService.JNDI_KEY, service );
92          env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
93          env.put( Context.PROVIDER_URL, "ou=system" );
94  
95      	// Authenticate as admin and password "secret"
96      	env.put(Context.SECURITY_AUTHENTICATION, "simple");
97      	env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
98      	env.put(Context.SECURITY_CREDENTIALS, "secret");
99  
100     	DirContext ctx = null;
101     	
102     	// Create the initial context
103     	try
104     	{
105     		ctx = new InitialDirContext(env);
106     	}
107     	catch ( NamingException ne )
108     	{
109     		fail();
110     	}
111     	
112     	try
113     	{
114     		ctx.close();
115     	}
116     	catch ( NamingException ne )
117     	{
118     		fail();
119     	}
120     }
121     
122     
123     /**
124      * try to connect using a known user but with a bad password: we should get a invalidCredentials error.
125      * 
126      * @throws Exception on error
127      */
128     @Test
129     public void testSimpleBindUserBadPassword()
130     {
131     	// We will bind using JNDI
132     	// Set up the environment for creating the initial context
133         Hashtable<String, Object> env = new Hashtable<String, Object>();
134         env.put( DirectoryService.JNDI_KEY, service );
135         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
136         env.put( Context.PROVIDER_URL, "ou=system" );
137 
138     	// Authenticate as admin and password "badsecret"
139     	env.put(Context.SECURITY_AUTHENTICATION, "simple");
140     	env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
141     	env.put(Context.SECURITY_CREDENTIALS, "badsecret");
142 
143     	// Create the initial context
144     	try
145     	{
146     		new InitialDirContext(env);
147 
148         	// We should not be connected
149     		fail();
150     	}
151     	catch ( LdapAuthenticationException lae )
152     	{
153     		assertTrue( true );
154     	}
155     	catch ( NamingException ne )
156     	{
157     		fail();
158     	}
159     }
160     
161     
162     /**
163      * try to connect using a user with an invalid DN: we should get a invalidDNSyntax error.
164      * 
165      * @throws Exception on error
166      */
167     @Test
168     public void testSimpleBindBadUserPassword()
169     {
170     	// We will bind using JNDI
171     	// Set up the environment for creating the initial context
172         Hashtable<String, Object> env = new Hashtable<String, Object>();
173         env.put( DirectoryService.JNDI_KEY, service );
174         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
175         env.put( Context.PROVIDER_URL, "ou=system" );
176 
177     	// Authenticate as admin and password "secret"
178     	env.put(Context.SECURITY_AUTHENTICATION, "simple");
179     	env.put(Context.SECURITY_PRINCIPAL, "admin");
180     	env.put(Context.SECURITY_CREDENTIALS, "secret");
181 
182     	// Create the initial context
183     	try
184     	{
185     		new InitialDirContext(env);
186 
187         	// We should not be connected
188     		fail();
189     	}
190     	catch ( InvalidNameException ine )
191     	{
192     		assertEquals( "Bad DN : admin", ine.getMessage() );
193     	}
194     	catch ( NamingException ne )
195     	{
196     		fail();
197     	}
198     }
199     
200 
201     /**
202      * try to connect using a unknown user: we should get a invalidCredentials error.
203      * 
204      * @throws Exception on error
205      */
206     @Test
207     public void testSimpleBindUnknowUserPassword()
208     {
209     	// We will bind using JNDI
210     	// Set up the environment for creating the initial context
211         Hashtable<String, Object> env = new Hashtable<String, Object>();
212         env.put( DirectoryService.JNDI_KEY, service );
213         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
214         env.put( Context.PROVIDER_URL, "ou=system" );
215 
216     	// Authenticate as uid=unknown and password "secret"
217     	env.put(Context.SECURITY_AUTHENTICATION, "simple");
218     	env.put(Context.SECURITY_PRINCIPAL, "uid=unknown,ou=system");
219     	env.put(Context.SECURITY_CREDENTIALS, "secret");
220 
221     	// Create the initial context
222     	try
223     	{
224     		new InitialDirContext(env);
225 
226         	// We should not be connected
227     		fail();
228     	}
229     	catch ( LdapAuthenticationException lae )
230     	{
231     		assertEquals( "Cannot authenticate user uid=unknown,ou=system", lae.getMessage() );
232     	}
233     	catch ( NamingException ne )
234     	{
235     		fail();
236     	}
237     }
238     
239     
240     /**
241      * covers the anonymous authentication : we should be able to read the rootDSE, but that's it
242      * 
243      * @throws Exception on error
244      */
245     @Test
246     public void testSimpleBindNoUserNoPassword()
247     {
248     	// We will bind using JNDI
249     	// Set up the environment for creating the initial context
250         Hashtable<String, Object> env = new Hashtable<String, Object>();
251         env.put( DirectoryService.JNDI_KEY, service );
252         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
253         
254         // Bind on the rootDSE
255         env.put( Context.PROVIDER_URL, "" );
256 
257     	// Authenticate as admin and password "secret"
258     	env.put(Context.SECURITY_AUTHENTICATION, "simple");
259     	env.put(Context.SECURITY_PRINCIPAL, "");
260     	env.put(Context.SECURITY_CREDENTIALS, "");
261 
262     	DirContext ctx = null;
263     	
264     	// Create the initial context
265     	try
266     	{
267     		ctx = new InitialDirContext(env);
268     	}
269     	catch ( NamingException ne )
270     	{
271     		fail();
272     	}
273     	
274     	// We should be anonymous here. 
275     	// Check that we can read the rootDSE
276     	try
277     	{
278     		NamingEnumeration<SearchResult> list = search( ctx, "", "(ObjectClass=*)", SearchControls.OBJECT_SCOPE );
279     		
280     		assertNotNull( list );
281     		
282             while ( list.hasMore() )
283             {
284                 SearchResult result = list.next();
285                 assertNotNull( result );
286             }
287     	}
288     	catch ( NamingException ne )
289     	{
290     		fail();
291     	}
292 
293     	// Check that we cannot read another entry being anonymous
294     	try
295     	{
296     		NamingEnumeration<SearchResult> list = search( ctx, "uid=admin, ou=system", "(ObjectClass=*)", SearchControls.OBJECT_SCOPE );
297     		
298     		assertNotNull( list );
299     		assertFalse( list.hasMore() );
300     	}
301     	catch ( NamingException ne )
302     	{
303     		fail();
304     	}
305 
306     	try
307     	{
308     		ctx.close();
309     	}
310     	catch ( NamingException ne )
311     	{
312     		fail();
313     	}
314     }
315     
316     
317     /**
318      * covers the Unauthenticated case : we should get a UnwillingToPerform error.
319      * 
320      * @throws Exception on error
321      */
322     @Test
323     public void testSimpleBindUserNoPassword()
324     {
325     	// We will bind using JNDI
326     	// Set up the environment for creating the initial context
327         Hashtable<String, Object> env = new Hashtable<String, Object>();
328         env.put( DirectoryService.JNDI_KEY, service );
329         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
330         
331         // Bind on the rootDSE
332         env.put( Context.PROVIDER_URL, "" );
333 
334     	// Authenticate as admin and password "secret"
335     	env.put(Context.SECURITY_AUTHENTICATION, "simple");
336     	env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
337     	env.put(Context.SECURITY_CREDENTIALS, "");
338 
339     	// Create the initial context
340     	try
341     	{
342     		new InitialDirContext(env);
343     	}
344     	catch ( OperationNotSupportedException onse )
345     	{
346     		assertEquals( "Cannot Bind for DN uid=admin,ou=system", onse.getMessage() );
347     	}
348     	catch ( NamingException ne )
349     	{
350     		fail();
351     	}
352     }
353     
354     
355     /**
356      * not allowed by the server. We should get a invalidCredentials error.
357      * 
358      * @throws Exception on error
359      */
360     @Test
361     public void testSimpleBindNoUserPassword() throws Exception
362     {
363     	// We will bind using JNDI
364     	// Set up the environment for creating the initial context
365         Hashtable<String, Object> env = new Hashtable<String, Object>();
366         env.put( DirectoryService.JNDI_KEY, service );
367         env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
368         
369         // Bind on the rootDSE
370         env.put( Context.PROVIDER_URL, "" );
371 
372     	// Authenticate as admin and password "secret"
373     	env.put(Context.SECURITY_AUTHENTICATION, "simple");
374     	env.put(Context.SECURITY_PRINCIPAL, "");
375     	env.put(Context.SECURITY_CREDENTIALS, "secret");
376 
377     	// Create the initial context
378     	try
379     	{
380     		new InitialDirContext(env);
381     	}
382     	catch ( LdapNameNotFoundException lnnfe )
383     	{
384     		assertTrue( true );
385     	}
386     	catch ( NamingException ne )
387     	{
388     		fail();
389     	}
390     }
391 }