View Javadoc

1   package org.apache.hadoop.hbase.security;
2   
3   /**
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  import java.io.IOException;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.conf.Configuration;
28  import org.apache.hadoop.security.UserGroupInformation;
29  
30  public class HBaseMultiRealmUserAuthentication {
31  
32    private static final Log LOG = LogFactory.getLog(HBaseMultiRealmUserAuthentication.class);
33    // Configuration setttings copy-pasted from Hadoop
34    public static String KERBEROS_USER_REALM_PRINCIPAL =
35        "hadoop.security.authentication.userrealm.principal";
36    public static String KERBEROS_USER_REALM ="hadoop.security.authentication.userrealm";
37  
38    // class variable used to store the Subject
39    private static UserGroupInformation ugi;
40  
41    private static boolean isInitialized;
42    private static boolean isEnabled;
43    private static Method isAUserInADifferentRealmMethod;
44    private static Method getServerUGIForUserRealmMethod;
45    private static Method loginServerFromCurrentKeytabAndReturnUGIMethod;
46    private static Method replaceRealmWithUserRealmMethod;
47  
48    private static synchronized void initialize(boolean isEnabled) throws IOException {
49      if (!isEnabled) {
50        return;
51      }
52      String className = "org.apache.hadoop.security.MultiRealmUserAuthentication";
53      try {
54        Class c = Class.forName(className, true, UserGroupInformation.class.getClassLoader());
55        isAUserInADifferentRealmMethod = c.getDeclaredMethod(
56            "isAUserInADifferentRealm", UserGroupInformation.class, Configuration.class);
57        getServerUGIForUserRealmMethod = c.getDeclaredMethod(
58            "getServerUGIForUserRealm", Configuration.class);
59        replaceRealmWithUserRealmMethod = c.getDeclaredMethod(
60            "replaceRealmWithUserRealm", String.class, Configuration.class);
61        loginServerFromCurrentKeytabAndReturnUGIMethod = UserGroupInformation.class
62            .getDeclaredMethod("loginServerFromCurrentKeytabAndReturnUGI", String.class);
63      } catch (Throwable t) {
64        LOG.warn("Failed to load the required class" + className + " or get methods: " + t);
65        throw new IOException("Underlying Hadoop version doesn't support multi-realm " +
66            "authentication", t);
67      }
68      return;
69    }
70  
71    private static void ensureInitialized(Configuration conf) throws IOException {
72      if (!isInitialized) {
73        isEnabled = conf.get(KERBEROS_USER_REALM) == null ? false : true;
74        initialize(isEnabled);
75        isInitialized = true;
76      }
77    }
78  
79    /** Forwarding method, doesn't have to be synchronized. */
80    public static boolean isAUserInADifferentRealm(UserGroupInformation ticket, Configuration conf)
81        throws IOException {
82      ensureInitialized(conf);
83      if (!isEnabled) {
84        return false;
85      }
86      try {
87        return ((Boolean)isAUserInADifferentRealmMethod.invoke(null, ticket, conf)).booleanValue();
88      } catch (IllegalAccessException iae) {
89        throw new IOException("Hadoop version does not support multi-realm", iae);
90      } catch (InvocationTargetException ite) {
91        throw new IOException(ite.getTargetException());
92      }
93    }
94  
95    /**
96     * return the subject for server Principal  in the user realm
97     * This will be the same name as the server principal of the default realm with the
98     *  realm name replaced with the user realm name.
99     *  Once created, the the UGI is cached.
100    * @param conf
101    * @return UserGroupInformation
102    */
103   public static synchronized UserGroupInformation getServerUGIForUserRealm(Configuration conf)
104     throws IOException {
105     ensureInitialized(conf);
106     if (ugi != null) return ugi;
107 
108     // In case of error before ugi is set, we fall thru and return null.
109     String kurp = conf.get(KERBEROS_USER_REALM_PRINCIPAL);
110     try {
111       if (kurp != null) {
112         try {
113           ugi = (UserGroupInformation)
114               loginServerFromCurrentKeytabAndReturnUGIMethod.invoke(null, kurp);
115         } catch (InvocationTargetException ite) {
116           LOG.warn("Current user information cannot be obtained", ite);
117         }
118       } else {
119         try {
120           ugi = (UserGroupInformation)getServerUGIForUserRealmMethod.invoke(null, conf);
121         } catch (InvocationTargetException ite) {
122           throw new IOException(ite.getTargetException());
123         }
124       }
125     } catch (IllegalAccessException iae) {
126       throw new IOException("Hadoop version does not support multi-realm", iae);
127     }
128     return ugi;
129   }
130 
131   /**
132    * replaces the realm part of the principal name with the user realm
133    * This method will be invoked by client side
134    * @param principalName
135    * @param conf
136    * @return string value containing server principal in user realm
137    */
138   public static String replaceRealmWithUserRealm(String principalName, Configuration conf)
139       throws IOException {
140     ensureInitialized(conf);
141     String kurp = conf.get(KERBEROS_USER_REALM_PRINCIPAL);
142     if (kurp != null) return kurp;
143     try {
144       return (String)replaceRealmWithUserRealmMethod.invoke(null, principalName, conf);
145     } catch (IllegalAccessException iae) {
146       throw new IOException("Hadoop version does not support multi-realm", iae);
147     } catch (InvocationTargetException ite) {
148       throw new IOException(ite.getTargetException());
149     }
150   }
151 }