View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.fail;
23  
24  import java.io.IOException;
25  import java.lang.reflect.InvocationTargetException;
26  import java.lang.reflect.Method;
27  import java.util.List;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.conf.Configuration;
32  import org.junit.Test;
33  import org.junit.experimental.categories.Category;
34  
35  @Category(SmallTests.class)
36  public class TestHBaseConfiguration {
37  
38    private static final Log LOG = LogFactory.getLog(TestHBaseConfiguration.class);
39  
40    @Test
41    public void testGetIntDeprecated() {
42      int VAL = 1, VAL2 = 2;
43      String NAME = "foo";
44      String DEPRECATED_NAME = "foo.deprecated";
45  
46      Configuration conf = HBaseConfiguration.create();
47      conf.setInt(NAME, VAL);
48      assertEquals(VAL, HBaseConfiguration.getInt(conf, NAME, DEPRECATED_NAME, 0));
49  
50      conf = HBaseConfiguration.create();
51      conf.setInt(DEPRECATED_NAME, VAL);
52      assertEquals(VAL, HBaseConfiguration.getInt(conf, NAME, DEPRECATED_NAME, 0));
53  
54      conf = HBaseConfiguration.create();
55      conf.setInt(DEPRECATED_NAME, VAL);
56      conf.setInt(NAME, VAL);
57      assertEquals(VAL, HBaseConfiguration.getInt(conf, NAME, DEPRECATED_NAME, 0));
58  
59      conf = HBaseConfiguration.create();
60      conf.setInt(DEPRECATED_NAME, VAL);
61      conf.setInt(NAME, VAL2); // deprecated value will override this
62      assertEquals(VAL, HBaseConfiguration.getInt(conf, NAME, DEPRECATED_NAME, 0));
63    }
64  
65    @Test
66    public void testGetPassword() throws Exception {
67      Configuration conf = HBaseConfiguration.create();
68      conf.set(ReflectiveCredentialProviderClient.CREDENTIAL_PROVIDER_PATH,
69          "jceks://file/tmp/foo.jks");
70      ReflectiveCredentialProviderClient client =
71          new ReflectiveCredentialProviderClient();
72      if (client.isHadoopCredentialProviderAvailable()) {
73        char[] keyPass = {'k', 'e', 'y', 'p', 'a', 's', 's'};
74        char[] storePass = {'s', 't', 'o', 'r', 'e', 'p', 'a', 's', 's'};
75        client.createEntry(conf, "ssl.keypass.alias", keyPass);
76        client.createEntry(conf, "ssl.storepass.alias", storePass);
77  
78        String keypass = HBaseConfiguration.getPassword(
79            conf, "ssl.keypass.alias", null);
80        assertEquals(keypass, new String(keyPass));
81  
82        String storepass = HBaseConfiguration.getPassword(
83            conf, "ssl.storepass.alias", null);
84        assertEquals(storepass, new String(storePass));
85      }
86    }
87  
88    private static class ReflectiveCredentialProviderClient {
89      public static final String HADOOP_CRED_PROVIDER_FACTORY_CLASS_NAME =
90          "org.apache.hadoop.security.alias.JavaKeyStoreProvider$Factory";
91      public static final String
92        HADOOP_CRED_PROVIDER_FACTORY_GET_PROVIDERS_METHOD_NAME = "getProviders";
93  
94      public static final String HADOOP_CRED_PROVIDER_CLASS_NAME =
95          "org.apache.hadoop.security.alias.CredentialProvider";
96      public static final String
97          HADOOP_CRED_PROVIDER_GET_CREDENTIAL_ENTRY_METHOD_NAME =
98          "getCredentialEntry";
99      public static final String
100         HADOOP_CRED_PROVIDER_GET_ALIASES_METHOD_NAME = "getAliases";
101     public static final String
102         HADOOP_CRED_PROVIDER_CREATE_CREDENTIAL_ENTRY_METHOD_NAME =
103         "createCredentialEntry";
104     public static final String HADOOP_CRED_PROVIDER_FLUSH_METHOD_NAME = "flush";
105 
106     public static final String HADOOP_CRED_ENTRY_CLASS_NAME =
107         "org.apache.hadoop.security.alias.CredentialProvider$CredentialEntry";
108     public static final String HADOOP_CRED_ENTRY_GET_CREDENTIAL_METHOD_NAME =
109         "getCredential";
110 
111     public static final String CREDENTIAL_PROVIDER_PATH =
112         "hadoop.security.credential.provider.path";
113 
114     private static Object hadoopCredProviderFactory = null;
115     private static Method getProvidersMethod = null;
116     private static Method getAliasesMethod = null;
117     private static Method getCredentialEntryMethod = null;
118     private static Method getCredentialMethod = null;
119     private static Method createCredentialEntryMethod = null;
120     private static Method flushMethod = null;
121     private static Boolean hadoopClassesAvailable = null;
122 
123     /**
124      * Determine if we can load the necessary CredentialProvider classes. Only
125      * loaded the first time, so subsequent invocations of this method should
126      * return fast.
127      *
128      * @return True if the CredentialProvider classes/methods are available,
129      *         false otherwise.
130      */
131     private boolean isHadoopCredentialProviderAvailable() {
132       if (null != hadoopClassesAvailable) {
133         // Make sure everything is initialized as expected
134         if (hadoopClassesAvailable && null != getProvidersMethod
135             && null != hadoopCredProviderFactory
136             && null != getCredentialEntryMethod && null != getCredentialMethod) {
137           return true;
138         } else {
139           // Otherwise we failed to load it
140           return false;
141         }
142       }
143 
144       hadoopClassesAvailable = false;
145 
146       // Load Hadoop CredentialProviderFactory
147       Class<?> hadoopCredProviderFactoryClz = null;
148       try {
149         hadoopCredProviderFactoryClz = Class
150             .forName(HADOOP_CRED_PROVIDER_FACTORY_CLASS_NAME);
151       } catch (ClassNotFoundException e) {
152         return false;
153       }
154       // Instantiate Hadoop CredentialProviderFactory
155       try {
156         hadoopCredProviderFactory = hadoopCredProviderFactoryClz.newInstance();
157       } catch (InstantiationException e) {
158         return false;
159       } catch (IllegalAccessException e) {
160         return false;
161       }
162 
163       try {
164         getProvidersMethod = loadMethod(hadoopCredProviderFactoryClz,
165             HADOOP_CRED_PROVIDER_FACTORY_GET_PROVIDERS_METHOD_NAME,
166             Configuration.class);
167 
168         // Load Hadoop CredentialProvider
169         Class<?> hadoopCredProviderClz = null;
170         hadoopCredProviderClz = Class.forName(HADOOP_CRED_PROVIDER_CLASS_NAME);
171         getCredentialEntryMethod = loadMethod(hadoopCredProviderClz,
172             HADOOP_CRED_PROVIDER_GET_CREDENTIAL_ENTRY_METHOD_NAME, String.class);
173 
174         getAliasesMethod = loadMethod(hadoopCredProviderClz,
175             HADOOP_CRED_PROVIDER_GET_ALIASES_METHOD_NAME);
176 
177         createCredentialEntryMethod = loadMethod(hadoopCredProviderClz,
178             HADOOP_CRED_PROVIDER_CREATE_CREDENTIAL_ENTRY_METHOD_NAME,
179             String.class, char[].class);
180 
181         flushMethod = loadMethod(hadoopCredProviderClz,
182             HADOOP_CRED_PROVIDER_FLUSH_METHOD_NAME);
183 
184         // Load Hadoop CredentialEntry
185         Class<?> hadoopCredentialEntryClz = null;
186         try {
187           hadoopCredentialEntryClz = Class
188               .forName(HADOOP_CRED_ENTRY_CLASS_NAME);
189         } catch (ClassNotFoundException e) {
190           LOG.error("Failed to load class:" + e);
191           return false;
192         }
193 
194         getCredentialMethod = loadMethod(hadoopCredentialEntryClz,
195             HADOOP_CRED_ENTRY_GET_CREDENTIAL_METHOD_NAME);
196       } catch (Exception e1) {
197         return false;
198       }
199 
200       hadoopClassesAvailable = true;
201       LOG.info("Credential provider classes have been" +
202       		" loaded and initialized successfully through reflection.");
203       return true;
204 
205     }
206 
207     private Method loadMethod(Class<?> clz, String name, Class<?>... classes)
208         throws Exception {
209       Method method = null;
210       try {
211         method = clz.getMethod(name, classes);
212       } catch (SecurityException e) {
213         fail("security exception caught for: " + name + " in " +
214       clz.getCanonicalName());
215         throw e;
216       } catch (NoSuchMethodException e) {
217         LOG.error("Failed to load the " + name + ": " + e);
218         fail("no such method: " + name + " in " + clz.getCanonicalName());
219         throw e;
220       }
221       return method;
222     }
223 
224     /**
225      * Wrapper to fetch the configured {@code List<CredentialProvider>}s.
226      *
227      * @param conf
228      *    Configuration with GENERAL_SECURITY_CREDENTIAL_PROVIDER_PATHS defined
229      * @return List of CredentialProviders, or null if they could not be loaded
230      */
231     @SuppressWarnings("unchecked")
232     protected  List<Object> getCredentialProviders(Configuration conf) {
233       // Call CredentialProviderFactory.getProviders(Configuration)
234       Object providersObj = null;
235       try {
236         providersObj = getProvidersMethod.invoke(hadoopCredProviderFactory,
237             conf);
238       } catch (IllegalArgumentException e) {
239         LOG.error("Failed to invoke: " + getProvidersMethod.getName() +
240             ": " + e);
241         return null;
242       } catch (IllegalAccessException e) {
243         LOG.error("Failed to invoke: " + getProvidersMethod.getName() +
244             ": " + e);
245         return null;
246       } catch (InvocationTargetException e) {
247         LOG.error("Failed to invoke: " + getProvidersMethod.getName() +
248             ": " + e);
249         return null;
250       }
251 
252       // Cast the Object to List<Object> (actually List<CredentialProvider>)
253       try {
254         return (List<Object>) providersObj;
255       } catch (ClassCastException e) {
256         return null;
257       }
258     }
259 
260     /**
261      * Create a CredentialEntry using the configured Providers.
262      * If multiple CredentialProviders are configured, the first will be used.
263      *
264      * @param conf
265      *          Configuration for the CredentialProvider
266      * @param name
267      *          CredentialEntry name (alias)
268      * @param credential
269      *          The credential
270      */
271     public  void createEntry(Configuration conf, String name, char[] credential)
272         throws Exception {
273 
274       if (!isHadoopCredentialProviderAvailable()) {
275         return;
276       }
277 
278       List<Object> providers = getCredentialProviders(conf);
279       if (null == providers) {
280         throw new IOException("Could not fetch any CredentialProviders, " +
281         		"is the implementation available?");
282       }
283 
284       Object provider = providers.get(0);
285       createEntryInProvider(provider, name, credential);
286     }
287 
288     /**
289      * Create a CredentialEntry with the give name and credential in the
290      * credentialProvider. The credentialProvider argument must be an instance
291      * of Hadoop
292      * CredentialProvider.
293      *
294      * @param credentialProvider
295      *          Instance of CredentialProvider
296      * @param name
297      *          CredentialEntry name (alias)
298      * @param credential
299      *          The credential to store
300      */
301     private void createEntryInProvider(Object credentialProvider,
302         String name, char[] credential) throws Exception {
303 
304       if (!isHadoopCredentialProviderAvailable()) {
305         return;
306       }
307 
308       try {
309         createCredentialEntryMethod.invoke(credentialProvider, name, credential);
310       } catch (IllegalArgumentException e) {
311         return;
312       } catch (IllegalAccessException e) {
313         return;
314       } catch (InvocationTargetException e) {
315         return;
316       }
317 
318       try {
319         flushMethod.invoke(credentialProvider);
320       } catch (IllegalArgumentException e) {
321         throw e;
322       } catch (IllegalAccessException e) {
323         throw e;
324       } catch (InvocationTargetException e) {
325         throw e;
326       }
327     }
328   }
329 }