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