1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 import static org.mockito.Matchers.any;
26 import static org.mockito.Matchers.anyString;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.verify;
29 import static org.mockito.Mockito.when;
30
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34
35 import javax.security.auth.callback.Callback;
36 import javax.security.auth.callback.CallbackHandler;
37 import javax.security.auth.callback.NameCallback;
38 import javax.security.auth.callback.PasswordCallback;
39 import javax.security.auth.callback.TextOutputCallback;
40 import javax.security.auth.callback.UnsupportedCallbackException;
41 import javax.security.sasl.Sasl;
42 import javax.security.sasl.RealmCallback;
43 import javax.security.sasl.RealmChoiceCallback;
44 import javax.security.sasl.SaslClient;
45
46 import org.apache.hadoop.hbase.testclassification.SmallTests;
47 import org.apache.hadoop.hbase.security.HBaseSaslRpcClient.SaslClientCallbackHandler;
48 import org.apache.hadoop.io.DataInputBuffer;
49 import org.apache.hadoop.io.DataOutputBuffer;
50 import org.apache.hadoop.security.token.Token;
51 import org.apache.hadoop.security.token.TokenIdentifier;
52 import org.apache.log4j.Level;
53 import org.apache.log4j.Logger;
54 import org.junit.BeforeClass;
55 import org.junit.Rule;
56 import org.junit.Test;
57 import org.junit.experimental.categories.Category;
58 import org.junit.rules.ExpectedException;
59 import org.mockito.Mockito;
60
61 import com.google.common.base.Strings;
62
63 @Category(SmallTests.class)
64 public class TestHBaseSaslRpcClient {
65
66 static {
67 System.setProperty("java.security.krb5.realm", "DOMAIN.COM");
68 System.setProperty("java.security.krb5.kdc", "DOMAIN.COM");
69 }
70
71 static final String DEFAULT_USER_NAME = "principal";
72 static final String DEFAULT_USER_PASSWORD = "password";
73
74 private static final Logger LOG = Logger.getLogger(TestHBaseSaslRpcClient.class);
75
76
77 @Rule
78 public ExpectedException exception = ExpectedException.none();
79
80 @BeforeClass
81 public static void before() {
82 Logger.getRootLogger().setLevel(Level.DEBUG);
83 }
84
85 @Test
86 public void testSaslQOPNotEmpty() throws Exception {
87 Token<? extends TokenIdentifier> token = createTokenMockWithCredentials(DEFAULT_USER_NAME,
88 DEFAULT_USER_PASSWORD);
89
90 new HBaseSaslRpcClient(AuthMethod.DIGEST, token, "principal/host@DOMAIN.COM", false);
91 assertTrue(SaslUtil.SASL_PROPS.get(Sasl.QOP).equals(SaslUtil.QualityOfProtection.
92 AUTHENTICATION.getSaslQop()));
93
94
95 new HBaseSaslRpcClient(AuthMethod.DIGEST, token, "principal/host@DOMAIN.COM", false,
96 "authentication");
97 assertTrue(SaslUtil.SASL_PROPS.get(Sasl.QOP).equals(SaslUtil.QualityOfProtection.
98 AUTHENTICATION.getSaslQop()));
99
100 new HBaseSaslRpcClient(AuthMethod.DIGEST, token, "principal/host@DOMAIN.COM", false,
101 "privacy");
102 assertTrue(SaslUtil.SASL_PROPS.get(Sasl.QOP).equals(SaslUtil.QualityOfProtection.
103 PRIVACY.getSaslQop()));
104
105 new HBaseSaslRpcClient(AuthMethod.DIGEST, token, "principal/host@DOMAIN.COM", false,
106 "integrity");
107 assertTrue(SaslUtil.SASL_PROPS.get(Sasl.QOP).equals(SaslUtil.QualityOfProtection.
108 INTEGRITY.getSaslQop()));
109
110 exception.expect(IllegalArgumentException.class);
111 new HBaseSaslRpcClient(AuthMethod.DIGEST, token, "principal/host@DOMAIN.COM", false,
112 "wrongvalue");
113 }
114
115 @Test
116 public void testSaslClientCallbackHandler() throws UnsupportedCallbackException {
117 final Token<? extends TokenIdentifier> token = createTokenMock();
118 when(token.getIdentifier()).thenReturn(DEFAULT_USER_NAME.getBytes());
119 when(token.getPassword()).thenReturn(DEFAULT_USER_PASSWORD.getBytes());
120
121 final NameCallback nameCallback = mock(NameCallback.class);
122 final PasswordCallback passwordCallback = mock(PasswordCallback.class);
123 final RealmCallback realmCallback = mock(RealmCallback.class);
124 final RealmChoiceCallback realmChoiceCallback = mock(RealmChoiceCallback.class);
125
126 Callback[] callbackArray = {nameCallback, passwordCallback,
127 realmCallback, realmChoiceCallback};
128 final SaslClientCallbackHandler saslClCallbackHandler = new SaslClientCallbackHandler(token);
129 saslClCallbackHandler.handle(callbackArray);
130 verify(nameCallback).setName(anyString());
131 verify(realmCallback).setText(anyString());
132 verify(passwordCallback).setPassword(any(char[].class));
133 }
134
135 @Test
136 public void testSaslClientCallbackHandlerWithException() {
137 final Token<? extends TokenIdentifier> token = createTokenMock();
138 when(token.getIdentifier()).thenReturn(DEFAULT_USER_NAME.getBytes());
139 when(token.getPassword()).thenReturn(DEFAULT_USER_PASSWORD.getBytes());
140 final SaslClientCallbackHandler saslClCallbackHandler = new SaslClientCallbackHandler(token);
141 try {
142 saslClCallbackHandler.handle(new Callback[] { mock(TextOutputCallback.class) });
143 } catch (UnsupportedCallbackException expEx) {
144
145 } catch (Exception ex) {
146 fail("testSaslClientCallbackHandlerWithException error : " + ex.getMessage());
147 }
148 }
149
150 @Test
151 public void testHBaseSaslRpcClientCreation() throws Exception {
152
153 assertFalse(assertSuccessCreationKerberosPrincipal(null));
154 assertFalse(assertSuccessCreationKerberosPrincipal("DOMAIN.COM"));
155 assertFalse(assertSuccessCreationKerberosPrincipal("principal/DOMAIN.COM"));
156 if (!assertSuccessCreationKerberosPrincipal("principal/localhost@DOMAIN.COM")) {
157
158
159 LOG.warn("Could not create a SASL client with valid Kerberos credential");
160 }
161
162
163 assertFalse(assertSuccessCreationDigestPrincipal(null, null));
164 assertFalse(assertSuccessCreationDigestPrincipal("", ""));
165 assertFalse(assertSuccessCreationDigestPrincipal("", null));
166 assertFalse(assertSuccessCreationDigestPrincipal(null, ""));
167 assertTrue(assertSuccessCreationDigestPrincipal(DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
168
169
170 assertFalse(assertSuccessCreationSimplePrincipal("", ""));
171 assertFalse(assertSuccessCreationSimplePrincipal(null, null));
172 assertFalse(assertSuccessCreationSimplePrincipal(DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
173
174
175 assertTrue(assertIOExceptionThenSaslClientIsNull(DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
176 assertTrue(assertIOExceptionWhenGetStreamsBeforeConnectCall(
177 DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
178 }
179
180 @Test
181 public void testAuthMethodReadWrite() throws IOException {
182 DataInputBuffer in = new DataInputBuffer();
183 DataOutputBuffer out = new DataOutputBuffer();
184
185 assertAuthMethodRead(in, AuthMethod.SIMPLE);
186 assertAuthMethodRead(in, AuthMethod.KERBEROS);
187 assertAuthMethodRead(in, AuthMethod.DIGEST);
188
189 assertAuthMethodWrite(out, AuthMethod.SIMPLE);
190 assertAuthMethodWrite(out, AuthMethod.KERBEROS);
191 assertAuthMethodWrite(out, AuthMethod.DIGEST);
192 }
193
194 private void assertAuthMethodRead(DataInputBuffer in, AuthMethod authMethod)
195 throws IOException {
196 in.reset(new byte[] {authMethod.code}, 1);
197 assertEquals(authMethod, AuthMethod.read(in));
198 }
199
200 private void assertAuthMethodWrite(DataOutputBuffer out, AuthMethod authMethod)
201 throws IOException {
202 authMethod.write(out);
203 assertEquals(authMethod.code, out.getData()[0]);
204 out.reset();
205 }
206
207 private boolean assertIOExceptionWhenGetStreamsBeforeConnectCall(String principal,
208 String password) throws IOException {
209 boolean inState = false;
210 boolean outState = false;
211
212 HBaseSaslRpcClient rpcClient = new HBaseSaslRpcClient(AuthMethod.DIGEST,
213 createTokenMockWithCredentials(principal, password), principal, false) {
214 @Override
215 public SaslClient createDigestSaslClient(String[] mechanismNames,
216 String saslDefaultRealm, CallbackHandler saslClientCallbackHandler)
217 throws IOException {
218 return Mockito.mock(SaslClient.class);
219 }
220
221 @Override
222 public SaslClient createKerberosSaslClient(String[] mechanismNames,
223 String userFirstPart, String userSecondPart) throws IOException {
224 return Mockito.mock(SaslClient.class);
225 }
226 };
227
228 try {
229 rpcClient.getInputStream(Mockito.mock(InputStream.class));
230 } catch(IOException ex) {
231
232 inState = true;
233 }
234
235 try {
236 rpcClient.getOutputStream(Mockito.mock(OutputStream.class));
237 } catch(IOException ex) {
238
239 outState = true;
240 }
241
242 return inState && outState;
243 }
244
245 private boolean assertIOExceptionThenSaslClientIsNull(String principal, String password) {
246 try {
247 new HBaseSaslRpcClient(AuthMethod.DIGEST,
248 createTokenMockWithCredentials(principal, password), principal, false) {
249 @Override
250 public SaslClient createDigestSaslClient(String[] mechanismNames,
251 String saslDefaultRealm, CallbackHandler saslClientCallbackHandler)
252 throws IOException {
253 return null;
254 }
255
256 @Override
257 public SaslClient createKerberosSaslClient(String[] mechanismNames,
258 String userFirstPart, String userSecondPart) throws IOException {
259 return null;
260 }
261 };
262 return false;
263 } catch (IOException ex) {
264 return true;
265 }
266 }
267
268 private boolean assertSuccessCreationKerberosPrincipal(String principal) {
269 HBaseSaslRpcClient rpcClient = null;
270 try {
271 rpcClient = createSaslRpcClientForKerberos(principal);
272 } catch(Exception ex) {
273 LOG.error(ex.getMessage(), ex);
274 }
275 return rpcClient != null;
276 }
277
278 private boolean assertSuccessCreationDigestPrincipal(String principal, String password) {
279 HBaseSaslRpcClient rpcClient = null;
280 try {
281 rpcClient = new HBaseSaslRpcClient(AuthMethod.DIGEST,
282 createTokenMockWithCredentials(principal, password), principal, false);
283 } catch(Exception ex) {
284 LOG.error(ex.getMessage(), ex);
285 }
286 return rpcClient != null;
287 }
288
289 private boolean assertSuccessCreationSimplePrincipal(String principal, String password) {
290 HBaseSaslRpcClient rpcClient = null;
291 try {
292 rpcClient = createSaslRpcClientSimple(principal, password);
293 } catch(Exception ex) {
294 LOG.error(ex.getMessage(), ex);
295 }
296 return rpcClient != null;
297 }
298
299 private HBaseSaslRpcClient createSaslRpcClientForKerberos(String principal)
300 throws IOException {
301 return new HBaseSaslRpcClient(AuthMethod.KERBEROS, createTokenMock(), principal, false);
302 }
303
304 private Token<? extends TokenIdentifier> createTokenMockWithCredentials(
305 String principal, String password)
306 throws IOException {
307 Token<? extends TokenIdentifier> token = createTokenMock();
308 if (!Strings.isNullOrEmpty(principal) && !Strings.isNullOrEmpty(password)) {
309 when(token.getIdentifier()).thenReturn(DEFAULT_USER_NAME.getBytes());
310 when(token.getPassword()).thenReturn(DEFAULT_USER_PASSWORD.getBytes());
311 }
312 return token;
313 }
314
315 private HBaseSaslRpcClient createSaslRpcClientSimple(String principal, String password)
316 throws IOException {
317 return new HBaseSaslRpcClient(AuthMethod.SIMPLE, createTokenMock(), principal, false);
318 }
319
320 @SuppressWarnings("unchecked")
321 private Token<? extends TokenIdentifier> createTokenMock() {
322 return mock(Token.class);
323 }
324 }