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