HBASE-8662 [rest] support impersonation
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1503337 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9eeeafe824
commit
0bed86e6de
|
@ -0,0 +1,322 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hbase.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.HRegionLocation;
|
||||
import org.apache.hadoop.hbase.HTableDescriptor;
|
||||
import org.apache.hadoop.hbase.ServerName;
|
||||
import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
|
||||
import org.apache.hadoop.hbase.exceptions.MasterNotRunningException;
|
||||
import org.apache.hadoop.hbase.exceptions.ZooKeeperConnectionException;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ClientService;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.MasterAdminProtos.MasterAdminService;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.MasterMonitorProtos.MasterMonitorService;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
|
||||
/**
|
||||
* This is a HConnection wrapper. Whenever a RPC connection is created,
|
||||
* this class makes sure the specific user is used as the ticket. We assume
|
||||
* just these methods will create any RPC connection based on the default
|
||||
* HConnection implementation: getClient, getAdmin, getKeepAliveMasterMonitorService,
|
||||
* and getKeepAliveMasterAdminService.
|
||||
*
|
||||
* This class is put here only because the HConnection interface exposes
|
||||
* packaged private class MasterMonitorKeepAliveConnection and
|
||||
* MasterAddKeepAliveConnection.
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
@SuppressWarnings("deprecation")
|
||||
public class HConnectionWrapper implements HConnection {
|
||||
private final UserGroupInformation ugi;
|
||||
private final HConnection hconnection;
|
||||
|
||||
public HConnectionWrapper(final UserGroupInformation ugi,
|
||||
final HConnection hconnection) {
|
||||
this.hconnection = hconnection;
|
||||
this.ugi = ugi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(String why, Throwable e) {
|
||||
hconnection.abort(why, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAborted() {
|
||||
return hconnection.isAborted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
hconnection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration() {
|
||||
return hconnection.getConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMasterRunning() throws MasterNotRunningException,
|
||||
ZooKeeperConnectionException {
|
||||
return hconnection.isMasterRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTableEnabled(byte[] tableName) throws IOException {
|
||||
return hconnection.isTableEnabled(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTableDisabled(byte[] tableName) throws IOException {
|
||||
return hconnection.isTableDisabled(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTableAvailable(byte[] tableName) throws IOException {
|
||||
return hconnection.isTableAvailable(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTableAvailable(byte[] tableName, byte[][] splitKeys)
|
||||
throws IOException {
|
||||
return hconnection.isTableAvailable(tableName, splitKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTableDescriptor[] listTables() throws IOException {
|
||||
return hconnection.listTables();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTableDescriptor getHTableDescriptor(byte[] tableName)
|
||||
throws IOException {
|
||||
return hconnection.getHTableDescriptor(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HRegionLocation locateRegion(byte[] tableName, byte[] row)
|
||||
throws IOException {
|
||||
return hconnection.locateRegion(tableName, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearRegionCache() {
|
||||
hconnection.clearRegionCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearRegionCache(byte[] tableName) {
|
||||
hconnection.clearRegionCache(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCachedRegionLocation(HRegionLocation location) {
|
||||
hconnection.deleteCachedRegionLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HRegionLocation relocateRegion(byte[] tableName, byte[] row)
|
||||
throws IOException {
|
||||
return hconnection.relocateRegion(tableName, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCachedLocations(byte[] tableName, byte[] rowkey,
|
||||
Object exception, HRegionLocation source) {
|
||||
hconnection.updateCachedLocations(tableName, rowkey, exception, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HRegionLocation locateRegion(byte[] regionName) throws IOException {
|
||||
return hconnection.locateRegion(regionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HRegionLocation> locateRegions(byte[] tableName)
|
||||
throws IOException {
|
||||
return hconnection.locateRegions(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HRegionLocation> locateRegions(byte[] tableName,
|
||||
boolean useCache, boolean offlined) throws IOException {
|
||||
return hconnection.locateRegions(tableName, useCache, offlined);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterAdminService.BlockingInterface getMasterAdmin() throws IOException {
|
||||
return hconnection.getMasterAdmin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterMonitorService.BlockingInterface getMasterMonitor()
|
||||
throws IOException {
|
||||
return hconnection.getMasterMonitor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminService.BlockingInterface getAdmin(
|
||||
ServerName serverName) throws IOException {
|
||||
return hconnection.getAdmin(serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientService.BlockingInterface getClient(
|
||||
final ServerName serverName) throws IOException {
|
||||
try {
|
||||
return ugi.doAs(new PrivilegedExceptionAction<ClientService.BlockingInterface>() {
|
||||
@Override
|
||||
public ClientService.BlockingInterface run() throws IOException {
|
||||
return hconnection.getClient(serverName);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminService.BlockingInterface getAdmin(
|
||||
final ServerName serverName, final boolean getMaster) throws IOException {
|
||||
try {
|
||||
return ugi.doAs(new PrivilegedExceptionAction<AdminService.BlockingInterface>() {
|
||||
@Override
|
||||
public AdminService.BlockingInterface run() throws IOException {
|
||||
return hconnection.getAdmin(serverName, getMaster);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HRegionLocation getRegionLocation(byte[] tableName, byte[] row,
|
||||
boolean reload) throws IOException {
|
||||
return hconnection.getRegionLocation(tableName, row, reload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getRegionServerWithRetries(ServerCallable<T> callable)
|
||||
throws IOException, RuntimeException {
|
||||
return hconnection.getRegionServerWithRetries(callable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getRegionServerWithoutRetries(ServerCallable<T> callable)
|
||||
throws IOException, RuntimeException {
|
||||
return hconnection.getRegionServerWithoutRetries(callable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBatch(List<? extends Row> actions, byte[] tableName,
|
||||
ExecutorService pool, Object[] results) throws IOException,
|
||||
InterruptedException {
|
||||
hconnection.processBatch(actions, tableName, pool, results);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> void processBatchCallback(List<? extends Row> list,
|
||||
byte[] tableName, ExecutorService pool, Object[] results,
|
||||
Callback<R> callback) throws IOException, InterruptedException {
|
||||
hconnection.processBatchCallback(list, tableName, pool, results, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegionCachePrefetch(byte[] tableName, boolean enable) {
|
||||
hconnection.setRegionCachePrefetch(tableName, enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRegionCachePrefetch(byte[] tableName) {
|
||||
return hconnection.getRegionCachePrefetch(tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentNrHRS() throws IOException {
|
||||
return hconnection.getCurrentNrHRS();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTableDescriptor[] getHTableDescriptors(List<String> tableNames)
|
||||
throws IOException {
|
||||
return hconnection.getHTableDescriptors(tableNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return hconnection.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCaches(ServerName sn) {
|
||||
hconnection.clearCaches(sn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterMonitorKeepAliveConnection getKeepAliveMasterMonitorService()
|
||||
throws MasterNotRunningException {
|
||||
try {
|
||||
return ugi.doAs(new PrivilegedExceptionAction<MasterMonitorKeepAliveConnection>() {
|
||||
@Override
|
||||
public MasterMonitorKeepAliveConnection run() throws MasterNotRunningException {
|
||||
return hconnection.getKeepAliveMasterMonitorService();
|
||||
}
|
||||
});
|
||||
} catch (IOException ie) {
|
||||
throw new MasterNotRunningException(ie);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new MasterNotRunningException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterAdminKeepAliveConnection getKeepAliveMasterAdminService()
|
||||
throws MasterNotRunningException {
|
||||
try {
|
||||
return ugi.doAs(new PrivilegedExceptionAction<MasterAdminKeepAliveConnection>() {
|
||||
@Override
|
||||
public MasterAdminKeepAliveConnection run() throws MasterNotRunningException {
|
||||
return hconnection.getKeepAliveMasterAdminService();
|
||||
}
|
||||
});
|
||||
} catch (IOException ie) {
|
||||
throw new MasterNotRunningException(ie);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new MasterNotRunningException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeadServer(ServerName serverName) {
|
||||
return hconnection.isDeadServer(serverName);
|
||||
}
|
||||
}
|
|
@ -1000,6 +1000,24 @@ public class RpcClient {
|
|||
builder.setTraceInfo(RPCTInfo.newBuilder().
|
||||
setParentId(s.getSpanId()).setTraceId(s.getTraceId()));
|
||||
}
|
||||
UserGroupInformation ticket = remoteId.getTicket().getUGI();
|
||||
if (ticket != null) {
|
||||
UserGroupInformation currentUser = UserGroupInformation.getCurrentUser();
|
||||
String effectiveUser = currentUser == null ? null : currentUser.getShortUserName();
|
||||
if (effectiveUser != null && !effectiveUser.equals(ticket.getShortUserName())) {
|
||||
if (ticket.getRealUser() != null) {
|
||||
LOG.warn("Current user " + effectiveUser
|
||||
+ " is different from the ticket user " + ticket.getShortUserName()
|
||||
+ ". But the ticket is already a proxy user with real user "
|
||||
+ ticket.getRealUser().getShortUserName());
|
||||
} else {
|
||||
// If the ticket is not a proxy user, and the current user
|
||||
// is not the same as the ticket user, pass the current user
|
||||
// over to the server as the actual effective user.
|
||||
builder.setEffectiveUser(effectiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.setMethodName(call.md.getName());
|
||||
builder.setRequestParam(call.param != null);
|
||||
ByteBuffer cellBlock = ipcUtil.buildCellBlock(this.codec, this.compressor, call.cells);
|
||||
|
|
|
@ -2446,6 +2446,10 @@ public final class RPCProtos {
|
|||
boolean hasCellBlockMeta();
|
||||
org.apache.hadoop.hbase.protobuf.generated.RPCProtos.CellBlockMeta getCellBlockMeta();
|
||||
org.apache.hadoop.hbase.protobuf.generated.RPCProtos.CellBlockMetaOrBuilder getCellBlockMetaOrBuilder();
|
||||
|
||||
// optional string effective_user = 6;
|
||||
boolean hasEffectiveUser();
|
||||
String getEffectiveUser();
|
||||
}
|
||||
public static final class RequestHeader extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
|
@ -2554,12 +2558,45 @@ public final class RPCProtos {
|
|||
return cellBlockMeta_;
|
||||
}
|
||||
|
||||
// optional string effective_user = 6;
|
||||
public static final int EFFECTIVE_USER_FIELD_NUMBER = 6;
|
||||
private java.lang.Object effectiveUser_;
|
||||
public boolean hasEffectiveUser() {
|
||||
return ((bitField0_ & 0x00000020) == 0x00000020);
|
||||
}
|
||||
public String getEffectiveUser() {
|
||||
java.lang.Object ref = effectiveUser_;
|
||||
if (ref instanceof String) {
|
||||
return (String) ref;
|
||||
} else {
|
||||
com.google.protobuf.ByteString bs =
|
||||
(com.google.protobuf.ByteString) ref;
|
||||
String s = bs.toStringUtf8();
|
||||
if (com.google.protobuf.Internal.isValidUtf8(bs)) {
|
||||
effectiveUser_ = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
private com.google.protobuf.ByteString getEffectiveUserBytes() {
|
||||
java.lang.Object ref = effectiveUser_;
|
||||
if (ref instanceof String) {
|
||||
com.google.protobuf.ByteString b =
|
||||
com.google.protobuf.ByteString.copyFromUtf8((String) ref);
|
||||
effectiveUser_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
callId_ = 0;
|
||||
traceInfo_ = org.apache.hadoop.hbase.protobuf.generated.Tracing.RPCTInfo.getDefaultInstance();
|
||||
methodName_ = "";
|
||||
requestParam_ = false;
|
||||
cellBlockMeta_ = org.apache.hadoop.hbase.protobuf.generated.RPCProtos.CellBlockMeta.getDefaultInstance();
|
||||
effectiveUser_ = "";
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
|
@ -2588,6 +2625,9 @@ public final class RPCProtos {
|
|||
if (((bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
output.writeMessage(5, cellBlockMeta_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
output.writeBytes(6, getEffectiveUserBytes());
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
|
@ -2617,6 +2657,10 @@ public final class RPCProtos {
|
|||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(5, cellBlockMeta_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(6, getEffectiveUserBytes());
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
|
@ -2665,6 +2709,11 @@ public final class RPCProtos {
|
|||
result = result && getCellBlockMeta()
|
||||
.equals(other.getCellBlockMeta());
|
||||
}
|
||||
result = result && (hasEffectiveUser() == other.hasEffectiveUser());
|
||||
if (hasEffectiveUser()) {
|
||||
result = result && getEffectiveUser()
|
||||
.equals(other.getEffectiveUser());
|
||||
}
|
||||
result = result &&
|
||||
getUnknownFields().equals(other.getUnknownFields());
|
||||
return result;
|
||||
|
@ -2694,6 +2743,10 @@ public final class RPCProtos {
|
|||
hash = (37 * hash) + CELL_BLOCK_META_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getCellBlockMeta().hashCode();
|
||||
}
|
||||
if (hasEffectiveUser()) {
|
||||
hash = (37 * hash) + EFFECTIVE_USER_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getEffectiveUser().hashCode();
|
||||
}
|
||||
hash = (29 * hash) + getUnknownFields().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
@ -2830,6 +2883,8 @@ public final class RPCProtos {
|
|||
cellBlockMetaBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
effectiveUser_ = "";
|
||||
bitField0_ = (bitField0_ & ~0x00000020);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -2896,6 +2951,10 @@ public final class RPCProtos {
|
|||
} else {
|
||||
result.cellBlockMeta_ = cellBlockMetaBuilder_.build();
|
||||
}
|
||||
if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
to_bitField0_ |= 0x00000020;
|
||||
}
|
||||
result.effectiveUser_ = effectiveUser_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
|
@ -2927,6 +2986,9 @@ public final class RPCProtos {
|
|||
if (other.hasCellBlockMeta()) {
|
||||
mergeCellBlockMeta(other.getCellBlockMeta());
|
||||
}
|
||||
if (other.hasEffectiveUser()) {
|
||||
setEffectiveUser(other.getEffectiveUser());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
|
@ -2991,6 +3053,11 @@ public final class RPCProtos {
|
|||
setCellBlockMeta(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
case 50: {
|
||||
bitField0_ |= 0x00000020;
|
||||
effectiveUser_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3255,6 +3322,42 @@ public final class RPCProtos {
|
|||
return cellBlockMetaBuilder_;
|
||||
}
|
||||
|
||||
// optional string effective_user = 6;
|
||||
private java.lang.Object effectiveUser_ = "";
|
||||
public boolean hasEffectiveUser() {
|
||||
return ((bitField0_ & 0x00000020) == 0x00000020);
|
||||
}
|
||||
public String getEffectiveUser() {
|
||||
java.lang.Object ref = effectiveUser_;
|
||||
if (!(ref instanceof String)) {
|
||||
String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
|
||||
effectiveUser_ = s;
|
||||
return s;
|
||||
} else {
|
||||
return (String) ref;
|
||||
}
|
||||
}
|
||||
public Builder setEffectiveUser(String value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000020;
|
||||
effectiveUser_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
public Builder clearEffectiveUser() {
|
||||
bitField0_ = (bitField0_ & ~0x00000020);
|
||||
effectiveUser_ = getDefaultInstance().getEffectiveUser();
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
void setEffectiveUser(com.google.protobuf.ByteString value) {
|
||||
bitField0_ |= 0x00000020;
|
||||
effectiveUser_ = value;
|
||||
onChanged();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:RequestHeader)
|
||||
}
|
||||
|
||||
|
@ -3991,15 +4094,16 @@ public final class RPCProtos {
|
|||
"th\030\001 \001(\r\"|\n\021ExceptionResponse\022\034\n\024excepti" +
|
||||
"on_class_name\030\001 \001(\t\022\023\n\013stack_trace\030\002 \001(\t",
|
||||
"\022\020\n\010hostname\030\003 \001(\t\022\014\n\004port\030\004 \001(\005\022\024\n\014do_n" +
|
||||
"ot_retry\030\005 \001(\010\"\224\001\n\rRequestHeader\022\017\n\007call" +
|
||||
"ot_retry\030\005 \001(\010\"\254\001\n\rRequestHeader\022\017\n\007call" +
|
||||
"_id\030\001 \001(\r\022\035\n\ntrace_info\030\002 \001(\0132\t.RPCTInfo" +
|
||||
"\022\023\n\013method_name\030\003 \001(\t\022\025\n\rrequest_param\030\004" +
|
||||
" \001(\010\022\'\n\017cell_block_meta\030\005 \001(\0132\016.CellBloc" +
|
||||
"kMeta\"q\n\016ResponseHeader\022\017\n\007call_id\030\001 \001(\r" +
|
||||
"\022%\n\texception\030\002 \001(\0132\022.ExceptionResponse\022" +
|
||||
"\'\n\017cell_block_meta\030\003 \001(\0132\016.CellBlockMeta" +
|
||||
"B<\n*org.apache.hadoop.hbase.protobuf.gen" +
|
||||
"eratedB\tRPCProtosH\001\240\001\001"
|
||||
"kMeta\022\026\n\016effective_user\030\006 \001(\t\"q\n\016Respons" +
|
||||
"eHeader\022\017\n\007call_id\030\001 \001(\r\022%\n\texception\030\002 " +
|
||||
"\001(\0132\022.ExceptionResponse\022\'\n\017cell_block_me" +
|
||||
"ta\030\003 \001(\0132\016.CellBlockMetaB<\n*org.apache.h" +
|
||||
"adoop.hbase.protobuf.generatedB\tRPCProto",
|
||||
"sH\001\240\001\001"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
|
@ -4043,7 +4147,7 @@ public final class RPCProtos {
|
|||
internal_static_RequestHeader_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_RequestHeader_descriptor,
|
||||
new java.lang.String[] { "CallId", "TraceInfo", "MethodName", "RequestParam", "CellBlockMeta", },
|
||||
new java.lang.String[] { "CallId", "TraceInfo", "MethodName", "RequestParam", "CellBlockMeta", "EffectiveUser", },
|
||||
org.apache.hadoop.hbase.protobuf.generated.RPCProtos.RequestHeader.class,
|
||||
org.apache.hadoop.hbase.protobuf.generated.RPCProtos.RequestHeader.Builder.class);
|
||||
internal_static_ResponseHeader_descriptor =
|
||||
|
|
|
@ -119,6 +119,8 @@ message RequestHeader {
|
|||
optional bool request_param = 4;
|
||||
// If present, then an encoded data block follows.
|
||||
optional CellBlockMeta cell_block_meta = 5;
|
||||
// If present, the request is made on behalf of this user
|
||||
optional string effective_user = 6;
|
||||
// TODO: Have client specify priority
|
||||
}
|
||||
|
||||
|
|
|
@ -387,6 +387,10 @@
|
|||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-sslengine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jsp-2.1</artifactId>
|
||||
|
|
|
@ -272,10 +272,11 @@ public class RpcServer implements RpcServerInterface {
|
|||
protected long size; // size of current call
|
||||
protected boolean isError;
|
||||
protected TraceInfo tinfo;
|
||||
protected String effectiveUser;
|
||||
|
||||
Call(int id, final BlockingService service, final MethodDescriptor md, Message param,
|
||||
CellScanner cellScanner, Connection connection, Responder responder, long size,
|
||||
TraceInfo tinfo) {
|
||||
TraceInfo tinfo, String effectiveUser) {
|
||||
this.id = id;
|
||||
this.service = service;
|
||||
this.md = md;
|
||||
|
@ -289,6 +290,7 @@ public class RpcServer implements RpcServerInterface {
|
|||
this.isError = false;
|
||||
this.size = size;
|
||||
this.tinfo = tinfo;
|
||||
this.effectiveUser = effectiveUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1141,13 +1143,14 @@ public class RpcServer implements RpcServerInterface {
|
|||
// Fake 'call' for failed authorization response
|
||||
private static final int AUTHROIZATION_FAILED_CALLID = -1;
|
||||
private final Call authFailedCall =
|
||||
new Call(AUTHROIZATION_FAILED_CALLID, this.service, null, null, null, this, null, 0, null);
|
||||
new Call(AUTHROIZATION_FAILED_CALLID, this.service, null,
|
||||
null, null, this, null, 0, null, null);
|
||||
private ByteArrayOutputStream authFailedResponse =
|
||||
new ByteArrayOutputStream();
|
||||
// Fake 'call' for SASL context setup
|
||||
private static final int SASL_CALLID = -33;
|
||||
private final Call saslCall =
|
||||
new Call(SASL_CALLID, this.service, null, null, null, this, null, 0, null);
|
||||
new Call(SASL_CALLID, this.service, null, null, null, this, null, 0, null, null);
|
||||
|
||||
public UserGroupInformation attemptingUser = null; // user name before auth
|
||||
|
||||
|
@ -1500,7 +1503,7 @@ public class RpcServer implements RpcServerInterface {
|
|||
|
||||
private int doBadPreambleHandling(final String msg, final Exception e) throws IOException {
|
||||
LOG.warn(msg);
|
||||
Call fakeCall = new Call(-1, null, null, null, null, this, responder, -1, null);
|
||||
Call fakeCall = new Call(-1, null, null, null, null, this, responder, -1, null, null);
|
||||
setupResponse(null, fakeCall, e, msg);
|
||||
responder.doRespond(fakeCall);
|
||||
// Returning -1 closes out the connection.
|
||||
|
@ -1650,7 +1653,8 @@ public class RpcServer implements RpcServerInterface {
|
|||
// This is a bit late to be doing this check - we have already read in the total request.
|
||||
if ((totalRequestSize + callQueueSize.get()) > maxQueueSize) {
|
||||
final Call callTooBig =
|
||||
new Call(id, this.service, null, null, null, this, responder, totalRequestSize, null);
|
||||
new Call(id, this.service, null, null, null, this,
|
||||
responder, totalRequestSize, null, null);
|
||||
ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
|
||||
setupResponse(responseBuffer, callTooBig, new CallQueueTooBigException(),
|
||||
"Call queue is full, is ipc.server.max.callqueue.size too small?");
|
||||
|
@ -1660,6 +1664,7 @@ public class RpcServer implements RpcServerInterface {
|
|||
MethodDescriptor md = null;
|
||||
Message param = null;
|
||||
CellScanner cellScanner = null;
|
||||
String effectiveUser = null;
|
||||
try {
|
||||
if (header.hasRequestParam() && header.getRequestParam()) {
|
||||
md = this.service.getDescriptorForType().findMethodByName(header.getMethodName());
|
||||
|
@ -1677,11 +1682,15 @@ public class RpcServer implements RpcServerInterface {
|
|||
cellScanner = ipcUtil.createCellScanner(this.codec, this.compressionCodec,
|
||||
buf, offset, buf.length);
|
||||
}
|
||||
if (header.hasEffectiveUser()) {
|
||||
effectiveUser = header.getEffectiveUser();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
String msg = "Unable to read call parameter from client " + getHostAddress();
|
||||
LOG.warn(msg, t);
|
||||
final Call readParamsFailedCall =
|
||||
new Call(id, this.service, null, null, null, this, responder, totalRequestSize, null);
|
||||
new Call(id, this.service, null, null, null, this,
|
||||
responder, totalRequestSize, null, null);
|
||||
ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
|
||||
setupResponse(responseBuffer, readParamsFailedCall, t,
|
||||
msg + "; " + t.getMessage());
|
||||
|
@ -1691,14 +1700,16 @@ public class RpcServer implements RpcServerInterface {
|
|||
|
||||
Call call = null;
|
||||
if (header.hasTraceInfo()) {
|
||||
call = new Call(id, this.service, md, param, cellScanner, this, responder, totalRequestSize,
|
||||
new TraceInfo(header.getTraceInfo().getTraceId(), header.getTraceInfo().getParentId()));
|
||||
call = new Call(id, this.service, md, param, cellScanner, this,
|
||||
responder, totalRequestSize, new TraceInfo(header.getTraceInfo().getTraceId(),
|
||||
header.getTraceInfo().getParentId()), effectiveUser);
|
||||
} else {
|
||||
call = new Call(id, this.service, md, param, cellScanner, this, responder,
|
||||
totalRequestSize, null);
|
||||
totalRequestSize, null, effectiveUser);
|
||||
}
|
||||
callQueueSize.add(totalRequestSize);
|
||||
Pair<RequestHeader, Message> headerAndParam = new Pair<RequestHeader, Message>(header, param);
|
||||
Pair<RequestHeader, Message> headerAndParam =
|
||||
new Pair<RequestHeader, Message>(header, param);
|
||||
if (priorityCallQueue != null && getQosLevel(headerAndParam) > highPriorityLevel) {
|
||||
priorityCallQueue.put(call);
|
||||
} else if (replicationQueue != null &&
|
||||
|
@ -1824,8 +1835,20 @@ public class RpcServer implements RpcServerInterface {
|
|||
currentRequestSpan = Trace.startSpan(
|
||||
"handling " + call.toShortString(), call.tinfo, Sampler.ALWAYS);
|
||||
}
|
||||
RequestContext.set(User.create(call.connection.user), getRemoteIp(),
|
||||
call.connection.service);
|
||||
User user;
|
||||
if (call.effectiveUser == null) {
|
||||
user = User.create(call.connection.user);
|
||||
} else {
|
||||
UserGroupInformation ugi = UserGroupInformation.createProxyUser(
|
||||
call.effectiveUser, call.connection.user);
|
||||
ProxyUsers.authorize(ugi, call.connection.getHostAddress(), conf);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Authorized " + call.connection.user
|
||||
+ " to impersonate " + call.effectiveUser);
|
||||
}
|
||||
user = User.create(ugi);
|
||||
}
|
||||
RequestContext.set(user, getRemoteIp(), call.connection.service);
|
||||
|
||||
// make the call
|
||||
resultPair = call(call.service, call.md, call.param, call.cellScanner, call.timestamp,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/*
|
||||
*
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
|
@ -43,4 +42,19 @@ public interface Constants {
|
|||
String MIMETYPE_JSON = "application/json";
|
||||
|
||||
String CRLF = "\r\n";
|
||||
|
||||
static final String REST_KEYTAB_FILE = "hbase.rest.keytab.file";
|
||||
static final String REST_KERBEROS_PRINCIPAL = "hbase.rest.kerberos.principal";
|
||||
static final String REST_AUTHENTICATION_TYPE = "hbase.rest.authentication.type";
|
||||
static final String REST_AUTHENTICATION_PRINCIPAL =
|
||||
"hbase.rest.authentication.kerberos.principal";
|
||||
|
||||
static final String REST_SSL_ENABLED = "hbase.rest.ssl.enabled";
|
||||
static final String REST_SSL_KEYSTORE_STORE = "hbase.rest.ssl.keystore.store";
|
||||
static final String REST_SSL_KEYSTORE_PASSWORD = "hbase.rest.ssl.keystore.password";
|
||||
static final String REST_SSL_KEYSTORE_KEYPASSWORD =
|
||||
"hbase.rest.ssl.keystore.keypassword";
|
||||
|
||||
static final String REST_DNS_NAMESERVER = "hbase.rest.dns.nameserver";
|
||||
static final String REST_DNS_INTERFACE = "hbase.rest.dns.interface";
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
package org.apache.hadoop.hbase.rest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -35,6 +34,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||
import org.apache.hadoop.hbase.rest.filter.AuthFilter;
|
||||
import org.apache.hadoop.hbase.rest.filter.GzipFilter;
|
||||
import org.apache.hadoop.hbase.security.User;
|
||||
import org.apache.hadoop.hbase.util.InfoServer;
|
||||
|
@ -42,10 +42,11 @@ import org.apache.hadoop.hbase.util.Strings;
|
|||
import org.apache.hadoop.hbase.util.VersionInfo;
|
||||
import org.apache.hadoop.net.DNS;
|
||||
import org.apache.hadoop.security.SecurityUtil;
|
||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.mortbay.jetty.Connector;
|
||||
import org.mortbay.jetty.Server;
|
||||
import org.mortbay.jetty.nio.SelectChannelConnector;
|
||||
import org.mortbay.jetty.security.SslSelectChannelConnector;
|
||||
import org.mortbay.jetty.servlet.Context;
|
||||
import org.mortbay.jetty.servlet.FilterHolder;
|
||||
import org.mortbay.jetty.servlet.ServletHolder;
|
||||
|
@ -67,11 +68,6 @@ import com.sun.jersey.spi.container.servlet.ServletContainer;
|
|||
@InterfaceAudience.Private
|
||||
public class RESTServer implements Constants {
|
||||
|
||||
private static final String REST_SECURITY_KEY = "hbase.rest.authentication";
|
||||
private static final String REST_KEYTAB_KEY = "hbase.rest.keytab.file";
|
||||
private static final String REST_SPNEGO_PRINCIPAL_KEY = "hbase.rest.kerberos.spnego.principal";
|
||||
private static final String REST_PRINCIPAL_KEY = "hbase.rest.kerberos.principal";
|
||||
|
||||
private static void printUsageAndExit(Options options, int exitCode) {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp("bin/hbase rest start", "", options,
|
||||
|
@ -90,35 +86,35 @@ public class RESTServer implements Constants {
|
|||
|
||||
VersionInfo.logVersion();
|
||||
FilterHolder authFilter = null;
|
||||
UserGroupInformation realUser = null;
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
// login the server principal (if using secure Hadoop)
|
||||
Class<? extends ServletContainer> containerClass = ServletContainer.class;
|
||||
|
||||
// login the server principal (if using secure Hadoop)
|
||||
if (User.isSecurityEnabled() && User.isHBaseSecurityEnabled(conf)) {
|
||||
String machineName = Strings.domainNamePointerToHostName(
|
||||
DNS.getDefaultHost(conf.get("hbase.rest.dns.interface", "default"),
|
||||
conf.get("hbase.rest.dns.nameserver", "default")));
|
||||
User.login(conf, REST_KEYTAB_KEY, REST_PRINCIPAL_KEY,
|
||||
machineName);
|
||||
if ("kerberos".equalsIgnoreCase(conf.get(REST_SECURITY_KEY))) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
String principalInConf = conf.get(REST_SPNEGO_PRINCIPAL_KEY);
|
||||
Preconditions.checkArgument(principalInConf != null && !principalInConf.isEmpty(),
|
||||
REST_SPNEGO_PRINCIPAL_KEY + " should be set if authentication is enabled");
|
||||
params.put("kerberos.principal",
|
||||
SecurityUtil.getServerPrincipal(principalInConf, machineName));
|
||||
String httpKeytab = conf.get(REST_KEYTAB_KEY);
|
||||
Preconditions.checkArgument(httpKeytab != null && !httpKeytab.isEmpty(),
|
||||
REST_KEYTAB_KEY + " should be set if authentication is enabled");
|
||||
params.put("kerberos.keytab", httpKeytab);
|
||||
params.put(AuthenticationFilter.AUTH_TYPE, "kerberos");
|
||||
params.put(AuthenticationFilter.COOKIE_PATH, "/");
|
||||
DNS.getDefaultHost(conf.get(REST_DNS_INTERFACE, "default"),
|
||||
conf.get(REST_DNS_NAMESERVER, "default")));
|
||||
String keytabFilename = conf.get(REST_KEYTAB_FILE);
|
||||
Preconditions.checkArgument(keytabFilename != null && !keytabFilename.isEmpty(),
|
||||
REST_KEYTAB_FILE + " should be set if security is enabled");
|
||||
String principalConfig = conf.get(REST_KERBEROS_PRINCIPAL);
|
||||
Preconditions.checkArgument(principalConfig != null && !principalConfig.isEmpty(),
|
||||
REST_KERBEROS_PRINCIPAL + " should be set if security is enabled");
|
||||
String principalName = SecurityUtil.getServerPrincipal(principalConfig, machineName);
|
||||
UserGroupInformation loginUser =
|
||||
UserGroupInformation.loginUserFromKeytabAndReturnUGI(
|
||||
principalName, keytabFilename);
|
||||
if (conf.get(REST_AUTHENTICATION_TYPE) != null) {
|
||||
containerClass = RESTServletContainer.class;
|
||||
authFilter = new FilterHolder();
|
||||
authFilter.setClassName(AuthFilter.class.getName());
|
||||
authFilter.setName("AuthenticationFilter");
|
||||
authFilter.setClassName(AuthenticationFilter.class.getName());
|
||||
authFilter.setInitParameters(params);
|
||||
realUser = loginUser;
|
||||
}
|
||||
}
|
||||
|
||||
RESTServlet servlet = RESTServlet.getInstance(conf);
|
||||
RESTServlet servlet = RESTServlet.getInstance(conf, realUser);
|
||||
|
||||
Options options = new Options();
|
||||
options.addOption("p", "port", true, "Port to bind to [default: 8080]");
|
||||
|
@ -173,7 +169,7 @@ public class RESTServer implements Constants {
|
|||
}
|
||||
|
||||
// set up the Jersey servlet container for Jetty
|
||||
ServletHolder sh = new ServletHolder(ServletContainer.class);
|
||||
ServletHolder sh = new ServletHolder(containerClass);
|
||||
sh.setInitParameter(
|
||||
"com.sun.jersey.config.property.resourceConfigClass",
|
||||
ResourceConfig.class.getCanonicalName());
|
||||
|
@ -181,12 +177,12 @@ public class RESTServer implements Constants {
|
|||
"jetty");
|
||||
// The servlet holder below is instantiated to only handle the case
|
||||
// of the /status/cluster returning arrays of nodes (live/dead). Without
|
||||
// this servlet holder, the problem is that the node arrays in the response
|
||||
// are collapsed to single nodes. We want to be able to treat the
|
||||
// node lists as POJO in the response to /status/cluster servlet call,
|
||||
// this servlet holder, the problem is that the node arrays in the response
|
||||
// are collapsed to single nodes. We want to be able to treat the
|
||||
// node lists as POJO in the response to /status/cluster servlet call,
|
||||
// but not change the behavior for any of the other servlets
|
||||
// Hence we don't use the servlet holder for all servlets / paths
|
||||
ServletHolder shPojoMap = new ServletHolder(ServletContainer.class);
|
||||
ServletHolder shPojoMap = new ServletHolder(containerClass);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> shInitMap = sh.getInitParameters();
|
||||
for (Entry<String, String> e : shInitMap.entrySet()) {
|
||||
|
@ -199,6 +195,16 @@ public class RESTServer implements Constants {
|
|||
Server server = new Server();
|
||||
|
||||
Connector connector = new SelectChannelConnector();
|
||||
if(conf.getBoolean(REST_SSL_ENABLED, false)) {
|
||||
SslSelectChannelConnector sslConnector = new SslSelectChannelConnector();
|
||||
String keystore = conf.get(REST_SSL_KEYSTORE_STORE);
|
||||
String password = conf.get(REST_SSL_KEYSTORE_PASSWORD);
|
||||
String keyPassword = conf.get(REST_SSL_KEYSTORE_KEYPASSWORD, password);
|
||||
sslConnector.setKeystore(keystore);
|
||||
sslConnector.setPassword(password);
|
||||
sslConnector.setKeyPassword(keyPassword);
|
||||
connector = sslConnector;
|
||||
}
|
||||
connector.setPort(servlet.getConfiguration().getInt("hbase.rest.port", 8080));
|
||||
connector.setHost(servlet.getConfiguration().get("hbase.rest.host", "0.0.0.0"));
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/*
|
||||
*
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
|
@ -20,11 +19,16 @@
|
|||
package org.apache.hadoop.hbase.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.client.HBaseAdmin;
|
||||
import org.apache.hadoop.hbase.client.HConnectionManager;
|
||||
import org.apache.hadoop.hbase.client.HConnectionWrapper;
|
||||
import org.apache.hadoop.hbase.client.HTableInterface;
|
||||
import org.apache.hadoop.hbase.client.HTablePool;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
|
||||
/**
|
||||
* Singleton class encapsulating global REST servlet state and functions.
|
||||
|
@ -36,6 +40,7 @@ public class RESTServlet implements Constants {
|
|||
private final HTablePool pool;
|
||||
private final MetricsREST metrics = new MetricsREST();
|
||||
private final HBaseAdmin admin;
|
||||
private final UserGroupInformation ugi;
|
||||
|
||||
/**
|
||||
* @return the RESTServlet singleton instance
|
||||
|
@ -53,8 +58,13 @@ public class RESTServlet implements Constants {
|
|||
*/
|
||||
public synchronized static RESTServlet getInstance(Configuration conf)
|
||||
throws IOException {
|
||||
return getInstance(conf, null);
|
||||
}
|
||||
|
||||
public synchronized static RESTServlet getInstance(Configuration conf,
|
||||
UserGroupInformation ugi) throws IOException {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new RESTServlet(conf);
|
||||
INSTANCE = new RESTServlet(conf, ugi);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
@ -68,11 +78,44 @@ public class RESTServlet implements Constants {
|
|||
* @param conf existing configuration
|
||||
* @throws IOException
|
||||
*/
|
||||
RESTServlet(Configuration conf) throws IOException {
|
||||
RESTServlet(final Configuration conf,
|
||||
final UserGroupInformation ugi) throws IOException {
|
||||
this.conf = conf;
|
||||
this.ugi = ugi;
|
||||
int maxSize = conf.getInt("hbase.rest.htablepool.size", 10);
|
||||
this.pool = new HTablePool(conf, maxSize);
|
||||
this.admin = new HBaseAdmin(conf);
|
||||
if (ugi == null) {
|
||||
pool = new HTablePool(conf, maxSize);
|
||||
admin = new HBaseAdmin(conf);
|
||||
} else {
|
||||
admin = new HBaseAdmin(new HConnectionWrapper(ugi,
|
||||
HConnectionManager.getConnection(new Configuration(conf))));
|
||||
|
||||
pool = new HTablePool(conf, maxSize) {
|
||||
/**
|
||||
* A HTablePool adapter. It makes sure the real user is
|
||||
* always used in creating any table so that the HConnection
|
||||
* is not any proxy user in case impersonation with
|
||||
* RESTServletContainer.
|
||||
*/
|
||||
@Override
|
||||
protected HTableInterface createHTable(final String tableName) {
|
||||
return ugi.doAs(new PrivilegedAction<HTableInterface>() {
|
||||
@Override
|
||||
public HTableInterface run() {
|
||||
return callCreateHTable(tableName);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method used to call super.createHTable.
|
||||
*/
|
||||
HTableInterface callCreateHTable(final String tableName) {
|
||||
return super.createHTable(tableName);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
HBaseAdmin getAdmin() {
|
||||
|
@ -99,4 +142,8 @@ public class RESTServlet implements Constants {
|
|||
boolean isReadOnly() {
|
||||
return getConfiguration().getBoolean("hbase.rest.readonly", false);
|
||||
}
|
||||
|
||||
UserGroupInformation getUser() {
|
||||
return ugi;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.hbase.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
|
||||
import com.sun.jersey.spi.container.servlet.ServletContainer;
|
||||
|
||||
/**
|
||||
* REST servlet container. It is used to get the remote request user
|
||||
* without going through @HttpContext, so that we can minimize code changes.
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class RESTServletContainer extends ServletContainer {
|
||||
private static final long serialVersionUID = -2474255003443394314L;
|
||||
|
||||
/**
|
||||
* This container is used only if authentication and
|
||||
* impersonation is enabled. The remote request user is used
|
||||
* as a proxy user for impersonation in invoking any REST service.
|
||||
*/
|
||||
@Override
|
||||
public void service(final HttpServletRequest request,
|
||||
final HttpServletResponse response) throws ServletException, IOException {
|
||||
UserGroupInformation effectiveUser = UserGroupInformation.createProxyUser(
|
||||
request.getRemoteUser(), RESTServlet.getInstance().getUser());
|
||||
try {
|
||||
effectiveUser.doAs(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws ServletException, IOException {
|
||||
callService(request, response);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method used to invoke super.service()
|
||||
* on behalf of an effective user with doAs.
|
||||
*/
|
||||
void callService(HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException {
|
||||
super.service(request, response);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hbase.rest.filter;
|
||||
|
||||
import static org.apache.hadoop.hbase.rest.Constants.REST_AUTHENTICATION_PRINCIPAL;
|
||||
import static org.apache.hadoop.hbase.rest.Constants.REST_DNS_INTERFACE;
|
||||
import static org.apache.hadoop.hbase.rest.Constants.REST_DNS_NAMESERVER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||
import org.apache.hadoop.hbase.util.Strings;
|
||||
import org.apache.hadoop.net.DNS;
|
||||
import org.apache.hadoop.security.SecurityUtil;
|
||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||
|
||||
public class AuthFilter extends AuthenticationFilter {
|
||||
private static final Log LOG = LogFactory.getLog(AuthFilter.class);
|
||||
private static final String REST_PREFIX = "hbase.rest.authentication.";
|
||||
private static final int REST_PREFIX_LEN = REST_PREFIX.length();
|
||||
|
||||
/**
|
||||
* Returns the configuration to be used by the authentication filter
|
||||
* to initialize the authentication handler.
|
||||
*
|
||||
* This filter retrieves all HBase configurations and passes those started
|
||||
* with REST_PREFIX to the authentication handler. It is useful to support
|
||||
* plugging different authentication handlers.
|
||||
*/
|
||||
@Override
|
||||
protected Properties getConfiguration(
|
||||
String configPrefix, FilterConfig filterConfig) throws ServletException {
|
||||
Properties props = super.getConfiguration(configPrefix, filterConfig);
|
||||
//setting the cookie path to root '/' so it is used for all resources.
|
||||
props.setProperty(AuthenticationFilter.COOKIE_PATH, "/");
|
||||
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
for (Map.Entry<String, String> entry : conf) {
|
||||
String name = entry.getKey();
|
||||
if (name.startsWith(REST_PREFIX)) {
|
||||
String value = entry.getValue();
|
||||
if(name.equals(REST_AUTHENTICATION_PRINCIPAL)) {
|
||||
try {
|
||||
String machineName = Strings.domainNamePointerToHostName(
|
||||
DNS.getDefaultHost(conf.get(REST_DNS_INTERFACE, "default"),
|
||||
conf.get(REST_DNS_NAMESERVER, "default")));
|
||||
value = SecurityUtil.getServerPrincipal(value, machineName);
|
||||
} catch (IOException ie) {
|
||||
throw new ServletException("Failed to retrieve server principal", ie);
|
||||
}
|
||||
}
|
||||
LOG.debug("Setting property " + name + "=" + value);
|
||||
name = name.substring(REST_PREFIX_LEN);
|
||||
props.setProperty(name, value);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.apache.hadoop.hbase.protobuf.generated.MasterAdminProtos.MasterAdminS
|
|||
import org.apache.hadoop.hbase.protobuf.generated.MasterMonitorProtos.MasterMonitorService;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStatusService;
|
||||
import org.apache.hadoop.security.authorize.PolicyProvider;
|
||||
import org.apache.hadoop.security.authorize.ProxyUsers;
|
||||
import org.apache.hadoop.security.authorize.Service;
|
||||
import org.apache.hadoop.security.authorize.ServiceAuthorizationManager;
|
||||
|
||||
|
@ -50,6 +51,7 @@ public class HBasePolicyProvider extends PolicyProvider {
|
|||
System.setProperty("hadoop.policy.file", "hbase-policy.xml");
|
||||
if (conf.getBoolean(ServiceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, false)) {
|
||||
authManager.refresh(conf, new HBasePolicyProvider());
|
||||
ProxyUsers.refreshSuperUserGroupsConfiguration(conf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1939,7 +1939,7 @@ public class TestAccessController {
|
|||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
final HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
|
||||
HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
|
||||
HTableDescriptor htd = new HTableDescriptor(TEST_TABLE2);
|
||||
htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
|
||||
admin.createTable(htd);
|
||||
|
@ -1950,7 +1950,7 @@ public class TestAccessController {
|
|||
final HRegionServer newRs = newRsThread.getRegionServer();
|
||||
|
||||
// Move region to the new RegionServer.
|
||||
final HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE2);
|
||||
HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE2);
|
||||
try {
|
||||
NavigableMap<HRegionInfo, ServerName> regions = table
|
||||
.getRegionLocations();
|
||||
|
@ -1959,6 +1959,7 @@ public class TestAccessController {
|
|||
|
||||
PrivilegedExceptionAction moveAction = new PrivilegedExceptionAction() {
|
||||
public Object run() throws Exception {
|
||||
HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
|
||||
admin.move(firstRegion.getKey().getEncodedNameAsBytes(),
|
||||
Bytes.toBytes(newRs.getServerName().getServerName()));
|
||||
return null;
|
||||
|
@ -1984,6 +1985,7 @@ public class TestAccessController {
|
|||
// permissions.
|
||||
PrivilegedExceptionAction putAction = new PrivilegedExceptionAction() {
|
||||
public Object run() throws Exception {
|
||||
HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE2);
|
||||
Put put = new Put(Bytes.toBytes("test"));
|
||||
put.add(TEST_FAMILY, Bytes.toBytes("qual"), Bytes.toBytes("value"));
|
||||
table.put(put);
|
||||
|
|
5
pom.xml
5
pom.xml
|
@ -1153,6 +1153,11 @@
|
|||
<artifactId>jetty-util</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-sslengine</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jsp-2.1</artifactId>
|
||||
|
|
Loading…
Reference in New Issue