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