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