1818
1919package org .apache .hadoop .security ;
2020
21+ import java .util .ArrayList ;
2122import java .util .Arrays ;
2223import java .util .List ;
2324
2425import javax .naming .NamingEnumeration ;
2526import javax .naming .NamingException ;
2627import javax .naming .directory .Attribute ;
28+ import javax .naming .directory .DirContext ;
2729import javax .naming .directory .SearchControls ;
2830import javax .naming .directory .SearchResult ;
2931
3032import org .apache .hadoop .conf .Configuration ;
3133import org .junit .Assert ;
32- import org .junit .Before ;
3334import org .junit .Test ;
35+ import org .mockito .stubbing .Stubber ;
3436
3537import static org .mockito .ArgumentMatchers .any ;
3638import static org .mockito .ArgumentMatchers .anyString ;
4951public class TestLdapGroupsMappingWithOneQuery
5052 extends TestLdapGroupsMappingBase {
5153
52- @ Before
53- public void setupMocks () throws NamingException {
54+ public void setupMocks (List <String > listOfDNs ) throws NamingException {
5455 Attribute groupDN = mock (Attribute .class );
5556
5657 NamingEnumeration <SearchResult > groupNames = getGroupNames ();
5758 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 );
59+ buildListOfGroupDNs (listOfDNs ).when (groupNames ).next ();
60+ when (groupNames .hasMore ()).
61+ thenReturn (true ).thenReturn (true ).
62+ thenReturn (true ).thenReturn (false );
6563
6664 when (getAttributes ().get (eq ("memberOf" ))).thenReturn (groupDN );
6765 }
6866
67+ /**
68+ * Build and return a list of individually added group DNs such
69+ * that calls to .next() will result in a single value each time.
70+ *
71+ * @param listOfDNs
72+ * @return the stubber to use for the .when().next() call
73+ */
74+ private Stubber buildListOfGroupDNs (List <String > listOfDNs ) {
75+ Stubber stubber = null ;
76+ for (String s : listOfDNs ) {
77+ if (stubber != null ) {
78+ stubber .doReturn (s );
79+ } else {
80+ stubber = doReturn (s );
81+ }
82+ }
83+ return stubber ;
84+ }
85+
6986 @ Test
7087 public void testGetGroups () throws NamingException {
7188 // given a user whose ldap query returns a user object with three "memberOf"
7289 // properties, return an array of strings representing its groups.
7390 String [] testGroups = new String [] {"abc" , "xyz" , "sss" };
7491 doTestGetGroups (Arrays .asList (testGroups ));
92+
93+ // test fallback triggered by NamingException
94+ doTestGetGroupsWithFallback ();
7595 }
7696
7797 private void doTestGetGroups (List <String > expectedGroups )
7898 throws NamingException {
99+ List <String > groupDns = new ArrayList <>();
100+ groupDns .add ("CN=abc,DC=foo,DC=bar,DC=com" );
101+ groupDns .add ("CN=xyz,DC=foo,DC=bar,DC=com" );
102+ groupDns .add ("CN=sss,DC=foo,DC=bar,DC=com" );
103+
104+ setupMocks (groupDns );
79105 String ldapUrl = "ldap://test" ;
80106 Configuration conf = getBaseConf (ldapUrl );
81107 // enable single-query lookup
82108 conf .set (LdapGroupsMapping .MEMBEROF_ATTR_KEY , "memberOf" );
83109
84- LdapGroupsMapping groupsMapping = getGroupsMapping ();
110+ TestLdapGroupsMapping groupsMapping = new TestLdapGroupsMapping ();
85111 groupsMapping .setConf (conf );
86112 // Username is arbitrary, since the spy is mocked to respond the same,
87113 // regardless of input
88114 List <String > groups = groupsMapping .getGroups ("some_user" );
89115
90116 Assert .assertEquals (expectedGroups , groups );
117+ Assert .assertFalse ("Second LDAP query should NOT have been called." ,
118+ groupsMapping .isSecondaryQueryCalled ());
91119
92120 // We should have only made one query because single-query lookup is enabled
93121 verify (getContext (), times (1 )).search (anyString (), anyString (),
94122 any (Object [].class ), any (SearchControls .class ));
95123 }
96- }
124+
125+ private void doTestGetGroupsWithFallback ()
126+ throws NamingException {
127+ List <String > groupDns = new ArrayList <>();
128+ groupDns .add ("CN=abc,DC=foo,DC=bar,DC=com" );
129+ groupDns .add ("CN=xyz,DC=foo,DC=bar,DC=com" );
130+ groupDns .add ("ipaUniqueID=e4a9a634-bb24-11ec-aec1-06ede52b5fe1," +
131+ "CN=sudo,DC=foo,DC=bar,DC=com" );
132+ setupMocks (groupDns );
133+ String ldapUrl = "ldap://test" ;
134+ Configuration conf = getBaseConf (ldapUrl );
135+ // enable single-query lookup
136+ conf .set (LdapGroupsMapping .MEMBEROF_ATTR_KEY , "memberOf" );
137+ conf .set (LdapGroupsMapping .LDAP_NUM_ATTEMPTS_KEY , "1" );
138+
139+ TestLdapGroupsMapping groupsMapping = new TestLdapGroupsMapping ();
140+ groupsMapping .setConf (conf );
141+ // Username is arbitrary, since the spy is mocked to respond the same,
142+ // regardless of input
143+ List <String > groups = groupsMapping .getGroups ("some_user" );
144+
145+ // expected to be empty due to invalid memberOf
146+ Assert .assertEquals (0 , groups .size ());
147+
148+ // expect secondary query to be called: getGroups()
149+ Assert .assertTrue ("Second LDAP query should have been called." ,
150+ groupsMapping .isSecondaryQueryCalled ());
151+
152+ // We should have fallen back to the second query because first threw
153+ // NamingException expected count is 3 since testGetGroups calls
154+ // doTestGetGroups and doTestGetGroupsWithFallback in succession and
155+ // the count is across both test scenarios.
156+ verify (getContext (), times (3 )).search (anyString (), anyString (),
157+ any (Object [].class ), any (SearchControls .class ));
158+ }
159+
160+ private static final class TestLdapGroupsMapping extends LdapGroupsMapping {
161+ private boolean secondaryQueryCalled = false ;
162+ public boolean isSecondaryQueryCalled () {
163+ return secondaryQueryCalled ;
164+ }
165+ List <String > lookupGroup (SearchResult result , DirContext c ,
166+ int goUpHierarchy ) throws NamingException {
167+ secondaryQueryCalled = true ;
168+ return super .lookupGroup (result , c , goUpHierarchy );
169+ }
170+ }
171+ }
0 commit comments