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 fbf505e..85abbf8 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 @@ -159,6 +159,16 @@ public abstract class BaseMasterAndRegionObserver extends BaseRegionObserver } @Override + public void preGetNamespaceDescriptor(ObserverContext ctx, + String namespace) throws IOException { + } + + @Override + public void postGetNamespaceDescriptor(ObserverContext ctx, + NamespaceDescriptor ns) throws IOException { + } + + @Override public void preListNamespaceDescriptors(ObserverContext ctx, List descriptors) 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 e33288d..de1645e 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 @@ -152,6 +152,16 @@ public class BaseMasterObserver implements MasterObserver { } @Override + public void preGetNamespaceDescriptor(ObserverContext ctx, + String namespace) throws IOException { + } + + @Override + public void postGetNamespaceDescriptor(ObserverContext ctx, + NamespaceDescriptor ns) throws IOException { + } + + @Override public void preListNamespaceDescriptors(ObserverContext ctx, List descriptors) 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 bad2c5f..ab9f709 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 @@ -789,6 +789,24 @@ public interface MasterObserver extends Coprocessor { NamespaceDescriptor ns) throws IOException; /** + * Called before a getNamespaceDescriptor request has been processed. + * @param ctx the environment to interact with the framework and master + * @param namespace the name of the namespace + * @throws IOException + */ + void preGetNamespaceDescriptor(ObserverContext ctx, + String namespace) throws IOException; + + /** + * Called after a getNamespaceDescriptor request has been processed. + * @param ctx the environment to interact with the framework and master + * @param ns the NamespaceDescriptor + * @throws IOException + */ + void postGetNamespaceDescriptor(ObserverContext ctx, + NamespaceDescriptor ns) throws IOException; + + /** * Called before a listNamespaceDescriptors request has been processed. * @param ctx the environment to interact with the framework and master * @param descriptors an empty list, can be filled with what to return if bypassing diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index 3d87c67..06b0e3c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -1227,7 +1227,7 @@ public class HMaster extends HRegionServer implements MasterServices, Server { } String namespace = hTableDescriptor.getTableName().getNamespaceAsString(); - getNamespaceDescriptor(namespace); // ensure namespace exists + ensureNamespaceExists(namespace); HRegionInfo[] newRegions = getHRegionInfos(hTableDescriptor, splitKeys); checkInitialized(); @@ -2028,13 +2028,39 @@ public class HMaster extends HRegionServer implements MasterServices, Server { } } + /** + * Ensure that the specified namespace exists, otherwise throws a NamespaceNotFoundException + * + * @param name the namespace to check + * @throws IOException if the namespace manager is not ready yet. + * @throws NamespaceNotFoundException if the namespace does not exists + */ + private void ensureNamespaceExists(final String name) + throws IOException, NamespaceNotFoundException { + checkNamespaceManagerReady(); + NamespaceDescriptor nsd = tableNamespaceManager.get(name); + if (nsd == null) { + throw new NamespaceNotFoundException(name); + } + } + @Override public NamespaceDescriptor getNamespaceDescriptor(String name) throws IOException { checkNamespaceManagerReady(); + + if (cpHost != null) { + cpHost.preGetNamespaceDescriptor(name); + } + NamespaceDescriptor nsd = tableNamespaceManager.get(name); if (nsd == null) { throw new NamespaceNotFoundException(name); } + + if (cpHost != null) { + cpHost.postGetNamespaceDescriptor(nsd); + } + return nsd; } @@ -2060,13 +2086,13 @@ public class HMaster extends HRegionServer implements MasterServices, Server { @Override public List listTableDescriptorsByNamespace(String name) throws IOException { - getNamespaceDescriptor(name); // check that namespace exists + ensureNamespaceExists(name); return listTableDescriptors(name, null, null, true); } @Override public List listTableNamesByNamespace(String name) throws IOException { - getNamespaceDescriptor(name); // check that namespace exists + ensureNamespaceExists(name); return listTableNames(name, null, true); } 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 679e62a..2997172 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 @@ -150,6 +150,28 @@ public class MasterCoprocessorHost }); } + public void preGetNamespaceDescriptor(final String namespaceName) + throws IOException { + execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { + @Override + public void call(MasterObserver oserver, ObserverContext ctx) + throws IOException { + oserver.preGetNamespaceDescriptor(ctx, namespaceName); + } + }); + } + + public void postGetNamespaceDescriptor(final NamespaceDescriptor ns) + throws IOException { + execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { + @Override + public void call(MasterObserver oserver, ObserverContext ctx) + throws IOException { + oserver.postGetNamespaceDescriptor(ctx, ns); + } + }); + } + public boolean preListNamespaceDescriptors(final List descriptors) throws IOException { return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { 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 f959bb0..c7abb81 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 @@ -1207,6 +1207,12 @@ public class AccessController extends BaseMasterAndRegionObserver } @Override + public void preGetNamespaceDescriptor(ObserverContext ctx, String namespace) + throws IOException { + requireGlobalPermission("getNamespaceDescriptor", Action.ADMIN, namespace); + } + + @Override public void postListNamespaceDescriptors(ObserverContext ctx, List descriptors) throws IOException { // Retains only those which passes authorization checks, as the checks weren't done as part 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 cca3496..636ea29 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 @@ -93,6 +93,8 @@ public class TestMasterObserver { private boolean postDeleteNamespaceCalled; private boolean preModifyNamespaceCalled; private boolean postModifyNamespaceCalled; + private boolean preGetNamespaceDescriptorCalled; + private boolean postGetNamespaceDescriptorCalled; private boolean preListNamespaceDescriptorsCalled; private boolean postListNamespaceDescriptorsCalled; private boolean preAddColumnCalled; @@ -175,6 +177,8 @@ public class TestMasterObserver { postDeleteNamespaceCalled = false; preModifyNamespaceCalled = false; postModifyNamespaceCalled = false; + preGetNamespaceDescriptorCalled = false; + postGetNamespaceDescriptorCalled = false; preListNamespaceDescriptorsCalled = false; postListNamespaceDescriptorsCalled = false; preAddColumnCalled = false; @@ -398,6 +402,23 @@ public class TestMasterObserver { return preModifyNamespaceCalled && !postModifyNamespaceCalled; } + + @Override + public void preGetNamespaceDescriptor(ObserverContext ctx, + String namespace) throws IOException { + preGetNamespaceDescriptorCalled = true; + } + + @Override + public void postGetNamespaceDescriptor(ObserverContext ctx, + NamespaceDescriptor ns) throws IOException { + postGetNamespaceDescriptorCalled = true; + } + + public boolean wasGetNamespaceDescriptorCalled() { + return preGetNamespaceDescriptorCalled && postGetNamespaceDescriptorCalled; + } + @Override public void preListNamespaceDescriptors(ObserverContext env, List descriptors) throws IOException { @@ -1424,6 +1445,7 @@ public class TestMasterObserver { assertTrue("Test namespace should be created", cp.wasCreateNamespaceCalled()); assertNotNull(admin.getNamespaceDescriptor(testNamespace)); + assertTrue("Test namespace descriptor should have been called", cp.wasGetNamespaceDescriptorCalled()); // turn off bypass, run the tests again cp.enableBypass(true); @@ -1434,11 +1456,13 @@ public class TestMasterObserver { cp.preModifyNamespaceCalledOnly()); assertNotNull(admin.getNamespaceDescriptor(testNamespace)); + assertTrue("Test namespace descriptor should have been called", cp.wasGetNamespaceDescriptorCalled()); admin.deleteNamespace(testNamespace); assertTrue("Test namespace should not have been deleted", cp.preDeleteNamespaceCalledOnly()); assertNotNull(admin.getNamespaceDescriptor(testNamespace)); + assertTrue("Test namespace descriptor should have been called", cp.wasGetNamespaceDescriptorCalled()); cp.enableBypass(false); cp.resetStates(); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestNamespaceCommands.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestNamespaceCommands.java index e26a9ca..2ee1100 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestNamespaceCommands.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestNamespaceCommands.java @@ -192,6 +192,21 @@ public class TestNamespaceCommands extends SecureTestUtil { } @Test + public void testGetNamespaceDescriptor() throws Exception { + AccessTestAction getNamespaceAction = new AccessTestAction() { + public Object run() throws Exception { + ACCESS_CONTROLLER.preGetNamespaceDescriptor(ObserverContext.createAndPrepare(CP_ENV, null), + TEST_NAMESPACE); + return null; + } + }; + // verify that superuser or hbase admin can get the namespace descriptor. + verifyAllowed(getNamespaceAction, SUPERUSER, USER_NSP_ADMIN); + // all others should be denied + verifyDenied(getNamespaceAction, USER_NSP_WRITE, USER_CREATE, USER_RW); + } + + @Test public void testListNamespaces() throws Exception { AccessTestAction listAction = new AccessTestAction() { @Override