18
18
19
19
package org .apache .hadoop .security ;
20
20
21
+ import java .util .ArrayList ;
21
22
import java .util .Arrays ;
22
23
import java .util .List ;
24
+ import java .util .Set ;
23
25
24
26
import javax .naming .NamingEnumeration ;
25
27
import javax .naming .NamingException ;
26
28
import javax .naming .directory .Attribute ;
29
+ import javax .naming .directory .DirContext ;
27
30
import javax .naming .directory .SearchControls ;
28
31
import javax .naming .directory .SearchResult ;
29
32
30
33
import org .apache .hadoop .conf .Configuration ;
31
34
import org .junit .Assert ;
32
- import org .junit .Before ;
33
35
import org .junit .Test ;
36
+ import org .mockito .stubbing .Stubber ;
34
37
35
38
import static org .mockito .ArgumentMatchers .any ;
36
39
import static org .mockito .ArgumentMatchers .anyString ;
49
52
public class TestLdapGroupsMappingWithOneQuery
50
53
extends TestLdapGroupsMappingBase {
51
54
52
- @ Before
53
- public void setupMocks () throws NamingException {
55
+ public void setupMocks (List <String > listOfDNs ) throws NamingException {
54
56
Attribute groupDN = mock (Attribute .class );
55
57
56
58
NamingEnumeration <SearchResult > groupNames = getGroupNames ();
57
59
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 );
65
64
66
65
when (getAttributes ().get (eq ("memberOf" ))).thenReturn (groupDN );
67
66
}
68
67
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
+
69
87
@ Test
70
88
public void testGetGroups () throws NamingException {
71
89
// given a user whose ldap query returns a user object with three "memberOf"
72
90
// properties, return an array of strings representing its groups.
73
91
String [] testGroups = new String [] {"abc" , "xyz" , "sss" };
74
92
doTestGetGroups (Arrays .asList (testGroups ));
93
+
94
+ // test fallback triggered by NamingException
95
+ doTestGetGroupsWithFallback ();
75
96
}
76
97
77
98
private void doTestGetGroups (List <String > expectedGroups )
78
99
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 );
79
106
String ldapUrl = "ldap://test" ;
80
107
Configuration conf = getBaseConf (ldapUrl );
81
108
// enable single-query lookup
82
109
conf .set (LdapGroupsMapping .MEMBEROF_ATTR_KEY , "memberOf" );
83
110
84
- LdapGroupsMapping groupsMapping = getGroupsMapping ();
111
+ TestLdapGroupsMapping groupsMapping = new TestLdapGroupsMapping ();
85
112
groupsMapping .setConf (conf );
86
113
// Username is arbitrary, since the spy is mocked to respond the same,
87
114
// regardless of input
88
115
List <String > groups = groupsMapping .getGroups ("some_user" );
89
116
90
117
Assert .assertEquals (expectedGroups , groups );
118
+ Assert .assertFalse ("Second LDAP query should NOT have been called." ,
119
+ groupsMapping .isSecondaryQueryCalled ());
91
120
92
121
// We should have only made one query because single-query lookup is enabled
93
122
verify (getContext (), times (1 )).search (anyString (), anyString (),
94
123
any (Object [].class ), any (SearchControls .class ));
95
124
}
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