1818
1919package org .apache .hadoop .security ;
2020
21+ import java .util .ArrayList ;
2122import java .util .Arrays ;
2223import java .util .List ;
24+ import java .util .Set ;
2325
2426import javax .naming .NamingEnumeration ;
2527import javax .naming .NamingException ;
2628import javax .naming .directory .Attribute ;
29+ import javax .naming .directory .DirContext ;
2730import javax .naming .directory .SearchControls ;
2831import javax .naming .directory .SearchResult ;
2932
3033import org .apache .hadoop .conf .Configuration ;
3134import org .junit .Assert ;
32- import org .junit .Before ;
3335import org .junit .Test ;
36+ import org .mockito .stubbing .Stubber ;
3437
3538import static org .mockito .ArgumentMatchers .any ;
3639import static org .mockito .ArgumentMatchers .anyString ;
4952public class TestLdapGroupsMappingWithOneQuery
5053 extends TestLdapGroupsMappingBase {
5154
52- @ Before
53- public void setupMocks () throws NamingException {
55+ public void setupMocks (List <String > listOfDNs ) throws NamingException {
5456 Attribute groupDN = mock (Attribute .class );
5557
5658 NamingEnumeration <SearchResult > groupNames = getGroupNames ();
5759 doReturn (groupNames ).when (groupDN ).getAll ();
58- String groupName1 = "CN=abc,DC=foo,DC=bar,DC=com" ;
59- String groupName2 = "CN=xyz,DC=foo,DC=bar,DC=com" ;
60- String groupName3 = "CN=sss,CN=foo,DC=bar,DC=com" ;
61- doReturn (groupName1 ).doReturn (groupName2 ).doReturn (groupName3 ).
62- when (groupNames ).next ();
63- when (groupNames .hasMore ()).thenReturn (true ).thenReturn (true ).
64- thenReturn (true ).thenReturn (false );
60+ buildListOfGroupDNs (listOfDNs ).when (groupNames ).next ();
61+ when (groupNames .hasMore ()).
62+ thenReturn (true ).thenReturn (true ).
63+ thenReturn (true ).thenReturn (false );
6564
6665 when (getAttributes ().get (eq ("memberOf" ))).thenReturn (groupDN );
6766 }
6867
68+ /**
69+ * Build and return a list of individually added group DNs such
70+ * that calls to .next() will result in a single value each time.
71+ *
72+ * @param listOfDNs
73+ * @return the stubber to use for the .when().next() call
74+ */
75+ private Stubber buildListOfGroupDNs (List <String > listOfDNs ) {
76+ Stubber stubber = null ;
77+ for (String s : listOfDNs ) {
78+ if (stubber != null ) {
79+ stubber .doReturn (s );
80+ } else {
81+ stubber = doReturn (s );
82+ }
83+ }
84+ return stubber ;
85+ }
86+
6987 @ Test
7088 public void testGetGroups () throws NamingException {
7189 // given a user whose ldap query returns a user object with three "memberOf"
7290 // properties, return an array of strings representing its groups.
7391 String [] testGroups = new String [] {"abc" , "xyz" , "sss" };
7492 doTestGetGroups (Arrays .asList (testGroups ));
93+
94+ // test fallback triggered by NamingException
95+ doTestGetGroupsWithFallback ();
7596 }
7697
7798 private void doTestGetGroups (List <String > expectedGroups )
7899 throws NamingException {
100+ List <String > groupDns = new ArrayList <>();
101+ groupDns .add ("CN=abc,DC=foo,DC=bar,DC=com" );
102+ groupDns .add ("CN=xyz,DC=foo,DC=bar,DC=com" );
103+ groupDns .add ("CN=sss,DC=foo,DC=bar,DC=com" );
104+
105+ setupMocks (groupDns );
79106 String ldapUrl = "ldap://test" ;
80107 Configuration conf = getBaseConf (ldapUrl );
81108 // enable single-query lookup
82109 conf .set (LdapGroupsMapping .MEMBEROF_ATTR_KEY , "memberOf" );
83110
84- LdapGroupsMapping groupsMapping = getGroupsMapping ();
111+ TestLdapGroupsMapping groupsMapping = new TestLdapGroupsMapping ();
85112 groupsMapping .setConf (conf );
86113 // Username is arbitrary, since the spy is mocked to respond the same,
87114 // regardless of input
88115 List <String > groups = groupsMapping .getGroups ("some_user" );
89116
90117 Assert .assertEquals (expectedGroups , groups );
118+ Assert .assertFalse ("Second LDAP query should NOT have been called." ,
119+ groupsMapping .isSecondaryQueryCalled ());
91120
92121 // We should have only made one query because single-query lookup is enabled
93122 verify (getContext (), times (1 )).search (anyString (), anyString (),
94123 any (Object [].class ), any (SearchControls .class ));
95124 }
96- }
125+
126+ private void doTestGetGroupsWithFallback ()
127+ throws NamingException {
128+ List <String > groupDns = new ArrayList <>();
129+ groupDns .add ("CN=abc,DC=foo,DC=bar,DC=com" );
130+ groupDns .add ("CN=xyz,DC=foo,DC=bar,DC=com" );
131+ groupDns .add ("ipaUniqueID=e4a9a634-bb24-11ec-aec1-06ede52b5fe1," +
132+ "CN=sudo,DC=foo,DC=bar,DC=com" );
133+ setupMocks (groupDns );
134+ String ldapUrl = "ldap://test" ;
135+ Configuration conf = getBaseConf (ldapUrl );
136+ // enable single-query lookup
137+ conf .set (LdapGroupsMapping .MEMBEROF_ATTR_KEY , "memberOf" );
138+ conf .set (LdapGroupsMapping .LDAP_NUM_ATTEMPTS_KEY , "1" );
139+
140+ TestLdapGroupsMapping groupsMapping = new TestLdapGroupsMapping ();
141+ groupsMapping .setConf (conf );
142+ // Username is arbitrary, since the spy is mocked to respond the same,
143+ // regardless of input
144+ List <String > groups = groupsMapping .getGroups ("some_user" );
145+
146+ // expected to be empty due to invalid memberOf
147+ Assert .assertEquals (0 , groups .size ());
148+
149+ // expect secondary query to be called: getGroups()
150+ Assert .assertTrue ("Second LDAP query should have been called." ,
151+ groupsMapping .isSecondaryQueryCalled ());
152+
153+ // We should have fallen back to the second query because first threw
154+ // NamingException expected count is 3 since testGetGroups calls
155+ // doTestGetGroups and doTestGetGroupsWithFallback in succession and
156+ // the count is across both test scenarios.
157+ verify (getContext (), times (3 )).search (anyString (), anyString (),
158+ any (Object [].class ), any (SearchControls .class ));
159+ }
160+
161+ private static final class TestLdapGroupsMapping extends LdapGroupsMapping {
162+ private boolean secondaryQueryCalled = false ;
163+ public boolean isSecondaryQueryCalled () {
164+ return secondaryQueryCalled ;
165+ }
166+ Set <String > lookupGroup (SearchResult result , DirContext c ,
167+ int goUpHierarchy ) throws NamingException {
168+ secondaryQueryCalled = true ;
169+ return super .lookupGroup (result , c , goUpHierarchy );
170+ }
171+ }
172+ }
0 commit comments