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.client.HTable;
32  import org.apache.hadoop.hbase.testclassification.MediumTests;
33  import org.apache.hadoop.hbase.TableName;
34  import org.apache.hadoop.hbase.client.Get;
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 final String GROUP_ADMIN = "group_admin";
72    private static final String GROUP_CREATE = "group_create";
73    private static final String GROUP_READ = "group_read";
74    private static final String GROUP_WRITE = "group_write";
75  
76    private static User USER_GROUP_ADMIN;
77    private static User USER_GROUP_CREATE;
78    private static User USER_GROUP_READ;
79    private static User USER_GROUP_WRITE;
80  
81    private static String TEST_TABLE = TEST_NAMESPACE + ":testtable";
82    private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
83  
84    @BeforeClass
85    public static void beforeClass() throws Exception {
86      conf = UTIL.getConfiguration();
87      enableSecurity(conf);
88  
89      SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
90      USER_RW = User.createUserForTesting(conf, "rw_user", new String[0]);
91      USER_CREATE = User.createUserForTesting(conf, "create_user", new String[0]);
92      USER_NSP_WRITE = User.createUserForTesting(conf, "namespace_write", new String[0]);
93      USER_NSP_ADMIN = User.createUserForTesting(conf, "namespace_admin", new String[0]);
94  
95      USER_GROUP_ADMIN =
96          User.createUserForTesting(conf, "user_group_admin", new String[] { GROUP_ADMIN });
97      USER_GROUP_CREATE =
98          User.createUserForTesting(conf, "user_group_create", new String[] { GROUP_CREATE });
99      USER_GROUP_READ =
100         User.createUserForTesting(conf, "user_group_read", new String[] { GROUP_READ });
101     USER_GROUP_WRITE =
102         User.createUserForTesting(conf, "user_group_write", new String[] { GROUP_WRITE });
103 
104     UTIL.startMiniCluster();
105     // Wait for the ACL table to become available
106     UTIL.waitTableAvailable(AccessControlLists.ACL_TABLE_NAME.getName(), 30 * 1000);
107 
108     ACCESS_CONTROLLER = (AccessController) UTIL.getMiniHBaseCluster().getMaster()
109       .getCoprocessorHost()
110         .findCoprocessor(AccessController.class.getName());
111 
112     UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(TEST_NAMESPACE).build());
113     UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(TEST_NAMESPACE2).build());
114 
115     grantOnNamespace(UTIL, USER_NSP_WRITE.getShortName(),
116       TEST_NAMESPACE, Permission.Action.WRITE, Permission.Action.CREATE);
117 
118     grantOnNamespace(UTIL, USER_NSP_ADMIN.getShortName(), TEST_NAMESPACE, Permission.Action.ADMIN);
119     grantOnNamespace(UTIL, USER_NSP_ADMIN.getShortName(), TEST_NAMESPACE2, Permission.Action.ADMIN);
120 
121     grantGlobal(UTIL, convertToGroup(GROUP_ADMIN), Permission.Action.ADMIN);
122     grantGlobal(UTIL, convertToGroup(GROUP_CREATE), Permission.Action.CREATE);
123     grantGlobal(UTIL, convertToGroup(GROUP_READ), Permission.Action.READ);
124     grantGlobal(UTIL, convertToGroup(GROUP_WRITE), Permission.Action.WRITE);
125   }
126 
127   @AfterClass
128   public static void afterClass() throws Exception {
129     UTIL.getHBaseAdmin().deleteNamespace(TEST_NAMESPACE);
130     UTIL.getHBaseAdmin().deleteNamespace(TEST_NAMESPACE2);
131     UTIL.shutdownMiniCluster();
132   }
133 
134   @Test
135   public void testAclTableEntries() throws Exception {
136     String userTestNamespace = "userTestNsp";
137     HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
138     try {
139       // Grant and check state in ACL table
140       grantOnNamespace(UTIL, userTestNamespace, TEST_NAMESPACE,
141         Permission.Action.WRITE);
142 
143       Result result = acl.get(new Get(Bytes.toBytes(userTestNamespace)));
144       assertTrue(result != null);
145       ListMultimap<String, TablePermission> perms =
146           AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
147       assertEquals(3, perms.size());
148       List<TablePermission> namespacePerms = perms.get(userTestNamespace);
149       assertTrue(perms.containsKey(userTestNamespace));
150       assertEquals(1, namespacePerms.size());
151       assertEquals(TEST_NAMESPACE,
152         namespacePerms.get(0).getNamespace());
153       assertEquals(null, namespacePerms.get(0).getFamily());
154       assertEquals(null, namespacePerms.get(0).getQualifier());
155       assertEquals(1, namespacePerms.get(0).getActions().length);
156       assertEquals(Permission.Action.WRITE, namespacePerms.get(0).getActions()[0]);
157 
158       // Revoke and check state in ACL table
159       revokeFromNamespace(UTIL, userTestNamespace, TEST_NAMESPACE,
160         Permission.Action.WRITE);
161 
162       perms = AccessControlLists.getNamespacePermissions(conf, TEST_NAMESPACE);
163       assertEquals(2, perms.size());
164     } finally {
165       acl.close();
166     }
167   }
168 
169   @Test
170   public void testModifyNamespace() throws Exception {
171     AccessTestAction modifyNamespace = new AccessTestAction() {
172       public Object run() throws Exception {
173         ACCESS_CONTROLLER.preModifyNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
174             NamespaceDescriptor.create(TEST_NAMESPACE).addConfiguration("abc", "156").build());
175         return null;
176       }
177     };
178     // verify that superuser or hbase admin can modify namespaces.
179     verifyAllowed(modifyNamespace, SUPERUSER, USER_NSP_ADMIN, USER_GROUP_ADMIN);
180     // all others should be denied
181     verifyDenied(modifyNamespace, USER_NSP_WRITE, USER_CREATE, USER_RW, USER_GROUP_READ,
182       USER_GROUP_WRITE, USER_GROUP_CREATE);
183   }
184 
185   @Test
186   public void testCreateAndDeleteNamespace() throws Exception {
187     AccessTestAction createNamespace = new AccessTestAction() {
188       public Object run() throws Exception {
189         ACCESS_CONTROLLER.preCreateNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
190           NamespaceDescriptor.create(TEST_NAMESPACE2).build());
191         return null;
192       }
193     };
194 
195     AccessTestAction deleteNamespace = new AccessTestAction() {
196       public Object run() throws Exception {
197         ACCESS_CONTROLLER.preDeleteNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
198           TEST_NAMESPACE2);
199         return null;
200       }
201     };
202 
203     // verify that only superuser can create namespaces.
204     verifyAllowed(createNamespace, SUPERUSER, USER_GROUP_ADMIN);
205     // verify that superuser or hbase admin can delete namespaces.
206     verifyAllowed(deleteNamespace, SUPERUSER, USER_NSP_ADMIN, USER_GROUP_ADMIN);
207 
208     // all others should be denied
209     verifyDenied(createNamespace, USER_NSP_WRITE, USER_CREATE, USER_RW, USER_NSP_ADMIN,
210       USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE);
211     verifyDenied(deleteNamespace, USER_NSP_WRITE, USER_CREATE, USER_RW, USER_GROUP_READ,
212       USER_GROUP_WRITE, USER_GROUP_CREATE);
213   }
214 
215   @Test
216   public void testGrantRevoke() throws Exception{
217     final String testUser = "testUser";
218 
219     // Test if client API actions are authorized
220 
221     AccessTestAction grantAction = new AccessTestAction() {
222       public Object run() throws Exception {
223         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
224         try {
225           BlockingRpcChannel service =
226               acl.coprocessorService(HConstants.EMPTY_START_ROW);
227           AccessControlService.BlockingInterface protocol =
228             AccessControlService.newBlockingStub(service);
229           ProtobufUtil.grant(protocol, testUser, TEST_NAMESPACE, Action.WRITE);
230         } finally {
231           acl.close();
232         }
233         return null;
234       }
235     };
236 
237     AccessTestAction revokeAction = new AccessTestAction() {
238       public Object run() throws Exception {
239         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
240         try {
241           BlockingRpcChannel service =
242               acl.coprocessorService(HConstants.EMPTY_START_ROW);
243           AccessControlService.BlockingInterface protocol =
244             AccessControlService.newBlockingStub(service);
245           ProtobufUtil.revoke(protocol, testUser, TEST_NAMESPACE, Action.WRITE);
246         } finally {
247           acl.close();
248         }
249         return null;
250       }
251     };
252 
253     AccessTestAction getPermissionsAction = new AccessTestAction() {
254       @Override
255       public Object run() throws Exception {
256         HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
257         try {
258           BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
259           AccessControlService.BlockingInterface protocol =
260             AccessControlService.newBlockingStub(service);
261           ProtobufUtil.getUserPermissions(protocol, Bytes.toBytes(TEST_NAMESPACE));
262         } finally {
263           acl.close();
264         }
265         return null;
266       }
267     };
268 
269     // Only HBase super user should be able to grant and revoke permissions to
270     // namespaces
271     verifyAllowed(grantAction, SUPERUSER, USER_NSP_ADMIN, USER_GROUP_ADMIN);
272     verifyDenied(grantAction, USER_CREATE, USER_RW, USER_GROUP_READ, USER_GROUP_WRITE,
273       USER_GROUP_CREATE);
274     verifyAllowed(revokeAction, SUPERUSER, USER_NSP_ADMIN, USER_GROUP_ADMIN);
275     verifyDenied(revokeAction, USER_CREATE, USER_RW, USER_GROUP_READ, USER_GROUP_WRITE,
276       USER_GROUP_CREATE);
277 
278     // Only an admin should be able to get the user permission
279     verifyAllowed(revokeAction, SUPERUSER, USER_NSP_ADMIN, USER_GROUP_ADMIN);
280     verifyDenied(revokeAction, USER_CREATE, USER_RW, USER_GROUP_READ, USER_GROUP_WRITE,
281       USER_GROUP_CREATE);
282   }
283 
284   @Test
285   public void testCreateTableWithNamespace() throws Exception {
286     AccessTestAction createTable = new AccessTestAction() {
287       @Override
288       public Object run() throws Exception {
289         HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TEST_TABLE));
290         htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
291         ACCESS_CONTROLLER.preCreateTable(ObserverContext.createAndPrepare(CP_ENV, null), htd, null);
292         return null;
293       }
294     };
295 
296     // Only users with create permissions on namespace should be able to create a new table
297     verifyAllowed(createTable, SUPERUSER, USER_NSP_WRITE, USER_GROUP_CREATE);
298 
299     // all others should be denied
300     verifyDenied(createTable, USER_CREATE, USER_RW, USER_GROUP_READ, USER_GROUP_WRITE,
301       USER_GROUP_ADMIN);
302   }
303 }