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  
25  import org.apache.hadoop.conf.Configuration;
26  import org.apache.hadoop.hbase.HBaseTestingUtility;
27  import org.apache.hadoop.hbase.HColumnDescriptor;
28  import org.apache.hadoop.hbase.HConstants;
29  import org.apache.hadoop.hbase.HTableDescriptor;
30  import org.apache.hadoop.hbase.NamespaceDescriptor;
31  import org.apache.hadoop.hbase.testclassification.MediumTests;
32  import org.apache.hadoop.hbase.TableName;
33  import org.apache.hadoop.hbase.client.Get;
34  import org.apache.hadoop.hbase.client.HTable;
35  import org.apache.hadoop.hbase.client.Result;
36  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
37  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
38  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
39  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
40  import org.apache.hadoop.hbase.security.User;
41  import org.apache.hadoop.hbase.security.access.Permission.Action;
42  import org.apache.hadoop.hbase.util.Bytes;
43  import org.junit.AfterClass;
44  import org.junit.BeforeClass;
45  import org.junit.Test;
46  import org.junit.experimental.categories.Category;
47  
48  import com.google.common.collect.ListMultimap;
49  import com.google.protobuf.BlockingRpcChannel;
50  
51  @Category(MediumTests.class)
52  public class TestNamespaceCommands extends SecureTestUtil {
53    private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
54    private static String TEST_NAMESPACE = "ns1";
55    private static String TEST_NAMESPACE2 = "ns2";
56    private static Configuration conf;
57    private static MasterCoprocessorEnvironment CP_ENV;
58    private static AccessController ACCESS_CONTROLLER;
59  
60    // user with all permissions
61    private static User SUPERUSER;
62    // user with rw permissions
63    private static User USER_RW;
64    // user with create table permissions alone
65    private static User USER_CREATE;
66    // user with permission on namespace for testing all operations.
67    private static User USER_NSP_WRITE;
68    // user with admin permission on namespace.
69    private static User USER_NSP_ADMIN;
70  
71    private static String TEST_TABLE = TEST_NAMESPACE + ":testtable";
72    private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
73  
74    @BeforeClass
75    public static void beforeClass() throws Exception {
76      conf = UTIL.getConfiguration();
77      enableSecurity(conf);
78  
79      SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
80      USER_RW = User.createUserForTesting(conf, "rw_user", new String[0]);
81      USER_CREATE = User.createUserForTesting(conf, "create_user", new String[0]);
82      USER_NSP_WRITE = User.createUserForTesting(conf, "namespace_write", new String[0]);
83      USER_NSP_ADMIN = User.createUserForTesting(conf, "namespace_admin", new String[0]);
84  
85      UTIL.startMiniCluster();
86      // Wait for the ACL table to become available
87      UTIL.waitTableAvailable(AccessControlLists.ACL_TABLE_NAME.getName(), 30 * 1000);
88  
89      ACCESS_CONTROLLER = (AccessController) UTIL.getMiniHBaseCluster().getMaster()
90        .getCoprocessorHost()
91          .findCoprocessor(AccessController.class.getName());
92  
93      UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(TEST_NAMESPACE).build());
94      UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(TEST_NAMESPACE2).build());
95  
96      grantOnNamespace(UTIL, USER_NSP_WRITE.getShortName(),
97        TEST_NAMESPACE, Permission.Action.WRITE, Permission.Action.CREATE);
98  
99      grantOnNamespace(UTIL, USER_NSP_ADMIN.getShortName(), TEST_NAMESPACE, Permission.Action.ADMIN);
100     grantOnNamespace(UTIL, USER_NSP_ADMIN.getShortName(), TEST_NAMESPACE2, Permission.Action.ADMIN);
101   }
102 
103   @AfterClass
104   public static void afterClass() throws Exception {
105     UTIL.getHBaseAdmin().deleteNamespace(TEST_NAMESPACE);
106     UTIL.getHBaseAdmin().deleteNamespace(TEST_NAMESPACE2);
107     UTIL.shutdownMiniCluster();
108   }
109 
110   @Test
111   public void testAclTableEntries() throws Exception {
112     String userTestNamespace = "userTestNsp";
113     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
114     try {
115       // Grant and check state in ACL table
116       grantOnNamespace(UTIL, userTestNamespace, TEST_NAMESPACE,
117         Permission.Action.WRITE);
118 
119       Result result = acl.get(new Get(Bytes.toBytes(userTestNamespace)));
120       assertTrue(result != null);
121       ListMultimap<String, TablePermission> perms =
122           AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
123       assertEquals(3, perms.size());
124       List<TablePermission> namespacePerms = perms.get(userTestNamespace);
125       assertTrue(perms.containsKey(userTestNamespace));
126       assertEquals(1, namespacePerms.size());
127       assertEquals(TEST_NAMESPACE,
128         namespacePerms.get(0).getNamespace());
129       assertEquals(null, namespacePerms.get(0).getFamily());
130       assertEquals(null, namespacePerms.get(0).getQualifier());
131       assertEquals(1, namespacePerms.get(0).getActions().length);
132       assertEquals(Permission.Action.WRITE, namespacePerms.get(0).getActions()[0]);
133 
134       // Revoke and check state in ACL table
135       revokeFromNamespace(UTIL, userTestNamespace, TEST_NAMESPACE,
136         Permission.Action.WRITE);
137 
138       perms = AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
139       assertEquals(2, perms.size());
140     } finally {
141       acl.close();
142     }
143   }
144 
145   @Test
146   public void testModifyNamespace() throws Exception {
147     AccessTestAction modifyNamespace = new AccessTestAction() {
148       public Object run() throws Exception {
149         ACCESS_CONTROLLER.preModifyNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
150           NamespaceDescriptor.create(TEST_NAMESPACE).addConfiguration("abc", "156").build());
151         return null;
152       }
153     };
154     // verify that superuser or hbase admin can modify namespaces.
155     verifyAllowed(modifyNamespace, SUPERUSER, USER_NSP_ADMIN);
156     // all others should be denied
157     verifyDenied(modifyNamespace, USER_NSP_WRITE, USER_CREATE, USER_RW);
158   }
159 
160   @Test
161   public void testCreateAndDeleteNamespace() throws Exception {
162     AccessTestAction createNamespace = new AccessTestAction() {
163       public Object run() throws Exception {
164         ACCESS_CONTROLLER.preCreateNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
165           NamespaceDescriptor.create(TEST_NAMESPACE2).build());
166         return null;
167       }
168     };
169 
170     AccessTestAction deleteNamespace = new AccessTestAction() {
171       public Object run() throws Exception {
172         ACCESS_CONTROLLER.preDeleteNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
173           TEST_NAMESPACE2);
174         return null;
175       }
176     };
177 
178     // verify that only superuser can create namespaces.
179     verifyAllowed(createNamespace, SUPERUSER);
180  // verify that superuser or hbase admin can delete namespaces.
181     verifyAllowed(deleteNamespace, SUPERUSER, USER_NSP_ADMIN);
182 
183     // all others should be denied
184     verifyDenied(createNamespace, USER_NSP_WRITE, USER_CREATE, USER_RW, USER_NSP_ADMIN);
185     verifyDenied(deleteNamespace, USER_NSP_WRITE, USER_CREATE, USER_RW);
186   }
187 
188   @Test
189   public void testGrantRevoke() throws Exception{
190     final String testUser = "testUser";
191 
192     // Test if client API actions are authorized
193 
194     AccessTestAction grantAction = new AccessTestAction() {
195       public Object run() throws Exception {
196         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
197         try {
198           BlockingRpcChannel service =
199               acl.coprocessorService(HConstants.EMPTY_START_ROW);
200           AccessControlService.BlockingInterface protocol =
201             AccessControlService.newBlockingStub(service);
202           ProtobufUtil.grant(protocol, testUser, TEST_NAMESPACE, Action.WRITE);
203         } finally {
204           acl.close();
205         }
206         return null;
207       }
208     };
209 
210     AccessTestAction revokeAction = new AccessTestAction() {
211       public Object run() throws Exception {
212         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
213         try {
214           BlockingRpcChannel service =
215               acl.coprocessorService(HConstants.EMPTY_START_ROW);
216           AccessControlService.BlockingInterface protocol =
217             AccessControlService.newBlockingStub(service);
218           ProtobufUtil.revoke(protocol, testUser, TEST_NAMESPACE, Action.WRITE);
219         } finally {
220           acl.close();
221         }
222         return null;
223       }
224     };
225 
226     AccessTestAction getPermissionsAction = new AccessTestAction() {
227       @Override
228       public Object run() throws Exception {
229         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
230         try {
231           BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
232           AccessControlService.BlockingInterface protocol =
233             AccessControlService.newBlockingStub(service);
234           ProtobufUtil.getUserPermissions(protocol, Bytes.toBytes(TEST_NAMESPACE));
235         } finally {
236           acl.close();
237         }
238         return null;
239       }
240     };
241 
242     // Only HBase super user should be able to grant and revoke permissions to
243     // namespaces
244     verifyAllowed(grantAction, SUPERUSER, USER_NSP_ADMIN);
245     verifyDenied(grantAction, USER_CREATE, USER_RW);
246     verifyAllowed(revokeAction, SUPERUSER, USER_NSP_ADMIN);
247     verifyDenied(revokeAction, USER_CREATE, USER_RW);
248 
249     // Only an admin should be able to get the user permission
250     verifyAllowed(revokeAction, SUPERUSER, USER_NSP_ADMIN);
251     verifyDeniedWithException(revokeAction, USER_CREATE, USER_RW);
252   }
253 
254   @Test
255   public void testCreateTableWithNamespace() throws Exception {
256     AccessTestAction createTable = new AccessTestAction() {
257       @Override
258       public Object run() throws Exception {
259         HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TEST_TABLE));
260         htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
261         ACCESS_CONTROLLER.preCreateTable(ObserverContext.createAndPrepare(CP_ENV, null), htd, null);
262         return null;
263       }
264     };
265 
266     // Only users with create permissions on namespace should be able to create a new table
267     verifyAllowed(createTable, SUPERUSER, USER_NSP_WRITE);
268 
269     // all others should be denied
270     verifyDenied(createTable, USER_CREATE, USER_RW);
271   }
272 }