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