From 325cdc0987f8176ac46695f5b0c93b0fc6605ab9 Mon Sep 17 00:00:00 2001 From: Matteo Bertozzi Date: Fri, 21 Nov 2014 10:55:48 +0000 Subject: [PATCH] HBASE-12073 Shell command user_permission fails on the table created by user if he is not global admin (Srikanth Srungarapu) --- .../hbase/client/ConnectionAdapter.java | 5 + .../hbase/client/ConnectionManager.java | 14 + .../hadoop/hbase/client/HBaseAdmin.java | 9 +- .../hadoop/hbase/client/HConnection.java | 7 + .../hbase/protobuf/RequestConverter.java | 11 + .../protobuf/generated/MasterProtos.java | 384 +++++++++++++----- hbase-protocol/src/main/protobuf/Master.proto | 1 + .../BaseMasterAndRegionObserver.java | 11 + .../hbase/coprocessor/BaseMasterObserver.java | 26 +- .../hbase/coprocessor/MasterObserver.java | 35 +- .../hbase/master/MasterCoprocessorHost.java | 48 ++- .../hbase/master/MasterRpcServices.java | 28 +- .../security/access/AccessController.java | 66 ++- .../hbase/coprocessor/TestMasterObserver.java | 12 +- .../security/access/TestAccessController.java | 37 ++ 15 files changed, 535 insertions(+), 159 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionAdapter.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionAdapter.java index 927b56c1d67..80fa14d88ac 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionAdapter.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionAdapter.java @@ -179,6 +179,11 @@ class ConnectionAdapter implements ClusterConnection { return wrappedConnection.listTables(); } + @Override + public HTableDescriptor[] listTables(String regex) throws IOException { + return wrappedConnection.listTables(regex); + } + @Override public String[] getTableNames() throws IOException { return wrappedConnection.getTableNames(); diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java index b3a6295ea55..bfdf5d2a20d 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionManager.java @@ -2410,6 +2410,20 @@ class ConnectionManager { } } + @Override + public HTableDescriptor[] listTables(String regex) throws IOException { + MasterKeepAliveConnection master = getKeepAliveMasterService(); + try { + GetTableDescriptorsRequest req = + RequestConverter.buildGetTableDescriptorsRequest(regex); + return ProtobufUtil.getHTableDescriptorArray(master.getTableDescriptors(null, req)); + } catch (ServiceException se) { + throw ProtobufUtil.getRemoteException(se); + } finally { + master.close(); + } + } + @Override public String[] getTableNames() throws IOException { TableName[] tableNames = listTableNames(); diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java index e9764864bc5..329a3738b55 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java @@ -322,14 +322,7 @@ public class HBaseAdmin implements Admin { */ @Override public HTableDescriptor[] listTables(Pattern pattern) throws IOException { - List matched = new LinkedList(); - HTableDescriptor[] tables = listTables(); - for (HTableDescriptor table : tables) { - if (pattern.matcher(table.getTableName().getNameAsString()).matches()) { - matched.add(table); - } - } - return matched.toArray(new HTableDescriptor[matched.size()]); + return this.connection.listTables(pattern.toString()); } /** diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HConnection.java index b5328bf901c..4b9b1b01812 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HConnection.java @@ -261,6 +261,13 @@ public interface HConnection extends Connection { */ HTableDescriptor[] listTables() throws IOException; + /** + * List all the userspace tables matching the pattern. + * @return - returns an array of HTableDescriptors + * @throws IOException if a remote or network exception occurs + */ + HTableDescriptor[] listTables(String pattern) throws IOException; + // This is a bit ugly - We call this getTableNames in 0.94 and the // successor function, returning TableName, listTableNames in later versions // because Java polymorphism doesn't consider return value types diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java index 6be8d2d8c49..d558fce8548 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java @@ -1226,6 +1226,17 @@ public final class RequestConverter { return builder.build(); } + /** + * Creates a protocol buffer GetTableDescriptorsRequest + * + * @return a GetTableDescriptorsRequest + */ + public static GetTableDescriptorsRequest buildGetTableDescriptorsRequest(final String pattern) { + GetTableDescriptorsRequest.Builder builder = GetTableDescriptorsRequest.newBuilder(); + builder.setRegex(pattern); + return builder.build(); + } + /** * Creates a protocol buffer GetTableStateRequest * diff --git a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/MasterProtos.java b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/MasterProtos.java index ee581d84095..caeea871501 100644 --- a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/MasterProtos.java +++ b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/MasterProtos.java @@ -34181,6 +34181,21 @@ public final class MasterProtos { */ org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableNameOrBuilder getTableNamesOrBuilder( int index); + + // optional string regex = 2; + /** + * optional string regex = 2; + */ + boolean hasRegex(); + /** + * optional string regex = 2; + */ + java.lang.String getRegex(); + /** + * optional string regex = 2; + */ + com.google.protobuf.ByteString + getRegexBytes(); } /** * Protobuf type {@code GetTableDescriptorsRequest} @@ -34241,6 +34256,11 @@ public final class MasterProtos { tableNames_.add(input.readMessage(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableName.PARSER, extensionRegistry)); break; } + case 18: { + bitField0_ |= 0x00000001; + regex_ = input.readBytes(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -34283,6 +34303,7 @@ public final class MasterProtos { return PARSER; } + private int bitField0_; // repeated .TableName table_names = 1; public static final int TABLE_NAMES_FIELD_NUMBER = 1; private java.util.List tableNames_; @@ -34319,8 +34340,52 @@ public final class MasterProtos { return tableNames_.get(index); } + // optional string regex = 2; + public static final int REGEX_FIELD_NUMBER = 2; + private java.lang.Object regex_; + /** + * optional string regex = 2; + */ + public boolean hasRegex() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string regex = 2; + */ + public java.lang.String getRegex() { + java.lang.Object ref = regex_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + regex_ = s; + } + return s; + } + } + /** + * optional string regex = 2; + */ + public com.google.protobuf.ByteString + getRegexBytes() { + java.lang.Object ref = regex_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + regex_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private void initFields() { tableNames_ = java.util.Collections.emptyList(); + regex_ = ""; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -34343,6 +34408,9 @@ public final class MasterProtos { for (int i = 0; i < tableNames_.size(); i++) { output.writeMessage(1, tableNames_.get(i)); } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(2, getRegexBytes()); + } getUnknownFields().writeTo(output); } @@ -34356,6 +34424,10 @@ public final class MasterProtos { size += com.google.protobuf.CodedOutputStream .computeMessageSize(1, tableNames_.get(i)); } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getRegexBytes()); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -34381,6 +34453,11 @@ public final class MasterProtos { boolean result = true; result = result && getTableNamesList() .equals(other.getTableNamesList()); + result = result && (hasRegex() == other.hasRegex()); + if (hasRegex()) { + result = result && getRegex() + .equals(other.getRegex()); + } result = result && getUnknownFields().equals(other.getUnknownFields()); return result; @@ -34398,6 +34475,10 @@ public final class MasterProtos { hash = (37 * hash) + TABLE_NAMES_FIELD_NUMBER; hash = (53 * hash) + getTableNamesList().hashCode(); } + if (hasRegex()) { + hash = (37 * hash) + REGEX_FIELD_NUMBER; + hash = (53 * hash) + getRegex().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -34514,6 +34595,8 @@ public final class MasterProtos { } else { tableNamesBuilder_.clear(); } + regex_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); return this; } @@ -34541,6 +34624,7 @@ public final class MasterProtos { public org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest buildPartial() { org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest result = new org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest(this); int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; if (tableNamesBuilder_ == null) { if (((bitField0_ & 0x00000001) == 0x00000001)) { tableNames_ = java.util.Collections.unmodifiableList(tableNames_); @@ -34550,6 +34634,11 @@ public final class MasterProtos { } else { result.tableNames_ = tableNamesBuilder_.build(); } + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000001; + } + result.regex_ = regex_; + result.bitField0_ = to_bitField0_; onBuilt(); return result; } @@ -34591,6 +34680,11 @@ public final class MasterProtos { } } } + if (other.hasRegex()) { + bitField0_ |= 0x00000002; + regex_ = other.regex_; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -34864,6 +34958,80 @@ public final class MasterProtos { return tableNamesBuilder_; } + // optional string regex = 2; + private java.lang.Object regex_ = ""; + /** + * optional string regex = 2; + */ + public boolean hasRegex() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string regex = 2; + */ + public java.lang.String getRegex() { + java.lang.Object ref = regex_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + regex_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string regex = 2; + */ + public com.google.protobuf.ByteString + getRegexBytes() { + java.lang.Object ref = regex_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + regex_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string regex = 2; + */ + public Builder setRegex( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + regex_ = value; + onChanged(); + return this; + } + /** + * optional string regex = 2; + */ + public Builder clearRegex() { + bitField0_ = (bitField0_ & ~0x00000002); + regex_ = getDefaultInstance().getRegex(); + onChanged(); + return this; + } + /** + * optional string regex = 2; + */ + public Builder setRegexBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + regex_ = value; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:GetTableDescriptorsRequest) } @@ -47830,113 +47998,113 @@ public final class MasterProtos { "equest\022\036\n\ntable_name\030\001 \002(\0132\n.TableName\"T", "\n\034GetSchemaAlterStatusResponse\022\035\n\025yet_to" + "_update_regions\030\001 \001(\r\022\025\n\rtotal_regions\030\002" + - " \001(\r\"=\n\032GetTableDescriptorsRequest\022\037\n\013ta" + - "ble_names\030\001 \003(\0132\n.TableName\"A\n\033GetTableD" + - "escriptorsResponse\022\"\n\014table_schema\030\001 \003(\013" + - "2\014.TableSchema\"\026\n\024GetTableNamesRequest\"8" + - "\n\025GetTableNamesResponse\022\037\n\013table_names\030\001" + - " \003(\0132\n.TableName\"6\n\024GetTableStateRequest" + - "\022\036\n\ntable_name\030\001 \002(\0132\n.TableName\"9\n\025GetT" + - "ableStateResponse\022 \n\013table_state\030\001 \002(\0132\013", - ".TableState\"\031\n\027GetClusterStatusRequest\"B" + - "\n\030GetClusterStatusResponse\022&\n\016cluster_st" + - "atus\030\001 \002(\0132\016.ClusterStatus\"\030\n\026IsMasterRu" + - "nningRequest\"4\n\027IsMasterRunningResponse\022" + - "\031\n\021is_master_running\030\001 \002(\010\"@\n\024ExecProced" + - "ureRequest\022(\n\tprocedure\030\001 \002(\0132\025.Procedur" + - "eDescription\"F\n\025ExecProcedureResponse\022\030\n" + - "\020expected_timeout\030\001 \001(\003\022\023\n\013return_data\030\002" + - " \001(\014\"B\n\026IsProcedureDoneRequest\022(\n\tproced" + - "ure\030\001 \001(\0132\025.ProcedureDescription\"W\n\027IsPr", - "ocedureDoneResponse\022\023\n\004done\030\001 \001(\010:\005false" + - "\022\'\n\010snapshot\030\002 \001(\0132\025.ProcedureDescriptio" + - "n\"\273\001\n\017SetQuotaRequest\022\021\n\tuser_name\030\001 \001(\t" + - "\022\022\n\nuser_group\030\002 \001(\t\022\021\n\tnamespace\030\003 \001(\t\022" + - "\036\n\ntable_name\030\004 \001(\0132\n.TableName\022\022\n\nremov" + - "e_all\030\005 \001(\010\022\026\n\016bypass_globals\030\006 \001(\010\022\"\n\010t" + - "hrottle\030\007 \001(\0132\020.ThrottleRequest\"\022\n\020SetQu" + - "otaResponse2\346\030\n\rMasterService\022S\n\024GetSche" + - "maAlterStatus\022\034.GetSchemaAlterStatusRequ" + - "est\032\035.GetSchemaAlterStatusResponse\022P\n\023Ge", - "tTableDescriptors\022\033.GetTableDescriptorsR" + - "equest\032\034.GetTableDescriptorsResponse\022>\n\r" + - "GetTableNames\022\025.GetTableNamesRequest\032\026.G" + - "etTableNamesResponse\022G\n\020GetClusterStatus" + - "\022\030.GetClusterStatusRequest\032\031.GetClusterS" + - "tatusResponse\022D\n\017IsMasterRunning\022\027.IsMas" + - "terRunningRequest\032\030.IsMasterRunningRespo" + - "nse\0222\n\tAddColumn\022\021.AddColumnRequest\032\022.Ad" + - "dColumnResponse\022;\n\014DeleteColumn\022\024.Delete" + - "ColumnRequest\032\025.DeleteColumnResponse\022;\n\014", - "ModifyColumn\022\024.ModifyColumnRequest\032\025.Mod" + - "ifyColumnResponse\0225\n\nMoveRegion\022\022.MoveRe" + - "gionRequest\032\023.MoveRegionResponse\022Y\n\026Disp" + - "atchMergingRegions\022\036.DispatchMergingRegi" + - "onsRequest\032\037.DispatchMergingRegionsRespo" + - "nse\022;\n\014AssignRegion\022\024.AssignRegionReques" + - "t\032\025.AssignRegionResponse\022A\n\016UnassignRegi" + - "on\022\026.UnassignRegionRequest\032\027.UnassignReg" + - "ionResponse\022>\n\rOfflineRegion\022\025.OfflineRe" + - "gionRequest\032\026.OfflineRegionResponse\0228\n\013D", - "eleteTable\022\023.DeleteTableRequest\032\024.Delete" + - "TableResponse\022>\n\rtruncateTable\022\025.Truncat" + - "eTableRequest\032\026.TruncateTableResponse\0228\n" + - "\013EnableTable\022\023.EnableTableRequest\032\024.Enab" + - "leTableResponse\022;\n\014DisableTable\022\024.Disabl" + - "eTableRequest\032\025.DisableTableResponse\0228\n\013" + - "ModifyTable\022\023.ModifyTableRequest\032\024.Modif" + - "yTableResponse\0228\n\013CreateTable\022\023.CreateTa" + - "bleRequest\032\024.CreateTableResponse\022/\n\010Shut" + - "down\022\020.ShutdownRequest\032\021.ShutdownRespons", - "e\0225\n\nStopMaster\022\022.StopMasterRequest\032\023.St" + - "opMasterResponse\022,\n\007Balance\022\017.BalanceReq" + - "uest\032\020.BalanceResponse\022M\n\022SetBalancerRun" + - "ning\022\032.SetBalancerRunningRequest\032\033.SetBa" + - "lancerRunningResponse\022A\n\016RunCatalogScan\022" + - "\026.RunCatalogScanRequest\032\027.RunCatalogScan" + - "Response\022S\n\024EnableCatalogJanitor\022\034.Enabl" + - "eCatalogJanitorRequest\032\035.EnableCatalogJa" + - "nitorResponse\022\\\n\027IsCatalogJanitorEnabled" + - "\022\037.IsCatalogJanitorEnabledRequest\032 .IsCa", - "talogJanitorEnabledResponse\022L\n\021ExecMaste" + - "rService\022\032.CoprocessorServiceRequest\032\033.C" + - "oprocessorServiceResponse\022/\n\010Snapshot\022\020." + - "SnapshotRequest\032\021.SnapshotResponse\022V\n\025Ge" + - "tCompletedSnapshots\022\035.GetCompletedSnapsh" + - "otsRequest\032\036.GetCompletedSnapshotsRespon" + - "se\022A\n\016DeleteSnapshot\022\026.DeleteSnapshotReq" + - "uest\032\027.DeleteSnapshotResponse\022A\n\016IsSnaps" + - "hotDone\022\026.IsSnapshotDoneRequest\032\027.IsSnap" + - "shotDoneResponse\022D\n\017RestoreSnapshot\022\027.Re", - "storeSnapshotRequest\032\030.RestoreSnapshotRe" + - "sponse\022V\n\025IsRestoreSnapshotDone\022\035.IsRest" + - "oreSnapshotDoneRequest\032\036.IsRestoreSnapsh" + - "otDoneResponse\022>\n\rExecProcedure\022\025.ExecPr" + - "ocedureRequest\032\026.ExecProcedureResponse\022E" + - "\n\024ExecProcedureWithRet\022\025.ExecProcedureRe" + - "quest\032\026.ExecProcedureResponse\022D\n\017IsProce" + - "dureDone\022\027.IsProcedureDoneRequest\032\030.IsPr" + - "ocedureDoneResponse\022D\n\017ModifyNamespace\022\027" + - ".ModifyNamespaceRequest\032\030.ModifyNamespac", - "eResponse\022D\n\017CreateNamespace\022\027.CreateNam" + - "espaceRequest\032\030.CreateNamespaceResponse\022" + - "D\n\017DeleteNamespace\022\027.DeleteNamespaceRequ" + - "est\032\030.DeleteNamespaceResponse\022Y\n\026GetName" + - "spaceDescriptor\022\036.GetNamespaceDescriptor" + - "Request\032\037.GetNamespaceDescriptorResponse" + - "\022_\n\030ListNamespaceDescriptors\022 .ListNames" + - "paceDescriptorsRequest\032!.ListNamespaceDe" + - "scriptorsResponse\022t\n\037ListTableDescriptor" + - "sByNamespace\022\'.ListTableDescriptorsByNam", - "espaceRequest\032(.ListTableDescriptorsByNa" + - "mespaceResponse\022b\n\031ListTableNamesByNames" + - "pace\022!.ListTableNamesByNamespaceRequest\032" + - "\".ListTableNamesByNamespaceResponse\022>\n\rG" + - "etTableState\022\025.GetTableStateRequest\032\026.Ge" + - "tTableStateResponse\022/\n\010SetQuota\022\020.SetQuo" + - "taRequest\032\021.SetQuotaResponseBB\n*org.apac" + - "he.hadoop.hbase.protobuf.generatedB\014Mast" + - "erProtosH\001\210\001\001\240\001\001" + " \001(\r\"L\n\032GetTableDescriptorsRequest\022\037\n\013ta" + + "ble_names\030\001 \003(\0132\n.TableName\022\r\n\005regex\030\002 \001" + + "(\t\"A\n\033GetTableDescriptorsResponse\022\"\n\014tab" + + "le_schema\030\001 \003(\0132\014.TableSchema\"\026\n\024GetTabl" + + "eNamesRequest\"8\n\025GetTableNamesResponse\022\037" + + "\n\013table_names\030\001 \003(\0132\n.TableName\"6\n\024GetTa" + + "bleStateRequest\022\036\n\ntable_name\030\001 \002(\0132\n.Ta" + + "bleName\"9\n\025GetTableStateResponse\022 \n\013tabl", + "e_state\030\001 \002(\0132\013.TableState\"\031\n\027GetCluster" + + "StatusRequest\"B\n\030GetClusterStatusRespons" + + "e\022&\n\016cluster_status\030\001 \002(\0132\016.ClusterStatu" + + "s\"\030\n\026IsMasterRunningRequest\"4\n\027IsMasterR" + + "unningResponse\022\031\n\021is_master_running\030\001 \002(" + + "\010\"@\n\024ExecProcedureRequest\022(\n\tprocedure\030\001" + + " \002(\0132\025.ProcedureDescription\"F\n\025ExecProce" + + "dureResponse\022\030\n\020expected_timeout\030\001 \001(\003\022\023" + + "\n\013return_data\030\002 \001(\014\"B\n\026IsProcedureDoneRe" + + "quest\022(\n\tprocedure\030\001 \001(\0132\025.ProcedureDesc", + "ription\"W\n\027IsProcedureDoneResponse\022\023\n\004do" + + "ne\030\001 \001(\010:\005false\022\'\n\010snapshot\030\002 \001(\0132\025.Proc" + + "edureDescription\"\273\001\n\017SetQuotaRequest\022\021\n\t" + + "user_name\030\001 \001(\t\022\022\n\nuser_group\030\002 \001(\t\022\021\n\tn" + + "amespace\030\003 \001(\t\022\036\n\ntable_name\030\004 \001(\0132\n.Tab" + + "leName\022\022\n\nremove_all\030\005 \001(\010\022\026\n\016bypass_glo" + + "bals\030\006 \001(\010\022\"\n\010throttle\030\007 \001(\0132\020.ThrottleR" + + "equest\"\022\n\020SetQuotaResponse2\346\030\n\rMasterSer" + + "vice\022S\n\024GetSchemaAlterStatus\022\034.GetSchema" + + "AlterStatusRequest\032\035.GetSchemaAlterStatu", + "sResponse\022P\n\023GetTableDescriptors\022\033.GetTa" + + "bleDescriptorsRequest\032\034.GetTableDescript" + + "orsResponse\022>\n\rGetTableNames\022\025.GetTableN" + + "amesRequest\032\026.GetTableNamesResponse\022G\n\020G" + + "etClusterStatus\022\030.GetClusterStatusReques" + + "t\032\031.GetClusterStatusResponse\022D\n\017IsMaster" + + "Running\022\027.IsMasterRunningRequest\032\030.IsMas" + + "terRunningResponse\0222\n\tAddColumn\022\021.AddCol" + + "umnRequest\032\022.AddColumnResponse\022;\n\014Delete" + + "Column\022\024.DeleteColumnRequest\032\025.DeleteCol", + "umnResponse\022;\n\014ModifyColumn\022\024.ModifyColu" + + "mnRequest\032\025.ModifyColumnResponse\0225\n\nMove" + + "Region\022\022.MoveRegionRequest\032\023.MoveRegionR" + + "esponse\022Y\n\026DispatchMergingRegions\022\036.Disp" + + "atchMergingRegionsRequest\032\037.DispatchMerg" + + "ingRegionsResponse\022;\n\014AssignRegion\022\024.Ass" + + "ignRegionRequest\032\025.AssignRegionResponse\022" + + "A\n\016UnassignRegion\022\026.UnassignRegionReques" + + "t\032\027.UnassignRegionResponse\022>\n\rOfflineReg" + + "ion\022\025.OfflineRegionRequest\032\026.OfflineRegi", + "onResponse\0228\n\013DeleteTable\022\023.DeleteTableR" + + "equest\032\024.DeleteTableResponse\022>\n\rtruncate" + + "Table\022\025.TruncateTableRequest\032\026.TruncateT" + + "ableResponse\0228\n\013EnableTable\022\023.EnableTabl" + + "eRequest\032\024.EnableTableResponse\022;\n\014Disabl" + + "eTable\022\024.DisableTableRequest\032\025.DisableTa" + + "bleResponse\0228\n\013ModifyTable\022\023.ModifyTable" + + "Request\032\024.ModifyTableResponse\0228\n\013CreateT" + + "able\022\023.CreateTableRequest\032\024.CreateTableR" + + "esponse\022/\n\010Shutdown\022\020.ShutdownRequest\032\021.", + "ShutdownResponse\0225\n\nStopMaster\022\022.StopMas" + + "terRequest\032\023.StopMasterResponse\022,\n\007Balan" + + "ce\022\017.BalanceRequest\032\020.BalanceResponse\022M\n" + + "\022SetBalancerRunning\022\032.SetBalancerRunning" + + "Request\032\033.SetBalancerRunningResponse\022A\n\016" + + "RunCatalogScan\022\026.RunCatalogScanRequest\032\027" + + ".RunCatalogScanResponse\022S\n\024EnableCatalog" + + "Janitor\022\034.EnableCatalogJanitorRequest\032\035." + + "EnableCatalogJanitorResponse\022\\\n\027IsCatalo" + + "gJanitorEnabled\022\037.IsCatalogJanitorEnable", + "dRequest\032 .IsCatalogJanitorEnabledRespon" + + "se\022L\n\021ExecMasterService\022\032.CoprocessorSer" + + "viceRequest\032\033.CoprocessorServiceResponse" + + "\022/\n\010Snapshot\022\020.SnapshotRequest\032\021.Snapsho" + + "tResponse\022V\n\025GetCompletedSnapshots\022\035.Get" + + "CompletedSnapshotsRequest\032\036.GetCompleted" + + "SnapshotsResponse\022A\n\016DeleteSnapshot\022\026.De" + + "leteSnapshotRequest\032\027.DeleteSnapshotResp" + + "onse\022A\n\016IsSnapshotDone\022\026.IsSnapshotDoneR" + + "equest\032\027.IsSnapshotDoneResponse\022D\n\017Resto", + "reSnapshot\022\027.RestoreSnapshotRequest\032\030.Re" + + "storeSnapshotResponse\022V\n\025IsRestoreSnapsh" + + "otDone\022\035.IsRestoreSnapshotDoneRequest\032\036." + + "IsRestoreSnapshotDoneResponse\022>\n\rExecPro" + + "cedure\022\025.ExecProcedureRequest\032\026.ExecProc" + + "edureResponse\022E\n\024ExecProcedureWithRet\022\025." + + "ExecProcedureRequest\032\026.ExecProcedureResp" + + "onse\022D\n\017IsProcedureDone\022\027.IsProcedureDon" + + "eRequest\032\030.IsProcedureDoneResponse\022D\n\017Mo" + + "difyNamespace\022\027.ModifyNamespaceRequest\032\030", + ".ModifyNamespaceResponse\022D\n\017CreateNamesp" + + "ace\022\027.CreateNamespaceRequest\032\030.CreateNam" + + "espaceResponse\022D\n\017DeleteNamespace\022\027.Dele" + + "teNamespaceRequest\032\030.DeleteNamespaceResp" + + "onse\022Y\n\026GetNamespaceDescriptor\022\036.GetName" + + "spaceDescriptorRequest\032\037.GetNamespaceDes" + + "criptorResponse\022_\n\030ListNamespaceDescript" + + "ors\022 .ListNamespaceDescriptorsRequest\032!." + + "ListNamespaceDescriptorsResponse\022t\n\037List" + + "TableDescriptorsByNamespace\022\'.ListTableD", + "escriptorsByNamespaceRequest\032(.ListTable" + + "DescriptorsByNamespaceResponse\022b\n\031ListTa" + + "bleNamesByNamespace\022!.ListTableNamesByNa" + + "mespaceRequest\032\".ListTableNamesByNamespa" + + "ceResponse\022>\n\rGetTableState\022\025.GetTableSt" + + "ateRequest\032\026.GetTableStateResponse\022/\n\010Se" + + "tQuota\022\020.SetQuotaRequest\032\021.SetQuotaRespo" + + "nseBB\n*org.apache.hadoop.hbase.protobuf." + + "generatedB\014MasterProtosH\001\210\001\001\240\001\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -48368,7 +48536,7 @@ public final class MasterProtos { internal_static_GetTableDescriptorsRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_GetTableDescriptorsRequest_descriptor, - new java.lang.String[] { "TableNames", }); + new java.lang.String[] { "TableNames", "Regex", }); internal_static_GetTableDescriptorsResponse_descriptor = getDescriptor().getMessageTypes().get(71); internal_static_GetTableDescriptorsResponse_fieldAccessorTable = new diff --git a/hbase-protocol/src/main/protobuf/Master.proto b/hbase-protocol/src/main/protobuf/Master.proto index 1b282bf4f45..5d60e556140 100644 --- a/hbase-protocol/src/main/protobuf/Master.proto +++ b/hbase-protocol/src/main/protobuf/Master.proto @@ -314,6 +314,7 @@ message GetSchemaAlterStatusResponse { message GetTableDescriptorsRequest { repeated TableName table_names = 1; + optional string regex = 2; } message GetTableDescriptorsResponse { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java index a6b9d848b21..03bcb782b6e 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterAndRegionObserver.java @@ -412,11 +412,22 @@ public abstract class BaseMasterAndRegionObserver extends BaseRegionObserver throws IOException { } + @Override + public void preGetTableDescriptors(ObserverContext ctx, + List tableNamesList, List descriptors, String regex) + throws IOException { + } + @Override public void postGetTableDescriptors(ObserverContext ctx, List descriptors) throws IOException { } + @Override + public void postGetTableDescriptors(ObserverContext ctx, + List descriptors, String regex) throws IOException { + } + @Override public void preTableFlush(ObserverContext ctx, TableName tableName) throws IOException { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java index b09341d8bf2..dbe99f51f49 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java @@ -19,23 +19,23 @@ package org.apache.hadoop.hbase.coprocessor; -import org.apache.hadoop.hbase.classification.InterfaceAudience; -import org.apache.hadoop.hbase.classification.InterfaceStability; +import java.io.IOException; +import java.util.List; + +import org.apache.hadoop.hbase.CoprocessorEnvironment; import org.apache.hadoop.hbase.HBaseInterfaceAudience; -import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HRegionInfo; -import org.apache.hadoop.hbase.CoprocessorEnvironment; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.master.RegionPlan; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas; -import java.io.IOException; -import java.util.List; - @InterfaceAudience.LimitedPrivate({HBaseInterfaceAudience.COPROC, HBaseInterfaceAudience.CONFIG}) @InterfaceStability.Evolving public class BaseMasterObserver implements MasterObserver { @@ -401,7 +401,12 @@ public class BaseMasterObserver implements MasterObserver { @Override public void preGetTableDescriptors(ObserverContext ctx, - List tableNamesList, List descriptors) + List tableNamesList, List descriptors) throws IOException { + } + + @Override + public void preGetTableDescriptors(ObserverContext ctx, + List tableNamesList, List descriptors, String regex) throws IOException { } @@ -410,6 +415,11 @@ public class BaseMasterObserver implements MasterObserver { List descriptors) throws IOException { } + @Override + public void postGetTableDescriptors(ObserverContext ctx, + List descriptors, String regex) throws IOException { + } + @Override public void preTableFlush(ObserverContext ctx, TableName tableName) throws IOException { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java index e975d0f5460..3086cf60dd4 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java @@ -22,16 +22,16 @@ package org.apache.hadoop.hbase.coprocessor; import java.io.IOException; import java.util.List; -import org.apache.hadoop.hbase.classification.InterfaceAudience; -import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.Coprocessor; import org.apache.hadoop.hbase.HBaseInterfaceAudience; -import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.master.RegionPlan; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas; @@ -674,20 +674,45 @@ public interface MasterObserver extends Coprocessor { * @param tableNamesList the list of table names, or null if querying for all * @param descriptors an empty list, can be filled with what to return if bypassing * @throws IOException + * @deprecated Use preGetTableDescriptors with regex instead. + */ + @Deprecated + void preGetTableDescriptors(ObserverContext ctx, + List tableNamesList, List descriptors) throws IOException; + + /** + * Called before a getTableDescriptors request has been processed. + * @param ctx the environment to interact with the framework and master + * @param tableNamesList the list of table names, or null if querying for all + * @param descriptors an empty list, can be filled with what to return if bypassing + * @param regex regular expression used for filtering the table names + * @throws IOException */ void preGetTableDescriptors(ObserverContext ctx, - List tableNamesList, - List descriptors) throws IOException; + List tableNamesList, List descriptors, + String regex) throws IOException; /** * Called after a getTableDescriptors request has been processed. * @param ctx the environment to interact with the framework and master * @param descriptors the list of descriptors about to be returned * @throws IOException + * @deprecated Use postGetTableDescriptors with regex instead. */ + @Deprecated void postGetTableDescriptors(ObserverContext ctx, List descriptors) throws IOException; + /** + * Called after a getTableDescriptors request has been processed. + * @param ctx the environment to interact with the framework and master + * @param descriptors the list of descriptors about to be returned + * @param regex regular expression used for filtering the table names + * @throws IOException + */ + void postGetTableDescriptors(ObserverContext ctx, + List descriptors, String regex) throws IOException; + /** * Called before a new namespace is created by * {@link org.apache.hadoop.hbase.master.HMaster}. diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java index 28aab619f01..293d9d5d3b0 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java @@ -19,16 +19,26 @@ package org.apache.hadoop.hbase.master; -import org.apache.hadoop.hbase.classification.InterfaceAudience; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.*; -import org.apache.hadoop.hbase.coprocessor.*; -import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; -import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas; - import java.io.IOException; import java.util.List; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Coprocessor; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.coprocessor.CoprocessorService; +import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.MasterObserver; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; +import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; +import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas; + /** * Provides the coprocessor framework and environment for master oriented * operations. {@link HMaster} interacts with the loaded coprocessors @@ -782,7 +792,7 @@ public class MasterCoprocessorHost } public boolean preGetTableDescriptors(final List tableNamesList, - final List descriptors) throws IOException { + final List descriptors) throws IOException { return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { @Override public void call(MasterObserver oserver, ObserverContext ctx) @@ -792,6 +802,17 @@ public class MasterCoprocessorHost }); } + public boolean preGetTableDescriptors(final List tableNamesList, + final List descriptors, final String regex) throws IOException { + return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { + @Override + public void call(MasterObserver oserver, ObserverContext ctx) + throws IOException { + oserver.preGetTableDescriptors(ctx, tableNamesList, descriptors, regex); + } + }); + } + public void postGetTableDescriptors(final List descriptors) throws IOException { execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { @@ -803,6 +824,17 @@ public class MasterCoprocessorHost }); } + public void postGetTableDescriptors(final List descriptors, final String regex) + throws IOException { + execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { + @Override + public void call(MasterObserver oserver, ObserverContext ctx) + throws IOException { + oserver.postGetTableDescriptors(ctx, descriptors, regex); + } + }); + } + public void preTableFlush(final TableName tableName) throws IOException { execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { @Override diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java index ff3d15fe64c..2efcf636932 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java @@ -22,16 +22,18 @@ import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.PleaseHoldException; import org.apache.hadoop.hbase.ServerLoad; @@ -39,7 +41,7 @@ import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.UnknownRegionException; -import org.apache.hadoop.hbase.MetaTableAccessor; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.client.TableState; import org.apache.hadoop.hbase.exceptions.MergeRegionException; import org.apache.hadoop.hbase.exceptions.UnknownProtocolException; @@ -49,11 +51,14 @@ import org.apache.hadoop.hbase.procedure.MasterProcedureManager; import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.protobuf.RequestConverter; import org.apache.hadoop.hbase.protobuf.ResponseConverter; -import org.apache.hadoop.hbase.protobuf.generated.*; +import org.apache.hadoop.hbase.protobuf.generated.ClientProtos; +import org.apache.hadoop.hbase.protobuf.generated.ClusterStatusProtos; +import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ProcedureDescription; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; +import org.apache.hadoop.hbase.protobuf.generated.MasterProtos; import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AddColumnRequest; import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AddColumnResponse; import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AssignRegionRequest; @@ -789,9 +794,12 @@ public class MasterRpcServices extends RSRpcServices tableNameList.add(ProtobufUtil.toTableName(tableNamePB)); } boolean bypass = false; + String regex = req.hasRegex() ? req.getRegex() : null; if (master.cpHost != null) { try { bypass = master.cpHost.preGetTableDescriptors(tableNameList, descriptors); + // method required for AccessController. + bypass |= master.cpHost.preGetTableDescriptors(tableNameList, descriptors, regex); } catch (IOException ioe) { throw new ServiceException(ioe); } @@ -826,15 +834,29 @@ public class MasterRpcServices extends RSRpcServices } } + // Retains only those matched by regular expression. + if(regex != null) { + Pattern pat = Pattern.compile(regex); + for (Iterator itr = descriptors.iterator(); itr.hasNext(); ) { + HTableDescriptor htd = itr.next(); + if (!pat.matcher(htd.getTableName().getNameAsString()).matches()) { + itr.remove(); + } + } + } + if (master.cpHost != null) { try { master.cpHost.postGetTableDescriptors(descriptors); + // method required for AccessController. + master.cpHost.postGetTableDescriptors(descriptors, regex); } catch (IOException ioe) { throw new ServiceException(ioe); } } } + GetTableDescriptorsResponse.Builder builder = GetTableDescriptorsResponse.newBuilder(); for (HTableDescriptor htd: descriptors) { builder.addTableSchema(htd.convert()); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java index 9d196abfac9..455408ad4be 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java @@ -1155,7 +1155,7 @@ public class AccessController extends BaseMasterAndRegionObserver return null; } }); - LOG.info(namespace + "entry deleted in "+AccessControlLists.ACL_TABLE_NAME+" table."); + LOG.info(namespace + "entry deleted in " + AccessControlLists.ACL_TABLE_NAME + " table."); } @Override @@ -2238,24 +2238,54 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preGetTableDescriptors(ObserverContext ctx, - List tableNamesList, - List descriptors) throws IOException { - // If the list is empty, this is a request for all table descriptors and requires GLOBAL - // ADMIN privs. - if (tableNamesList == null || tableNamesList.isEmpty()) { - requireGlobalPermission("getTableDescriptors", Action.ADMIN, null, null); - } - // Otherwise, if the requestor has ADMIN or CREATE privs for all listed tables, the - // request can be granted. - else { - MasterServices masterServices = ctx.getEnvironment().getMasterServices(); - for (TableName tableName: tableNamesList) { - // Skip checks for a table that does not exist - if (!masterServices.getTableStateManager().isTablePresent(tableName)) - continue; - requirePermission("getTableDescriptors", tableName, null, null, - Action.ADMIN, Action.CREATE); + List tableNamesList, List descriptors, + String regex) throws IOException { + // We are delegating the authorization check to postGetTableDescriptors as we don't have + // any concrete set of table names when regex is present. + if(regex == null) { + // If the list is empty, this is a request for all table descriptors and requires GLOBAL + // ADMIN privs. + if (tableNamesList == null || tableNamesList.isEmpty()) { + requireGlobalPermission("getTableDescriptors", Action.ADMIN, null, null); } + // Otherwise, if the requestor has ADMIN or CREATE privs for all listed tables, the + // request can be granted. + else { + MasterServices masterServices = ctx.getEnvironment().getMasterServices(); + for (TableName tableName: tableNamesList) { + // Skip checks for a table that does not exist + if (!masterServices.getTableStateManager().isTablePresent(tableName)) + continue; + requirePermission("getTableDescriptors", tableName, null, null, + Action.ADMIN, Action.CREATE); + } + } + } + } + + @Override + public void postGetTableDescriptors(ObserverContext ctx, + List descriptors, String regex) throws IOException { + // Skipping as checks in this case are already done by preGetTableDescriptors. + if (regex == null) { + return; + } + int numMatchedTables = descriptors.size(); + // Retains only those which passes authorization checks, as the checks weren't done as part + // of preGetTableDescriptors. + for(Iterator itr = descriptors.iterator(); itr.hasNext();) { + HTableDescriptor htd = itr.next(); + try { + requirePermission("getTableDescriptors", htd.getTableName(), null, null, + Action.ADMIN, Action.CREATE); + } catch(AccessDeniedException exception) { + itr.remove(); + } + } + + // Throws exception if none of the matched tables are authorized for the user. + if(numMatchedTables > 0 && descriptors.size() == 0) { + throw new AccessDeniedException("Insufficient permissions for accessing tables"); } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java index 0e54782c921..4a5fa633018 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java @@ -753,6 +753,11 @@ public class TestMasterObserver { postDeleteSnapshotCalled = true; } + @Override + public void preGetTableDescriptors(ObserverContext ctx, + List tableNamesList, List descriptors) throws IOException { + } + public boolean wasDeleteSnapshotCalled() { return preDeleteSnapshotCalled && postDeleteSnapshotCalled; } @@ -986,7 +991,7 @@ public class TestMasterObserver { @Override public void preGetTableDescriptors(ObserverContext ctx, - List tableNamesList, List descriptors) + List tableNamesList, List descriptors, String regex) throws IOException { preGetTableDescriptorsCalled = true; } @@ -994,6 +999,11 @@ public class TestMasterObserver { @Override public void postGetTableDescriptors(ObserverContext ctx, List descriptors) throws IOException { + } + + @Override + public void postGetTableDescriptors(ObserverContext ctx, + List descriptors, String regex) throws IOException { postGetTableDescriptorsCalled = true; } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java index b9ecc129938..76aa65659c9 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java @@ -19,10 +19,13 @@ package org.apache.hadoop.hbase.security.access; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; +import java.security.PrivilegedAction; import java.util.List; import java.util.Map; import java.util.NavigableMap; @@ -2430,4 +2433,38 @@ public class TestAccessController extends SecureTestUtil { } } + @Test + public void testAccessControlClientUserPerms() throws Exception { + try { + final String regex = TEST_TABLE.getTableName().getNameAsString(); + User testUserPerms = User.createUserForTesting(conf, "testUserPerms", new String[0]); + + PrivilegedAction> listTablesRestrictedAction = + new PrivilegedAction>() { + @Override + public List run() { + try { + return AccessControlClient.getUserPermissions(conf, regex); + } catch (Throwable e) { + LOG.error("error during call of AccessControlClient.getUserPermissions. " + + e.getStackTrace()); + return null; + } + } + }; + assertNull(testUserPerms.runAs(listTablesRestrictedAction)); + + // Grant TABLE ADMIN privs to testUserPerms + grantOnTable(TEST_UTIL, testUserPerms.getShortName(), + TEST_TABLE.getTableName(), null, null, + Permission.Action.ADMIN); + List perms = testUserPerms.runAs(listTablesRestrictedAction); + assertNotNull(perms); + // USER_ADMIN, USER_CREATE, USER_RW, USER_RO, testUserPerms has row each. + assertEquals(5, perms.size()); + } catch (Throwable e) { + throw new HBaseIOException(e); + } + } + }