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
This commit is contained in:
Devaraj Das 2013-12-19 18:05:15 +00:00
parent 9f496164d6
commit 5ef080e8a7
3 changed files with 45 additions and 14 deletions

View File

@ -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 thread pool always has at least these number of threads so
the REST server is ready to serve incoming requests.</description> the REST server is ready to serve incoming requests.</description>
</property> </property>
<property>
<name>hbase.rest.support.proxyuser</name>
<value>false</value>
<description>Enables running the REST server to support proxy-user mode.</description>
</property>
<property skipInDoc="true"> <property skipInDoc="true">
<name>hbase.defaults.for.version</name> <name>hbase.defaults.for.version</name>
<value>@@@VERSION@@@</value> <value>@@@VERSION@@@</value>

View File

@ -56,14 +56,17 @@ public class RESTServlet implements Constants {
static final String CLEANUP_INTERVAL = "hbase.rest.connection.cleanup-interval"; static final String CLEANUP_INTERVAL = "hbase.rest.connection.cleanup-interval";
static final String MAX_IDLETIME = "hbase.rest.connection.max-idletime"; static final String MAX_IDLETIME = "hbase.rest.connection.max-idletime";
static final String NULL_USERNAME = "--NULL--"; private final ThreadLocal<UserGroupInformation> effectiveUser =
new ThreadLocal<UserGroupInformation>() {
private final ThreadLocal<String> effectiveUser = new ThreadLocal<String>() { protected UserGroupInformation initialValue() {
protected String initialValue() { return realUser;
return NULL_USERNAME;
} }
}; };
UserGroupInformation getRealUser() {
return realUser;
}
// A chore to clean up idle connections. // A chore to clean up idle connections.
private final Chore connectionCleaner; private final Chore connectionCleaner;
private final Stoppable stoppable; private final Stoppable stoppable;
@ -192,7 +195,7 @@ public class RESTServlet implements Constants {
HBaseAdmin getAdmin() throws IOException { HBaseAdmin getAdmin() throws IOException {
ConnectionInfo connInfo = getCurrentConnection(); ConnectionInfo connInfo = getCurrentConnection();
if (connInfo.admin == null) { if (connInfo.admin == null) {
Lock lock = locker.acquireLock(effectiveUser.get()); Lock lock = locker.acquireLock(effectiveUser.get().getUserName());
try { try {
if (connInfo.admin == null) { if (connInfo.admin == null) {
connInfo.admin = new HBaseAdmin(connInfo.connection); connInfo.admin = new HBaseAdmin(connInfo.connection);
@ -229,23 +232,19 @@ public class RESTServlet implements Constants {
return getConfiguration().getBoolean("hbase.rest.readonly", false); return getConfiguration().getBoolean("hbase.rest.readonly", false);
} }
void setEffectiveUser(String effectiveUser) { void setEffectiveUser(UserGroupInformation effectiveUser) {
this.effectiveUser.set(effectiveUser); this.effectiveUser.set(effectiveUser);
} }
private ConnectionInfo getCurrentConnection() throws IOException { private ConnectionInfo getCurrentConnection() throws IOException {
String userName = effectiveUser.get(); String userName = effectiveUser.get().getUserName();
ConnectionInfo connInfo = connections.get(userName); ConnectionInfo connInfo = connections.get(userName);
if (connInfo == null || !connInfo.updateAccessTime()) { if (connInfo == null || !connInfo.updateAccessTime()) {
Lock lock = locker.acquireLock(userName); Lock lock = locker.acquireLock(userName);
try { try {
connInfo = connections.get(userName); connInfo = connections.get(userName);
if (connInfo == null) { if (connInfo == null) {
UserGroupInformation ugi = realUser; User user = userProvider.create(effectiveUser.get());
if (!userName.equals(NULL_USERNAME)) {
ugi = UserGroupInformation.createProxyUser(userName, realUser);
}
User user = userProvider.create(ugi);
HConnection conn = HConnectionManager.createConnection(conf, user); HConnection conn = HConnectionManager.createConnection(conf, user);
connInfo = new ConnectionInfo(conn, userName); connInfo = new ConnectionInfo(conn, userName);
connections.put(userName, connInfo); connections.put(userName, connInfo);

View File

@ -28,6 +28,11 @@ import org.apache.hadoop.classification.InterfaceAudience;
import com.sun.jersey.spi.container.servlet.ServletContainer; 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 * REST servlet container. It is used to get the remote request user
* without going through @HttpContext, so that we can minimize code changes. * without going through @HttpContext, so that we can minimize code changes.
@ -44,7 +49,29 @@ public class RESTServletContainer extends ServletContainer {
@Override @Override
public void service(final HttpServletRequest request, public void service(final HttpServletRequest request,
final HttpServletResponse response) throws ServletException, IOException { 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); super.service(request, response);
} }
} }