From 5ef080e8a702ca52167ca8ca1b50b9c123be5a1e Mon Sep 17 00:00:00 2001 From: Devaraj Das Date: Thu, 19 Dec 2013 18:05:15 +0000 Subject: [PATCH] HBASE-9866. Support the mode where REST server authorizes proxy users git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1552385 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/resources/hbase-default.xml | 5 ++++ .../apache/hadoop/hbase/rest/RESTServlet.java | 25 ++++++++-------- .../hbase/rest/RESTServletContainer.java | 29 ++++++++++++++++++- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/hbase-common/src/main/resources/hbase-default.xml b/hbase-common/src/main/resources/hbase-default.xml index e8cc56c162b..5eabdb3ee88 100644 --- a/hbase-common/src/main/resources/hbase-default.xml +++ b/hbase-common/src/main/resources/hbase-default.xml @@ -828,6 +828,11 @@ possible configurations would overwhelm and obscure the important. The thread pool always has at least these number of threads so the REST server is ready to serve incoming requests. + + hbase.rest.support.proxyuser + false + Enables running the REST server to support proxy-user mode. + hbase.defaults.for.version @@@VERSION@@@ diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServlet.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServlet.java index 6df1deae345..c265e40d410 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServlet.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServlet.java @@ -56,14 +56,17 @@ public class RESTServlet implements Constants { static final String CLEANUP_INTERVAL = "hbase.rest.connection.cleanup-interval"; static final String MAX_IDLETIME = "hbase.rest.connection.max-idletime"; - static final String NULL_USERNAME = "--NULL--"; - - private final ThreadLocal effectiveUser = new ThreadLocal() { - protected String initialValue() { - return NULL_USERNAME; + private final ThreadLocal effectiveUser = + new ThreadLocal() { + protected UserGroupInformation initialValue() { + return realUser; } }; + UserGroupInformation getRealUser() { + return realUser; + } + // A chore to clean up idle connections. private final Chore connectionCleaner; private final Stoppable stoppable; @@ -192,7 +195,7 @@ public class RESTServlet implements Constants { HBaseAdmin getAdmin() throws IOException { ConnectionInfo connInfo = getCurrentConnection(); if (connInfo.admin == null) { - Lock lock = locker.acquireLock(effectiveUser.get()); + Lock lock = locker.acquireLock(effectiveUser.get().getUserName()); try { if (connInfo.admin == null) { connInfo.admin = new HBaseAdmin(connInfo.connection); @@ -229,23 +232,19 @@ public class RESTServlet implements Constants { return getConfiguration().getBoolean("hbase.rest.readonly", false); } - void setEffectiveUser(String effectiveUser) { + void setEffectiveUser(UserGroupInformation effectiveUser) { this.effectiveUser.set(effectiveUser); } private ConnectionInfo getCurrentConnection() throws IOException { - String userName = effectiveUser.get(); + String userName = effectiveUser.get().getUserName(); ConnectionInfo connInfo = connections.get(userName); if (connInfo == null || !connInfo.updateAccessTime()) { Lock lock = locker.acquireLock(userName); try { connInfo = connections.get(userName); if (connInfo == null) { - UserGroupInformation ugi = realUser; - if (!userName.equals(NULL_USERNAME)) { - ugi = UserGroupInformation.createProxyUser(userName, realUser); - } - User user = userProvider.create(ugi); + User user = userProvider.create(effectiveUser.get()); HConnection conn = HConnectionManager.createConnection(conf, user); connInfo = new ConnectionInfo(conn, userName); connections.put(userName, connInfo); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java index 976132e5834..5caba47cdad 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/RESTServletContainer.java @@ -28,6 +28,11 @@ import org.apache.hadoop.classification.InterfaceAudience; import com.sun.jersey.spi.container.servlet.ServletContainer; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authorize.AuthorizationException; +import org.apache.hadoop.security.authorize.ProxyUsers; +import org.apache.hadoop.conf.Configuration; + /** * REST servlet container. It is used to get the remote request user * without going through @HttpContext, so that we can minimize code changes. @@ -44,7 +49,29 @@ public class RESTServletContainer extends ServletContainer { @Override public void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { - RESTServlet.getInstance().setEffectiveUser(request.getRemoteUser()); + final String doAsUserFromQuery = request.getParameter("doAs"); + Configuration conf = RESTServlet.getInstance().getConfiguration(); + final boolean proxyConfigured = conf.getBoolean("hbase.rest.support.proxyuser", false); + if (doAsUserFromQuery != null && !proxyConfigured) { + throw new ServletException("Support for proxyuser is not configured"); + } + UserGroupInformation ugi = RESTServlet.getInstance().getRealUser(); + if (doAsUserFromQuery != null) { + // create and attempt to authorize a proxy user (the client is attempting + // to do proxy user) + ugi = UserGroupInformation.createProxyUser(doAsUserFromQuery, ugi); + // validate the proxy user authorization + try { + ProxyUsers.authorize(ugi, request.getRemoteAddr(), conf); + } catch(AuthorizationException e) { + throw new ServletException(e.getMessage()); + } + } else { + // the REST server would send the request without validating the proxy + // user authorization + ugi = UserGroupInformation.createProxyUser(request.getRemoteUser(), ugi); + } + RESTServlet.getInstance().setEffectiveUser(ugi); super.service(request, response); } }