View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.security.access;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertTrue;
22  
23  import java.util.List;
24  import java.util.Arrays;
25  
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.hbase.HBaseTestingUtility;
28  import org.apache.hadoop.hbase.HColumnDescriptor;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.HTableDescriptor;
31  import org.apache.hadoop.hbase.NamespaceDescriptor;
32  import org.apache.hadoop.hbase.testclassification.MediumTests;
33  import org.apache.hadoop.hbase.TableName;
34  import org.apache.hadoop.hbase.client.Admin;
35  import org.apache.hadoop.hbase.client.Connection;
36  import org.apache.hadoop.hbase.client.ConnectionFactory;
37  import org.apache.hadoop.hbase.client.Get;
38  import org.apache.hadoop.hbase.client.Result;
39  import org.apache.hadoop.hbase.client.Table;
40  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
41  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
42  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
43  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
44  import org.apache.hadoop.hbase.security.User;
45  import org.apache.hadoop.hbase.security.access.Permission.Action;
46  import org.apache.hadoop.hbase.util.Bytes;
47  import org.junit.AfterClass;
48  import org.junit.BeforeClass;
49  import org.junit.Test;
50  import org.junit.experimental.categories.Category;
51  
52  import com.google.common.collect.ListMultimap;
53  import com.google.protobuf.BlockingRpcChannel;
54  
55  @Category(MediumTests.class)
56  public class TestNamespaceCommands extends SecureTestUtil {
57    private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
58    private static String TEST_NAMESPACE = "ns1";
59    private static String TEST_NAMESPACE2 = "ns2";
60    private static Configuration conf;
61    private static MasterCoprocessorEnvironment CP_ENV;
62    private static AccessController ACCESS_CONTROLLER;
63  
64    // user with all permissions
65    private static User SUPERUSER;
66  
67    // user with A permission on global
68    private static User USER_GLOBAL_ADMIN;
69    // user with C permission on global
70    private static User USER_GLOBAL_CREATE;
71    // user with W permission on global
72    private static User USER_GLOBAL_WRITE;
73    // user with R permission on global
74    private static User USER_GLOBAL_READ;
75    // user with X permission on global
76    private static User USER_GLOBAL_EXEC;
77  
78    // user with A permission on namespace
79    private static User USER_NS_ADMIN;
80    // user with C permission on namespace
81    private static User USER_NS_CREATE;
82    // user with W permission on namespace
83    private static User USER_NS_WRITE;
84    // user with R permission on namespace.
85    private static User USER_NS_READ;
86    // user with X permission on namespace.
87    private static User USER_NS_EXEC;
88  
89    // user with rw permissions
90    private static User USER_TABLE_WRITE;  // TODO: WE DO NOT GIVE ANY PERMS TO THIS USER
91    //user with create table permissions alone
92    private static User USER_TABLE_CREATE; // TODO: WE DO NOT GIVE ANY PERMS TO THIS USER
93  
94    private static String TEST_TABLE = TEST_NAMESPACE + ":testtable";
95    private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
96  
97    @BeforeClass
98    public static void beforeClass() throws Exception {
99      conf = UTIL.getConfiguration();
100     enableSecurity(conf);
101 
102     SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
103     // Users with global permissions
104     USER_GLOBAL_ADMIN = User.createUserForTesting(conf, "global_admin", new String[0]);
105     USER_GLOBAL_CREATE = User.createUserForTesting(conf, "global_create", new String[0]);
106     USER_GLOBAL_WRITE = User.createUserForTesting(conf, "global_write", new String[0]);
107     USER_GLOBAL_READ = User.createUserForTesting(conf, "global_read", new String[0]);
108     USER_GLOBAL_EXEC = User.createUserForTesting(conf, "global_exec", new String[0]);
109 
110     USER_NS_ADMIN = User.createUserForTesting(conf, "namespace_admin", new String[0]);
111     USER_NS_CREATE = User.createUserForTesting(conf, "namespace_create", new String[0]);
112     USER_NS_WRITE = User.createUserForTesting(conf, "namespace_write", new String[0]);
113     USER_NS_READ = User.createUserForTesting(conf, "namespace_read", new String[0]);
114     USER_NS_EXEC = User.createUserForTesting(conf, "namespace_exec", new String[0]);
115 
116     USER_TABLE_CREATE = User.createUserForTesting(conf, "table_create", new String[0]);
117     USER_TABLE_WRITE = User.createUserForTesting(conf, "table_write", new String[0]);
118     // TODO: other table perms
119 
120     UTIL.startMiniCluster();
121     // Wait for the ACL table to become available
122     UTIL.waitTableAvailable(AccessControlLists.ACL_TABLE_NAME.getName(), 30 * 1000);
123 
124     ACCESS_CONTROLLER = (AccessController) UTIL.getMiniHBaseCluster().getMaster()
125       .getMasterCoprocessorHost()
126         .findCoprocessor(AccessController.class.getName());
127 
128     UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(TEST_NAMESPACE).build());
129     UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(TEST_NAMESPACE2).build());
130 
131     // grants on global
132     grantGlobal(UTIL, USER_GLOBAL_ADMIN.getShortName(),  Permission.Action.ADMIN);
133     grantGlobal(UTIL, USER_GLOBAL_CREATE.getShortName(), Permission.Action.CREATE);
134     grantGlobal(UTIL, USER_GLOBAL_WRITE.getShortName(),  Permission.Action.WRITE);
135     grantGlobal(UTIL, USER_GLOBAL_READ.getShortName(),   Permission.Action.READ);
136     grantGlobal(UTIL, USER_GLOBAL_EXEC.getShortName(),   Permission.Action.EXEC);
137 
138     // grants on namespace
139     grantOnNamespace(UTIL, USER_NS_ADMIN.getShortName(),  TEST_NAMESPACE, Permission.Action.ADMIN);
140     grantOnNamespace(UTIL, USER_NS_CREATE.getShortName(), TEST_NAMESPACE, Permission.Action.CREATE);
141     grantOnNamespace(UTIL, USER_NS_WRITE.getShortName(),  TEST_NAMESPACE, Permission.Action.WRITE);
142     grantOnNamespace(UTIL, USER_NS_READ.getShortName(),   TEST_NAMESPACE, Permission.Action.READ);
143     grantOnNamespace(UTIL, USER_NS_EXEC.getShortName(),   TEST_NAMESPACE, Permission.Action.EXEC);
144 
145     grantOnNamespace(UTIL, USER_NS_ADMIN.getShortName(), TEST_NAMESPACE2, Permission.Action.ADMIN);
146   }
147 
148   @AfterClass
149   public static void afterClass() throws Exception {
150     UTIL.getHBaseAdmin().deleteNamespace(TEST_NAMESPACE);
151     UTIL.getHBaseAdmin().deleteNamespace(TEST_NAMESPACE2);
152     UTIL.shutdownMiniCluster();
153   }
154 
155   @Test
156   public void testAclTableEntries() throws Exception {
157     String userTestNamespace = "userTestNsp";
158     try (Connection connection = ConnectionFactory.createConnection(conf);
159          Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
160       ListMultimap<String, TablePermission> perms =
161           AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
162 
163       perms = AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
164       assertEquals(5, perms.size());
165 
166       // Grant and check state in ACL table
167       grantOnNamespace(UTIL, userTestNamespace, TEST_NAMESPACE,
168         Permission.Action.WRITE);
169 
170       Result result = acl.get(new Get(Bytes.toBytes(userTestNamespace)));
171       assertTrue(result != null);
172       perms = AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
173       assertEquals(6, perms.size());
174       List<TablePermission> namespacePerms = perms.get(userTestNamespace);
175       assertTrue(perms.containsKey(userTestNamespace));
176       assertEquals(1, namespacePerms.size());
177       assertEquals(TEST_NAMESPACE,
178         namespacePerms.get(0).getNamespace());
179       assertEquals(null, namespacePerms.get(0).getFamily());
180       assertEquals(null, namespacePerms.get(0).getQualifier());
181       assertEquals(1, namespacePerms.get(0).getActions().length);
182       assertEquals(Permission.Action.WRITE, namespacePerms.get(0).getActions()[0]);
183 
184       // Revoke and check state in ACL table
185       revokeFromNamespace(UTIL, userTestNamespace, TEST_NAMESPACE,
186         Permission.Action.WRITE);
187 
188       perms = AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
189       assertEquals(5, perms.size());
190     }
191   }
192 
193   @Test
194   public void testModifyNamespace() throws Exception {
195     AccessTestAction modifyNamespace = new AccessTestAction() {
196       public Object run() throws Exception {
197         ACCESS_CONTROLLER.preModifyNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
198           NamespaceDescriptor.create(TEST_NAMESPACE).addConfiguration("abc", "156").build());
199         return null;
200       }
201     };
202 
203     // modifyNamespace: superuser | global(A) | NS(A)
204     verifyAllowed(modifyNamespace,
205       SUPERUSER,
206       USER_GLOBAL_ADMIN);
207 
208     verifyDenied(modifyNamespace,
209         USER_GLOBAL_CREATE,
210         USER_GLOBAL_WRITE,
211         USER_GLOBAL_READ,
212         USER_GLOBAL_EXEC,
213         USER_NS_ADMIN,
214         USER_NS_CREATE,
215         USER_NS_WRITE,
216         USER_NS_READ,
217         USER_NS_EXEC);
218   }
219 
220   @Test
221   public void testCreateAndDeleteNamespace() throws Exception {
222     AccessTestAction createNamespace = new AccessTestAction() {
223       @Override
224       public Object run() throws Exception {
225         ACCESS_CONTROLLER.preCreateNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
226           NamespaceDescriptor.create(TEST_NAMESPACE2).build());
227         return null;
228       }
229     };
230 
231     AccessTestAction deleteNamespace = new AccessTestAction() {
232       @Override
233       public Object run() throws Exception {
234         ACCESS_CONTROLLER.preDeleteNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
235           TEST_NAMESPACE2);
236         return null;
237       }
238     };
239 
240     // createNamespace: superuser | global(A)
241     verifyAllowed(createNamespace,
242       SUPERUSER,
243       USER_GLOBAL_ADMIN);
244 
245     // all others should be denied
246     verifyDenied(createNamespace,
247         USER_GLOBAL_CREATE,
248         USER_GLOBAL_WRITE,
249         USER_GLOBAL_READ,
250         USER_GLOBAL_EXEC,
251         USER_NS_ADMIN,
252         USER_NS_CREATE,
253         USER_NS_WRITE,
254         USER_NS_READ,
255         USER_NS_EXEC,
256         USER_TABLE_CREATE,
257         USER_TABLE_WRITE);
258 
259     // deleteNamespace: superuser | global(A) | NS(A)
260     verifyAllowed(deleteNamespace,
261       SUPERUSER,
262       USER_GLOBAL_ADMIN);
263 
264     verifyDenied(deleteNamespace,
265         USER_GLOBAL_CREATE,
266         USER_GLOBAL_WRITE,
267         USER_GLOBAL_READ,
268         USER_GLOBAL_EXEC,
269         USER_NS_ADMIN,
270         USER_NS_CREATE,
271         USER_NS_WRITE,
272         USER_NS_READ,
273         USER_NS_EXEC,
274         USER_TABLE_CREATE,
275         USER_TABLE_WRITE);
276   }
277 
278   @Test
279   public void testGetNamespaceDescriptor() throws Exception {
280     AccessTestAction getNamespaceAction = new AccessTestAction() {
281       @Override
282       public Object run() throws Exception {
283         ACCESS_CONTROLLER.preGetNamespaceDescriptor(ObserverContext.createAndPrepare(CP_ENV, null),
284           TEST_NAMESPACE);
285         return null;
286       }
287     };
288     // getNamespaceDescriptor : superuser | global(A) | NS(A)
289     verifyAllowed(getNamespaceAction,
290       SUPERUSER,
291       USER_GLOBAL_ADMIN,
292       USER_NS_ADMIN);
293 
294     verifyDenied(getNamespaceAction,
295         USER_GLOBAL_CREATE,
296         USER_GLOBAL_WRITE,
297         USER_GLOBAL_READ,
298         USER_GLOBAL_EXEC,
299         USER_NS_CREATE,
300         USER_NS_WRITE,
301         USER_NS_READ,
302         USER_NS_EXEC,
303         USER_TABLE_CREATE,
304         USER_TABLE_WRITE);
305   }
306 
307   @Test
308   public void testListNamespaces() throws Exception {
309     AccessTestAction listAction = new AccessTestAction() {
310       @Override
311       public Object run() throws Exception {
312         Connection unmanagedConnection =
313             ConnectionFactory.createConnection(UTIL.getConfiguration());
314         Admin admin = unmanagedConnection.getAdmin();
315         try {
316           return Arrays.asList(admin.listNamespaceDescriptors());
317         } finally {
318           admin.close();
319           unmanagedConnection.close();
320         }
321       }
322     };
323 
324     // listNamespaces         : All access*
325     // * Returned list will only show what you can call getNamespaceDescriptor()
326 
327     verifyAllowed(listAction,
328       SUPERUSER,
329       USER_GLOBAL_ADMIN,
330       USER_NS_ADMIN);
331 
332     // we have 3 namespaces: [default, hbase, TEST_NAMESPACE, TEST_NAMESPACE2]
333     assertEquals(4, ((List)SUPERUSER.runAs(listAction)).size());
334     assertEquals(4, ((List)USER_GLOBAL_ADMIN.runAs(listAction)).size());
335 
336     assertEquals(2, ((List)USER_NS_ADMIN.runAs(listAction)).size());
337 
338     assertEquals(0, ((List)USER_GLOBAL_CREATE.runAs(listAction)).size());
339     assertEquals(0, ((List)USER_GLOBAL_WRITE.runAs(listAction)).size());
340     assertEquals(0, ((List)USER_GLOBAL_READ.runAs(listAction)).size());
341     assertEquals(0, ((List)USER_GLOBAL_EXEC.runAs(listAction)).size());
342     assertEquals(0, ((List)USER_NS_CREATE.runAs(listAction)).size());
343     assertEquals(0, ((List)USER_NS_WRITE.runAs(listAction)).size());
344     assertEquals(0, ((List)USER_NS_READ.runAs(listAction)).size());
345     assertEquals(0, ((List)USER_NS_EXEC.runAs(listAction)).size());
346     assertEquals(0, ((List)USER_TABLE_CREATE.runAs(listAction)).size());
347     assertEquals(0, ((List)USER_TABLE_WRITE.runAs(listAction)).size());
348   }
349 
350   @Test
351   public void testGrantRevoke() throws Exception{
352     final String testUser = "testUser";
353 
354     // Test if client API actions are authorized
355 
356     AccessTestAction grantAction = new AccessTestAction() {
357       @Override
358       public Object run() throws Exception {
359         try (Connection connection = ConnectionFactory.createConnection(conf);
360              Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
361           BlockingRpcChannel service =
362               acl.coprocessorService(HConstants.EMPTY_START_ROW);
363           AccessControlService.BlockingInterface protocol =
364             AccessControlService.newBlockingStub(service);
365           ProtobufUtil.grant(protocol, testUser, TEST_NAMESPACE, Action.WRITE);
366         }
367         return null;
368       }
369     };
370 
371     AccessTestAction revokeAction = new AccessTestAction() {
372       public Object run() throws Exception {
373         try (Connection connection = ConnectionFactory.createConnection(conf);
374              Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
375           BlockingRpcChannel service =
376               acl.coprocessorService(HConstants.EMPTY_START_ROW);
377           AccessControlService.BlockingInterface protocol =
378             AccessControlService.newBlockingStub(service);
379           ProtobufUtil.revoke(protocol, testUser, TEST_NAMESPACE, Action.WRITE);
380         }
381         return null;
382       }
383     };
384 
385     AccessTestAction getPermissionsAction = new AccessTestAction() {
386       @Override
387       public Object run() throws Exception {
388         try (Connection connection = ConnectionFactory.createConnection(conf);
389              Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
390           BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
391           AccessControlService.BlockingInterface protocol =
392             AccessControlService.newBlockingStub(service);
393           ProtobufUtil.getUserPermissions(protocol, Bytes.toBytes(TEST_NAMESPACE));
394         }
395         return null;
396       }
397     };
398 
399     verifyAllowed(grantAction,
400       SUPERUSER,
401       USER_GLOBAL_ADMIN);
402 
403     verifyDenied(grantAction,
404         USER_GLOBAL_CREATE,
405         USER_GLOBAL_WRITE,
406         USER_GLOBAL_READ,
407         USER_GLOBAL_EXEC,
408         USER_NS_ADMIN,
409         USER_NS_CREATE,
410         USER_NS_WRITE,
411         USER_NS_READ,
412         USER_NS_EXEC,
413         USER_TABLE_CREATE,
414         USER_TABLE_WRITE);
415 
416     verifyAllowed(revokeAction,
417       SUPERUSER,
418       USER_GLOBAL_ADMIN);
419 
420     verifyDenied(revokeAction,
421         USER_GLOBAL_CREATE,
422         USER_GLOBAL_WRITE,
423         USER_GLOBAL_READ,
424         USER_GLOBAL_EXEC,
425         USER_NS_ADMIN,
426         USER_NS_CREATE,
427         USER_NS_WRITE,
428         USER_NS_READ,
429         USER_NS_EXEC,
430         USER_TABLE_CREATE,
431         USER_TABLE_WRITE);
432 
433     verifyAllowed(getPermissionsAction,
434       SUPERUSER,
435       USER_GLOBAL_ADMIN,
436       USER_NS_ADMIN);
437 
438     verifyDenied(getPermissionsAction,
439         USER_GLOBAL_CREATE,
440         USER_GLOBAL_WRITE,
441         USER_GLOBAL_READ,
442         USER_GLOBAL_EXEC,
443         USER_NS_CREATE,
444         USER_NS_WRITE,
445         USER_NS_READ,
446         USER_NS_EXEC,
447         USER_TABLE_CREATE,
448         USER_TABLE_WRITE);
449   }
450 
451   @Test
452   public void testCreateTableWithNamespace() throws Exception {
453     AccessTestAction createTable = new AccessTestAction() {
454       @Override
455       public Object run() throws Exception {
456         HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TEST_TABLE));
457         htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
458         ACCESS_CONTROLLER.preCreateTable(ObserverContext.createAndPrepare(CP_ENV, null), htd, null);
459         return null;
460       }
461     };
462 
463     //createTable            : superuser | global(C) | NS(C)
464     verifyAllowed(createTable,
465       SUPERUSER,
466       USER_GLOBAL_CREATE,
467       USER_NS_CREATE);
468 
469     verifyDenied(createTable,
470         USER_GLOBAL_ADMIN,
471         USER_GLOBAL_WRITE,
472         USER_GLOBAL_READ,
473         USER_GLOBAL_EXEC,
474         USER_NS_ADMIN,
475         USER_NS_WRITE,
476         USER_NS_READ,
477         USER_NS_EXEC,
478         USER_TABLE_CREATE,
479         USER_TABLE_WRITE);
480   }
481 }