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.authn;
21  
22  
23  import org.apache.directory.server.core.DirectoryService;
24  import org.apache.directory.server.core.integ.CiRunner;
25  import org.apache.directory.server.core.jndi.ServerLdapContext;
26  
27  import static org.apache.directory.server.core.integ.IntegrationUtils.getUserAddLdif;
28  import static org.apache.directory.server.core.integ.IntegrationUtils.apply;
29  
30  import org.apache.directory.shared.ldap.name.LdapDN;
31  import org.apache.directory.shared.ldap.util.ArrayUtils;
32  import org.apache.directory.shared.ldap.util.StringTools;
33  
34  import static org.junit.Assert.assertTrue;
35  import static org.junit.Assert.assertNotNull;
36  import static org.junit.Assert.fail;
37  
38  import org.junit.Test;
39  import org.junit.runner.RunWith;
40  
41  import javax.naming.NamingException;
42  import javax.naming.directory.Attribute;
43  import javax.naming.directory.Attributes;
44  import javax.naming.directory.BasicAttribute;
45  import javax.naming.directory.DirContext;
46  import javax.naming.directory.ModificationItem;
47  import javax.naming.ldap.LdapContext;
48  
49  
50  /**
51   * A set of simple tests to make sure simple authentication is working as it
52   * should.
53   *
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev: 691179 $
56   */
57  @RunWith( CiRunner.class )
58  public class SimpleAuthenticationIT
59  {
60      public static DirectoryService service;
61  
62  
63      /**
64       * Checks all attributes of the admin account entry minus the userPassword
65       * attribute.
66       *
67       * @param attrs the entries attributes
68       */
69      protected void performAdminAccountChecks( Attributes attrs )
70      {
71          assertTrue( attrs.get( "objectClass" ).contains( "top" ) );
72          assertTrue( attrs.get( "objectClass" ).contains( "person" ) );
73          assertTrue( attrs.get( "objectClass" ).contains( "organizationalPerson" ) );
74          assertTrue( attrs.get( "objectClass" ).contains( "inetOrgPerson" ) );
75          assertTrue( attrs.get( "displayName" ).contains( "Directory Superuser" ) );
76      }
77  
78  
79      /**
80       * Check the creation of the admin account and persistence across restarts.
81       *
82       * @throws NamingException if there are failures
83       */
84      @Test
85      public void testAdminAccountCreation() throws Exception
86      {
87          String userDn = "uid=admin,ou=system";
88          LdapContext ctx = new ServerLdapContext( service, 
89              service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( "ou=system" ) );
90          Attributes attrs = ctx.getAttributes( "uid=admin" );
91          performAdminAccountChecks( attrs );
92          assertTrue( ArrayUtils.isEquals( attrs.get( "userPassword" ).get(), StringTools.getBytesUtf8( "secret" ) ) );
93          ctx.close();
94  
95          service.shutdown();
96          service.startup();
97  
98          ctx = new ServerLdapContext( service, 
99              service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( "ou=system" ) );
100         attrs = ctx.getAttributes( "uid=admin" );
101         performAdminAccountChecks( attrs );
102         assertTrue( ArrayUtils.isEquals( attrs.get( "userPassword" ).get(), StringTools.getBytesUtf8( "secret" ) ) );
103         ctx.close();
104     }
105 
106 
107     @Test
108     public void test3UseAkarasulu() throws Exception
109     {
110         apply( service, getUserAddLdif() );
111         String userDn = "uid=akarasulu,ou=users,ou=system";
112         LdapContext ctx = new ServerLdapContext( service, 
113             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
114 
115         Attributes attrs = ctx.getAttributes( "" );
116         Attribute ou = attrs.get( "ou" );
117         assertTrue( ou.contains( "Engineering" ) );
118         assertTrue( ou.contains( "People" ) );
119 
120         Attribute objectClass = attrs.get( "objectClass" );
121         assertTrue( objectClass.contains( "top" ) );
122         assertTrue( objectClass.contains( "person" ) );
123         assertTrue( objectClass.contains( "organizationalPerson" ) );
124         assertTrue( objectClass.contains( "inetOrgPerson" ) );
125 
126         assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
127         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
128         assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
129         assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
130         assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
131         assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
132         assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
133         assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
134         assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
135     }
136 
137 
138     /**
139      * Tests to make sure we can authenticate after the database has already
140      * been started by the admin user when simple authentication is in effect.
141      *
142      * @throws Exception if anything goes wrong
143      */
144     @Test
145     public void test8PassPrincAuthTypeSimple() throws Exception
146     {
147         String userDn = "uid=admin,ou=system";
148         assertNotNull( new ServerLdapContext( service, 
149             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) ) );
150     }
151 
152 
153     /**
154      * Checks to see if we can authenticate as a test user after the admin fires
155      * up and builds the the system database.
156      *
157      * @throws Exception if anything goes wrong
158      */
159     @Test
160     public void test10TestNonAdminUser() throws Exception
161     {
162         apply( service, getUserAddLdif() );
163         String userDn = "uid=akarasulu,ou=users,ou=system";
164         assertNotNull( new ServerLdapContext( service, 
165             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) ) );
166     }
167 
168 
169     @Test
170     public void test11InvalidateCredentialCache() throws Exception
171     {
172         apply( service, getUserAddLdif() );
173         String userDn = "uid=akarasulu,ou=users,ou=system";
174         
175         LdapContext ctx = new ServerLdapContext( service, 
176             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
177         assertNotNull( ctx );
178         Attributes attrs = ctx.getAttributes( "" );
179         Attribute ou = attrs.get( "ou" );
180         assertTrue( ou.contains( "Engineering" ) );
181         assertTrue( ou.contains( "People" ) );
182 
183         Attribute objectClass = attrs.get( "objectClass" );
184         assertTrue( objectClass.contains( "top" ) );
185         assertTrue( objectClass.contains( "person" ) );
186         assertTrue( objectClass.contains( "organizationalPerson" ) );
187         assertTrue( objectClass.contains( "inetOrgPerson" ) );
188 
189         assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
190         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
191         assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
192         assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
193         assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
194         assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
195         assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
196         assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
197         assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
198 
199         // now modify the password for akarasulu
200         Attribute userPasswordAttribute = new BasicAttribute( "userPassword", "newpwd" );
201         ctx.modifyAttributes( "", new ModificationItem[] {
202             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
203 
204         // close and try with old password (should fail)
205         ctx.close();
206 
207         try
208         {
209             new ServerLdapContext( service, 
210                 service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
211             fail( "Authentication with old password should fail" );
212         }
213         catch ( NamingException e )
214         {
215             // we should fail
216         }
217 
218         // close and try again now with new password (should fail)
219         ctx.close();
220         ctx = new ServerLdapContext( service, 
221             service.getSession( new LdapDN( userDn ), "newpwd".getBytes() ), new LdapDN( userDn ) ); 
222         attrs = ctx.getAttributes( "" );
223         ou = attrs.get( "ou" );
224         assertTrue( ou.contains( "Engineering" ) );
225         assertTrue( ou.contains( "People" ) );
226 
227         objectClass = attrs.get( "objectClass" );
228         assertTrue( objectClass.contains( "top" ) );
229         assertTrue( objectClass.contains( "person" ) );
230         assertTrue( objectClass.contains( "organizationalPerson" ) );
231         assertTrue( objectClass.contains( "inetOrgPerson" ) );
232 
233         assertTrue( attrs.get( "telephonenumber" ).contains( "+1 408 555 4798" ) );
234         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
235         assertTrue( attrs.get( "givenname" ).contains( "Alex" ) );
236         assertTrue( attrs.get( "mail" ).contains( "akarasulu@apache.org" ) );
237         assertTrue( attrs.get( "l" ).contains( "Bogusville" ) );
238         assertTrue( attrs.get( "sn" ).contains( "Karasulu" ) );
239         assertTrue( attrs.get( "cn" ).contains( "Alex Karasulu" ) );
240         assertTrue( attrs.get( "facsimiletelephonenumber" ).contains( "+1 408 555 9751" ) );
241         assertTrue( attrs.get( "roomnumber" ).contains( "4612" ) );
242     }
243 
244 
245     @Test
246     public void testSHA() throws Exception
247     {
248         apply( service, getUserAddLdif() );
249         String userDn = "uid=akarasulu,ou=users,ou=system";
250         LdapContext ctx = new ServerLdapContext( service, 
251             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) ); 
252 
253         // Check that we can get the attributes
254         Attributes attrs = ctx.getAttributes( "" );
255         assertNotNull( attrs );
256         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
257 
258         // now modify the password for akarasulu : 'secret', encrypted using SHA
259         Attribute userPasswordAttribute = new BasicAttribute( "userPassword", "{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ=" );
260         ctx.modifyAttributes( "", new ModificationItem[] {
261             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
262 
263         // close and try with old password (should fail)
264         ctx.close();
265 
266         try
267         {
268             ctx = new ServerLdapContext( service, 
269                 service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
270             fail( "Authentication with old password should fail" );
271         }
272         catch ( Exception e )
273         {
274             // we should fail
275         }
276         finally
277         {
278             if ( ctx != null )
279             {
280                 ctx.close();
281             }
282         }
283 
284         // try again now with new password (should be successfull)
285         ctx = new ServerLdapContext( service, 
286             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
287         attrs = ctx.getAttributes( "" );
288         assertNotNull( attrs );
289         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
290 
291         // close and try again now with new password, to check that the
292         // cache is updated (should be successfull)
293         ctx.close();
294         ctx = new ServerLdapContext( service, 
295             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) ); 
296         attrs = ctx.getAttributes( "" );
297         assertNotNull( attrs );
298         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
299     }
300 
301 
302     @Test
303     public void testSSHA() throws Exception
304     {
305         apply( service, getUserAddLdif() );
306         String userDn = "uid=akarasulu,ou=users,ou=system";
307         LdapContext ctx = new ServerLdapContext( service, 
308             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
309 
310         // Check that we can get the attributes
311         Attributes attrs = ctx.getAttributes( "" );
312         assertNotNull( attrs );
313         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
314 
315         // now modify the password for akarasulu : 'secret', encrypted using SHA
316         BasicAttribute userPasswordAttribute = new BasicAttribute( "userPassword", "{SSHA}mjVVxasFkk59wMW4L1Ldt+YCblfhULHs03WW7g==" );
317         ctx.modifyAttributes( "", new ModificationItem[] {
318             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
319 
320         // close and try with old password (should fail)
321         ctx.close();
322 
323         try
324         {
325             ctx = new ServerLdapContext( service, 
326                 service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
327             fail( "Authentication with old password should fail" );
328         }
329         catch ( Exception e )
330         {
331             // we should fail
332         }
333         finally
334         {
335             if ( ctx != null )
336             {
337                 ctx.close();
338             }
339         }
340 
341         // try again now with new password (should be successfull)
342         ctx = new ServerLdapContext( service, 
343             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
344         attrs = ctx.getAttributes( "" );
345         assertNotNull( attrs );
346         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
347 
348         // close and try again now with new password, to check that the
349         // cache is updated (should be successfull)
350         ctx = new ServerLdapContext( service, 
351             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
352         attrs = ctx.getAttributes( "" );
353         assertNotNull( attrs );
354         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
355     }
356 
357 
358     @Test
359     public void testMD5() throws Exception
360     {
361         apply( service, getUserAddLdif() );
362         String userDn = "uid=akarasulu,ou=users,ou=system";
363         LdapContext ctx = new ServerLdapContext( service, 
364             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
365 
366         // Check that we can get the attributes
367         Attributes attrs = ctx.getAttributes( "" );
368         assertNotNull( attrs );
369         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
370 
371         // now modify the password for akarasulu : 'secret', encrypted using MD5
372         Attribute userPasswordAttribute = new BasicAttribute( "userPassword", "{MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ==" );
373         ctx.modifyAttributes( "", new ModificationItem[] {
374             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
375 
376         // close and try with old password (should fail)
377         ctx.close();
378 
379         try
380         {
381             ctx = new ServerLdapContext( service, 
382                 service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
383             fail( "Authentication with old password should fail" );
384         }
385         catch ( Exception e )
386         {
387             // we should fail
388         }
389         finally
390         {
391             if ( ctx != null )
392             {
393                 ctx.close();
394             }
395         }
396 
397         // try again now with new password (should be successfull)
398         ctx = new ServerLdapContext( service, 
399             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
400         attrs = ctx.getAttributes( "" );
401         assertNotNull( attrs );
402         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
403 
404         // try again now with new password, to check that the
405         // cache is updated (should be successfull)
406         ctx = new ServerLdapContext( service, 
407             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
408         attrs = ctx.getAttributes( "" );
409         assertNotNull( attrs );
410         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
411     }
412 
413 
414     @Test
415     public void testSMD5() throws Exception
416     {
417         apply( service, getUserAddLdif() );
418         String userDn = "uid=akarasulu,ou=users,ou=system";
419         LdapContext ctx = new ServerLdapContext( service, 
420             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
421 
422         // Check that we can get the attributes
423         Attributes attrs = ctx.getAttributes( "" );
424         assertNotNull( attrs );
425         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
426 
427         // now modify the password for akarasulu : 'secret', encrypted using SHA
428         Attribute userPasswordAttribute = new BasicAttribute( "userPassword", "{SMD5}tQ9wo/VBuKsqBtylMMCcORbnYOJFMyDJ" );
429         ctx.modifyAttributes( "", new ModificationItem[] {
430             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
431 
432         // close and try with old password (should fail)
433         ctx.close();
434 
435         try
436         {
437             ctx = new ServerLdapContext( service, 
438                 service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
439             fail( "Authentication with old password should fail" );
440         }
441         catch ( Exception e )
442         {
443             // we should fail
444         }
445         finally
446         {
447             if ( ctx != null )
448             {
449                 ctx.close();
450             }
451         }
452 
453         // try again now with new password (should be successfull)
454         ctx = new ServerLdapContext( service, 
455             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
456         attrs = ctx.getAttributes( "" );
457         assertNotNull( attrs );
458         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
459 
460         // try again now with new password, to check that the
461         // cache is updated (should be successfull)
462         ctx = new ServerLdapContext( service, 
463             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
464         attrs = ctx.getAttributes( "" );
465         assertNotNull( attrs );
466         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
467     }
468 
469 
470     @Test
471     public void testCRYPT() throws Exception
472     {
473         apply( service, getUserAddLdif() );
474         String userDn = "uid=akarasulu,ou=users,ou=system";
475         LdapContext ctx = new ServerLdapContext( service, 
476             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
477 
478         // Check that we can get the attributes
479         Attributes attrs = ctx.getAttributes( "" );
480         assertNotNull( attrs );
481         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
482 
483         // now modify the password for akarasulu : 'secret', encrypted using CRYPT
484         Attribute userPasswordAttribute = new BasicAttribute( "userPassword", "{crypt}qFkH8Z1woBlXw" );
485         ctx.modifyAttributes( "", new ModificationItem[] {
486             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
487 
488         // close and try with old password (should fail)
489         ctx.close();
490 
491         try
492         {
493             ctx = new ServerLdapContext( service, 
494                 service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
495             fail( "Authentication with old password should fail" );
496         }
497         catch ( Exception e )
498         {
499             // we should fail
500         }
501         finally
502         {
503             if ( ctx != null )
504             {
505                 ctx.close();
506             }
507         }
508 
509         // try again now with new password (should be successfull)
510         ctx = new ServerLdapContext( service, 
511             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
512         attrs = ctx.getAttributes( "" );
513         assertNotNull( attrs );
514         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
515 
516         // try again now with new password, to check that the
517         // cache is updated (should be successfull)
518         ctx = new ServerLdapContext( service, 
519             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) ); 
520         attrs = ctx.getAttributes( "" );
521         assertNotNull( attrs );
522         assertTrue( attrs.get( "uid" ).contains( "akarasulu" ) );
523     }
524 
525 
526     @Test
527     public void testInvalidateCredentialCacheForUpdatingAnotherUsersPassword() throws Exception
528     {
529         apply( service, getUserAddLdif() );
530 
531         // bind as akarasulu
532         String userDn = "uid=akarasulu,ou=users,ou=system";
533         LdapContext ctx = new ServerLdapContext( service, 
534             service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
535         ctx.close();
536 
537         // bind as admin
538         userDn = "uid=admin,ou=system";
539         ctx = new ServerLdapContext( service, 
540             service.getSession( new LdapDN( userDn ), "secret".getBytes() ), new LdapDN( userDn ) );
541 
542         // now modify the password for akarasulu (while we're admin)
543         Attribute userPasswordAttribute = new BasicAttribute( "userPassword", "newpwd" );
544         ctx.modifyAttributes( "", new ModificationItem[] {
545             new ModificationItem( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute ) } );
546         ctx.close();
547 
548         try
549         {
550             ctx = new ServerLdapContext( service, 
551                 service.getSession( new LdapDN( userDn ), "test".getBytes() ), new LdapDN( userDn ) );
552             fail( "Authentication with old password should fail" );
553         }
554         catch ( Exception e )
555         {
556             // we should fail
557         }
558         finally
559         {
560             if ( ctx != null )
561             {
562                 ctx.close();
563             }
564         }
565     }
566 }