1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.authz;
18
19
20 import org.apache.ldap.common.message.LockableAttributesImpl;
21 import org.apache.ldap.common.message.LockableAttributeImpl;
22 import org.apache.ldap.common.name.LdapName;
23 import org.apache.ldap.common.exception.LdapNameNotFoundException;
24 import org.apache.ldap.common.exception.LdapNoPermissionException;
25
26 import javax.naming.NamingException;
27 import javax.naming.Name;
28 import javax.naming.NamingEnumeration;
29 import javax.naming.directory.*;
30 import java.util.Map;
31 import java.util.HashMap;
32 import java.util.Iterator;
33
34
35 /***
36 * Tests whether or not authorization around search, list and lookup operations
37 * work properly.
38 *
39 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
40 * @version $Rev$
41 */
42 public class SearchAuthorizationTest extends AbstractAuthorizationTest
43 {
44 /***
45 * The search results of tests are added to this map via put (<String, SearchResult>)
46 * the map is also cleared before each search test. This allows further inspections
47 * of the results for more specific test cases.
48 */
49 private Map results = new HashMap();
50
51 /***
52 * Generates a set of simple organizationalUnit entries where the
53 * ou of the entry returned is the index of the entry in the array.
54 *
55 * @param count the number of entries to produce
56 * @return an array of entries with length = count
57 */
58 private Attributes[] getTestNodes( final int count )
59 {
60 Attributes[] attributes = new Attributes[count];
61 for ( int ii = 0; ii < count; ii++ )
62 {
63 attributes[ii] = new LockableAttributesImpl();
64 Attribute oc = new LockableAttributeImpl( "objectClass" );
65 oc.add( "top" );
66 oc.add( "organizationalUnit" );
67 attributes[ii].put( oc );
68 Attribute ou = new LockableAttributeImpl( "ou" );
69 ou.add( String.valueOf( ii ) );
70 ou.add( "testEntry" );
71 attributes[ii].put( ou );
72 attributes[ii].put( "telephoneNumber", String.valueOf( count ) );
73 }
74
75 return attributes;
76 }
77
78
79 private void recursivelyAddSearchData( Name parent, Attributes[] children, final int sizeLimit, int[] count )
80 throws NamingException
81 {
82 Name[] childRdns = new Name[children.length];
83 for ( int ii = 0; ii < children.length && count[0] < sizeLimit; ii++ )
84 {
85 Name childRdn = new LdapName();
86 childRdn.addAll( parent );
87 childRdn.add( "ou=" + ii );
88 childRdns[ii] = childRdn;
89 sysRoot.createSubcontext( childRdn, children[ii] );
90 count[0]++;
91 }
92
93 if ( count[0] >= sizeLimit )
94 {
95 return;
96 }
97
98 for ( int ii = 0; ii < children.length && count[0] < sizeLimit; ii++ )
99 {
100 recursivelyAddSearchData( childRdns[ii], children, sizeLimit, count );
101 }
102 }
103
104
105 /***
106 * Starts creating nodes under a parent with a set number of children. First
107 * a single node is created under the parent. Thereafter a number of children
108 * determined by the branchingFactor is added. Until a sizeLimit is reached
109 * descendants are created this way in a breath first recursive descent.
110 *
111 * @param parent the parent under which the first node is created
112 * @param branchingFactor
113 * @param sizelimit
114 * @return the immediate child node created under parent which contains the subtree
115 * @throws NamingException
116 */
117 private Name addSearchData( Name parent, int branchingFactor, int sizelimit ) throws NamingException
118 {
119 parent = ( Name ) parent.clone();
120 parent.add( "ou=tests" );
121 sysRoot.createSubcontext( parent, getTestNodes(1)[0] );
122 recursivelyAddSearchData( parent, getTestNodes( branchingFactor ), sizelimit, new int[] { 1 } );
123 return parent;
124 }
125
126
127 /***
128 * Recursively deletes all entries including the base specified.
129 *
130 * @param rdn the relative dn from ou=system of the entry to delete recursively
131 * @throws NamingException if there are problems deleting entries
132 */
133 private void recursivelyDelete( Name rdn ) throws NamingException
134 {
135 NamingEnumeration results = sysRoot.search( rdn, "(objectClass=*)", new SearchControls() );
136 while ( results.hasMore() )
137 {
138 SearchResult result = ( SearchResult ) results.next();
139 Name childRdn = new LdapName( result.getName() );
140 childRdn.remove( 0 );
141 recursivelyDelete( childRdn );
142 }
143 sysRoot.destroySubcontext( rdn );
144 }
145
146
147 /***
148 * Performs a single level search as a specific user on newly created data and checks
149 * that result set count is 3. The basic (objectClass=*) filter is used.
150 *
151 * @param uid the uid RDN attribute value for the user under ou=users,ou=system
152 * @param password the password of the user
153 * @return true if the search succeeds as expected, false otherwise
154 * @throws NamingException if there are problems conducting the search
155 */
156 private boolean checkCanSearchAs( String uid, String password ) throws NamingException
157 {
158 return checkCanSearchAs( uid, password, "(objectClass=*)", null, 3 );
159 }
160
161
162 /***
163 * Performs a single level search as a specific user on newly created data and checks
164 * that result set count is equal to a user specified amount. The basic
165 * (objectClass=*) filter is used.
166 *
167 * @param uid the uid RDN attribute value for the user under ou=users,ou=system
168 * @param password the password of the user
169 * @param resultSetSz the expected size of the results
170 * @return true if the search succeeds as expected, false otherwise
171 * @throws NamingException if there are problems conducting the search
172 */
173 private boolean checkCanSearchAs( String uid, String password, int resultSetSz ) throws NamingException
174 {
175 return checkCanSearchAs( uid, password, "(objectClass=*)", null, resultSetSz );
176 }
177
178
179 /***
180 * Performs a search as a specific user on newly created data and checks
181 * that result set count is equal to a user specified amount. The basic
182 * (objectClass=*) filter is used.
183 *
184 * @param uid the uid RDN attribute value for the user under ou=users,ou=system
185 * @param password the password of the user
186 * @param resultSetSz the expected size of the results
187 * @return true if the search succeeds as expected, false otherwise
188 * @throws NamingException if there are problems conducting the search
189 */
190 private boolean checkCanSearchAs( String uid, String password, SearchControls cons, int resultSetSz )
191 throws NamingException
192 {
193 return checkCanSearchAs( uid, password, "(objectClass=*)", cons, resultSetSz );
194 }
195
196
197 /***
198 * Performs a search as a specific user on newly created data and checks
199 * that result set count is equal to a user specified amount.
200 *
201 * @param uid the uid RDN attribute value for the user under ou=users,ou=system
202 * @param password the password of the user
203 * @param filter the search filter to use
204 * @param resultSetSz the expected size of the results
205 * @return true if the search succeeds as expected, false otherwise
206 * @throws NamingException if there are problems conducting the search
207 */
208 private boolean checkCanSearchAs( String uid, String password, String filter,
209 SearchControls cons, int resultSetSz ) throws NamingException
210 {
211 if ( cons == null )
212 {
213 cons = new SearchControls();
214 }
215
216 Name base = addSearchData( new LdapName(), 3, 10 );
217 Name userDn = new LdapName( "uid="+uid+",ou=users,ou=system" );
218 try
219 {
220 results.clear();
221 DirContext userCtx = getContextAs( userDn, password );
222 NamingEnumeration list = userCtx.search( base, filter, cons );
223 int counter = 0;
224 while ( list.hasMore() )
225 {
226 SearchResult result = ( SearchResult ) list.next();
227 results.put( result.getName(), result );
228 counter++;
229 }
230 return counter == resultSetSz;
231 }
232 catch ( LdapNoPermissionException e )
233 {
234 return false;
235 }
236 finally
237 {
238 recursivelyDelete( base );
239 }
240 }
241
242
243 /***
244 * Adds an entryACI to specified entry below ou=system and runs a search. Then it
245 * checks to see the result size is correct.
246 *
247 * @param uid the uid RDN attribute value for the user under ou=users,ou=system
248 * @param password the password of the user
249 * @return true if the search succeeds as expected, false otherwise
250 * @throws NamingException if there are problems conducting the search
251 */
252 private boolean checkSearchAsWithEntryACI( String uid, String password, SearchControls cons, Name rdn,
253 String aci, int resultSetSz )
254 throws NamingException
255 {
256 if ( cons == null )
257 {
258 cons = new SearchControls();
259 }
260
261 Name base = addSearchData( new LdapName(), 3, 10 );
262 addEntryACI( rdn, aci );
263 Name userDn = new LdapName( "uid="+uid+",ou=users,ou=system" );
264 try
265 {
266 results.clear();
267 DirContext userCtx = getContextAs( userDn, password );
268 NamingEnumeration list = userCtx.search( base, "(objectClass=*)", cons );
269 int counter = 0;
270 while ( list.hasMore() )
271 {
272 SearchResult result = ( SearchResult ) list.next();
273 results.put( result.getName(), result );
274 counter++;
275 }
276 return counter == resultSetSz;
277 }
278 catch ( LdapNoPermissionException e )
279 {
280 return false;
281 }
282 finally
283 {
284 recursivelyDelete( base );
285 }
286 }
287
288
289 /***
290 * Checks to see that the addSearchData() and the recursiveDelete()
291 * functions in this test work properly.
292 *
293 * @throws NamingException if there is a problem with the implementation of
294 * these utility functions
295 */
296 public void testAddSearchData() throws NamingException
297 {
298 Name base = addSearchData( new LdapName(), 3, 10 );
299 SearchControls controls = new SearchControls();
300 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
301 NamingEnumeration results = sysRoot.search( base, "(objectClass=*)", controls );
302 int counter = 0;
303 while ( results.hasMore() )
304 {
305 results.next();
306 counter++;
307 }
308
309 assertEquals( 10, counter );
310 recursivelyDelete( base );
311 try { sysRoot.lookup( base ); fail(); } catch ( LdapNameNotFoundException e ) {}
312 }
313
314
315
316
317
318
319
320 /***
321 * Checks to make sure group membership based userClass works for add operations.
322 *
323 * @throws javax.naming.NamingException if the test encounters an error
324 */
325 public void testGrantAdministrators() throws NamingException
326 {
327
328 createUser( "billyd", "billyd" );
329
330
331 assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
332
333
334
335 createAccessControlSubentry( "searchAdmin", "{ " +
336 "identificationTag \"searchAci\", " +
337 "precedence 14, " +
338 "authenticationLevel none, " +
339 "itemOrUserFirst userFirst: { " +
340 "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
341 "userPermissions { { " +
342 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
343 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
344
345
346
347 assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
348
349
350 addUserToGroup( "billyd", "Administrators" );
351
352
353 assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
354 }
355
356
357 /***
358 * Checks to make sure name based userClass works for search operations.
359 *
360 * @throws javax.naming.NamingException if the test encounters an error
361 */
362 public void testGrantSearchByName() throws NamingException
363 {
364
365 createUser( "billyd", "billyd" );
366
367
368 assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
369
370
371 createAccessControlSubentry( "billydSearch", "{ " +
372 "identificationTag \"searchAci\", " +
373 "precedence 14, " +
374 "authenticationLevel none, " +
375 "itemOrUserFirst userFirst: { " +
376 "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
377 "userPermissions { { " +
378 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
379 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
380
381
382 assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
383 }
384
385
386 /***
387 * Checks to make sure subtree based userClass works for search operations.
388 *
389 * @throws javax.naming.NamingException if the test encounters an error
390 */
391 public void testGrantSearchBySubtree() throws NamingException
392 {
393
394 createUser( "billyd", "billyd" );
395
396
397 assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
398
399
400 createAccessControlSubentry( "billySearchBySubtree", "{ " +
401 "identificationTag \"searchAci\", " +
402 "precedence 14, " +
403 "authenticationLevel none, " +
404 "itemOrUserFirst userFirst: { " +
405 "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
406 "userPermissions { { " +
407 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
408 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
409
410
411 assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
412 }
413
414
415 /***
416 * Checks to make sure <b>allUsers</b> userClass works for search operations.
417 *
418 * @throws javax.naming.NamingException if the test encounters an error
419 */
420 public void testGrantSearchAllUsers() throws NamingException
421 {
422
423 createUser( "billyd", "billyd" );
424
425
426 assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
427
428
429 createAccessControlSubentry( "anybodySearch", "{ " +
430 "identificationTag \"searchAci\", " +
431 "precedence 14, " +
432 "authenticationLevel none, " +
433 "itemOrUserFirst userFirst: { " +
434 "userClasses { allUsers }, " +
435 "userPermissions { { " +
436 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
437 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
438
439
440
441 assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
442 }
443
444
445
446
447
448
449
450 /***
451 * Checks to make sure search does not return entries not assigned the
452 * perscriptiveACI and that it does not fail with an exception.
453 *
454 * @throws javax.naming.NamingException if the test encounters an error
455 */
456 public void testSelectiveGrantsAllUsers() throws NamingException
457 {
458
459 createUser( "billyd", "billyd" );
460
461
462 SearchControls cons = new SearchControls();
463 cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
464 assertFalse( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
465
466
467
468 createAccessControlSubentry( "anybodySearch",
469 "{ maximum 2 }",
470 "{ " +
471 "identificationTag \"searchAci\", " +
472 "precedence 14, " +
473 "authenticationLevel none, " +
474 "itemOrUserFirst userFirst: { " +
475 "userClasses { allUsers }, " +
476 "userPermissions { { " +
477 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
478 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
479
480
481
482 assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
483 }
484
485
486 /***
487 * Checks to make sure attributeTypes are not present when permissions are
488 * not given for reading them and their values.
489 *
490 * @throws javax.naming.NamingException if the test encounters an error
491 */
492 public void testHidingAttributes() throws NamingException
493 {
494
495 createUser( "billyd", "billyd" );
496
497
498 SearchControls cons = new SearchControls();
499 cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
500 assertFalse( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
501
502
503
504
505 createAccessControlSubentry( "excluseTelephoneNumber",
506 "{ maximum 2 }",
507 "{ " +
508 "identificationTag \"searchAci\", " +
509 "precedence 14, " +
510 "authenticationLevel none, " +
511 "itemOrUserFirst userFirst: { " +
512 "userClasses { allUsers }, " +
513 "userPermissions { { " +
514 "protectedItems {entry, allAttributeValues { ou, objectClass } }, " +
515 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
516
517
518 assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
519
520
521 Iterator list = results.values().iterator();
522 while ( list.hasNext() )
523 {
524 SearchResult result = ( SearchResult ) list.next();
525 assertNull( result.getAttributes().get( "telephoneNumber" ) );
526 }
527
528
529 deleteAccessControlSubentry( "excluseTelephoneNumber" );
530
531
532
533
534 createAccessControlSubentry( "includeAllAttributeTypesAndValues",
535 "{ maximum 2 }",
536 "{ " +
537 "identificationTag \"searchAci\", " +
538 "precedence 14, " +
539 "authenticationLevel none, " +
540 "itemOrUserFirst userFirst: { " +
541 "userClasses { allUsers }, " +
542 "userPermissions { { " +
543 "protectedItems {entry, allUserAttributeTypesAndValues }, " +
544 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
545
546
547 assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 4 ) );
548
549
550 list = results.values().iterator();
551 while ( list.hasNext() )
552 {
553 SearchResult result = ( SearchResult ) list.next();
554 assertNotNull( result.getAttributes().get( "telephoneNumber" ) );
555 }
556 }
557
558
559 /***
560 * Checks to make sure specific attribute values are not present when
561 * read permission is denied.
562 *
563 * @throws javax.naming.NamingException if the test encounters an error
564 */
565 public void testHidingAttributeValues() throws NamingException
566 {
567
568 createUser( "billyd", "billyd" );
569
570
571 assertFalse( checkCanSearchAs( "billyd", "billyd", 3 ) );
572
573
574
575
576 createAccessControlSubentry( "excluseOUValue",
577 "{ maximum 2 }",
578 "{ " +
579 "identificationTag \"searchAci\", " +
580 "precedence 14, " +
581 "authenticationLevel none, " +
582 "itemOrUserFirst userFirst: { " +
583 "userClasses { allUsers }, " +
584 "userPermissions { { " +
585 "protectedItems {entry, attributeType { ou }, allAttributeValues { objectClass }, attributeValue { ou=0, ou=1, ou=2 } }, " +
586 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
587
588
589 assertTrue( checkCanSearchAs( "billyd", "billyd", 3 ) );
590
591
592 Iterator list = results.values().iterator();
593 while ( list.hasNext() )
594 {
595 SearchResult result = ( SearchResult ) list.next();
596 assertFalse( result.getAttributes().get( "ou" ).contains( "testEntry" ) );
597 }
598
599
600 deleteAccessControlSubentry( "excluseOUValue" );
601
602
603
604
605 createAccessControlSubentry( "includeAllAttributeTypesAndValues",
606 "{ maximum 2 }",
607 "{ " +
608 "identificationTag \"searchAci\", " +
609 "precedence 14, " +
610 "authenticationLevel none, " +
611 "itemOrUserFirst userFirst: { " +
612 "userClasses { allUsers }, " +
613 "userPermissions { { " +
614 "protectedItems {entry, allUserAttributeTypesAndValues }, " +
615 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
616
617
618 assertTrue( checkCanSearchAs( "billyd", "billyd", 3 ) );
619
620
621 list = results.values().iterator();
622 while ( list.hasNext() )
623 {
624 SearchResult result = ( SearchResult ) list.next();
625 assertTrue( result.getAttributes().get( "ou" ).contains( "testEntry" ) );
626 }
627 }
628
629
630 /***
631 * Adds a perscriptiveACI to allow search, tests for success, then adds entryACI
632 * to deny read, browse and returnDN to a specific entry and checks to make sure
633 * that entry cannot be accessed via search as a specific user.
634 *
635 * @throws NamingException if the test is broken
636 */
637 public void testPerscriptiveGrantWithEntryDenial() throws NamingException
638 {
639
640 createUser( "billyd", "billyd" );
641
642
643 String aci = "{ " +
644 "identificationTag \"denyAci\", " +
645 "precedence 14, " +
646 "authenticationLevel none, " +
647 "itemOrUserFirst userFirst: { " +
648 "userClasses { allUsers }, " +
649 "userPermissions { { " +
650 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
651 "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }";
652
653
654 SearchControls cons = new SearchControls();
655 cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
656 LdapName rdn = new LdapName( "ou=tests" );
657 assertFalse( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
658
659
660 createAccessControlSubentry( "anybodySearch", "{ " +
661 "identificationTag \"searchAci\", " +
662 "precedence 14, " +
663 "authenticationLevel none, " +
664 "itemOrUserFirst userFirst: { " +
665 "userClasses { allUsers }, " +
666 "userPermissions { { " +
667 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
668 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
669
670
671
672
673 assertTrue( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
674 assertNull( results.get( "ou=tests,ou=system" ) );
675
676
677 assertTrue( checkCanSearchAs( "billyd", "billyd", cons, 10 ) );
678 assertNotNull( results.get( "ou=tests,ou=system" ) );
679 }
680
681
682 /***
683 * Adds a perscriptiveACI to allow search, tests for success, then adds entryACI
684 * to deny read, browse and returnDN to a specific entry and checks to make sure
685 * that entry cannot be accessed via search as a specific user. Here the
686 * precidence of the ACI is put to the test.
687 *
688 * @throws NamingException if the test is broken
689 */
690 public void testPerscriptiveGrantWithEntryDenialWithPrecidence() throws NamingException
691 {
692
693 createUser( "billyd", "billyd" );
694
695
696 String aci = "{ " +
697 "identificationTag \"denyAci\", " +
698 "precedence 14, " +
699 "authenticationLevel none, " +
700 "itemOrUserFirst userFirst: { " +
701 "userClasses { allUsers }, " +
702 "userPermissions { { " +
703 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
704 "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }";
705
706
707 SearchControls cons = new SearchControls();
708 cons.setSearchScope( SearchControls.SUBTREE_SCOPE );
709 LdapName rdn = new LdapName( "ou=tests" );
710 assertFalse( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
711
712
713 createAccessControlSubentry( "anybodySearch", "{ " +
714 "identificationTag \"searchAci\", " +
715 "precedence 15, " +
716 "authenticationLevel none, " +
717 "itemOrUserFirst userFirst: { " +
718 "userClasses { allUsers }, " +
719 "userPermissions { { " +
720 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
721 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
722
723
724
725
726
727
728 assertTrue( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 10 ) );
729 assertNotNull( results.get( "ou=tests,ou=system" ) );
730
731
732
733 aci = "{ " +
734 "identificationTag \"denyAci\", " +
735 "precedence 16, " +
736 "authenticationLevel none, " +
737 "itemOrUserFirst userFirst: { " +
738 "userClasses { allUsers }, " +
739 "userPermissions { { " +
740 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
741 "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }";
742
743
744
745
746
747
748 assertTrue( checkSearchAsWithEntryACI( "billyd", "billyd", cons, rdn, aci, 9 ) );
749 assertNull( results.get( "ou=tests,ou=system" ) );
750 }
751
752
753 /***
754 * Performs an object level search on the specified subentry relative to ou=system as a specific user.
755 *
756 * @param uid the uid RDN attribute value of the user to perform the search as
757 * @param password the password of the user
758 * @param rdn the relative name to the subentry under the ou=system AP
759 * @return the single search result if access is allowed or null
760 * @throws NamingException if the search fails w/ exception other than no permission
761 */
762 private SearchResult checkCanSearhSubentryAs( String uid, String password, Name rdn ) throws NamingException
763 {
764 DirContext userCtx = getContextAs( new LdapName( "uid="+uid+",ou=users,ou=system" ), password );
765 SearchControls cons = new SearchControls();
766 cons.setSearchScope( SearchControls.OBJECT_SCOPE );
767 SearchResult result = null;
768 NamingEnumeration list = null;
769
770 try
771 {
772 list = userCtx.search( rdn, "(objectClass=*)", cons );
773 if ( list.hasMore() )
774 {
775 result = ( SearchResult ) list.next();
776 list.close();
777 return result;
778 }
779 }
780 catch ( LdapNoPermissionException e )
781 {
782 }
783 finally
784 {
785 if ( list != null ) { list.close(); }
786 }
787
788 return result;
789 }
790
791
792 public void testSubentryAccess() throws NamingException
793 {
794
795 createUser( "billyd", "billyd" );
796
797
798 createAccessControlSubentry( "anybodySearch", "{ " +
799 "identificationTag \"searchAci\", " +
800 "precedence 14, " +
801 "authenticationLevel none, " +
802 "itemOrUserFirst userFirst: { " +
803 "userClasses { allUsers }, " +
804 "userPermissions { { " +
805 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
806 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
807
808
809 assertNotNull( checkCanSearhSubentryAs( "billyd", "billyd", new LdapName( "cn=anybodySearch" ) ) );
810
811
812 addSubentryACI( "{ " +
813 "identificationTag \"searchAci\", " +
814 "precedence 14, " +
815 "authenticationLevel none, " +
816 "itemOrUserFirst userFirst: { " +
817 "userClasses { allUsers }, " +
818 "userPermissions { { " +
819 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
820 "grantsAndDenials { denyRead, denyReturnDN, denyBrowse } } } } }" );
821
822
823 assertNull( checkCanSearhSubentryAs( "billyd", "billyd", new LdapName( "cn=anybodySearch" ) ) );
824 }
825
826
827 public void testGetMatchedName() throws NamingException
828 {
829
830 createUser( "billyd", "billyd" );
831
832
833
834 createAccessControlSubentry( "selectiveDiscloseOnError",
835 "{ specificExclusions { chopBefore:\"ou=groups\" } }",
836 "{ " +
837 "identificationTag \"searchAci\", " +
838 "precedence 14, " +
839 "authenticationLevel none, " +
840 "itemOrUserFirst userFirst: { " +
841 "userClasses { allUsers }, " +
842 "userPermissions { { " +
843 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
844 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError } } } } }" );
845
846
847 DirContext userCtx = getContextAs( new LdapName( "uid=billyd,ou=users,ou=system" ), "billyd" );
848 try
849 {
850 userCtx.lookup( "cn=blah,ou=groups" );
851 }
852 catch( NamingException e )
853 {
854 Name matched = e.getResolvedName();
855
856
857 assertEquals( matched.toString(), "ou=system" );
858 }
859
860
861 deleteAccessControlSubentry( "selectiveDiscloseOnError" );
862 createAccessControlSubentry( "selectiveDiscloseOnError",
863 "{ " +
864 "identificationTag \"searchAci\", " +
865 "precedence 14, " +
866 "authenticationLevel none, " +
867 "itemOrUserFirst userFirst: { " +
868 "userClasses { allUsers }, " +
869 "userPermissions { { " +
870 "protectedItems {entry, allUserAttributeTypesAndValues}, " +
871 "grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError } } } } }" );
872
873
874 try
875 {
876 userCtx.lookup( "cn=blah,ou=groups" );
877 }
878 catch( NamingException e )
879 {
880 Name matched = e.getResolvedName();
881
882
883 assertEquals( matched.toString(), "ou=groups,ou=system" );
884 }
885 }
886 }