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 java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.regex.Pattern;
24  
25  import org.apache.hadoop.conf.Configuration;
26  import org.apache.hadoop.hbase.HConstants;
27  import org.apache.hadoop.hbase.HTableDescriptor;
28  import org.apache.hadoop.hbase.MasterNotRunningException;
29  import org.apache.hadoop.hbase.NamespaceDescriptor;
30  import org.apache.hadoop.hbase.TableName;
31  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
32  import org.apache.hadoop.hbase.classification.InterfaceAudience;
33  import org.apache.hadoop.hbase.classification.InterfaceStability;
34  import org.apache.hadoop.hbase.client.Admin;
35  import org.apache.hadoop.hbase.client.ClusterConnection;
36  import org.apache.hadoop.hbase.client.Connection;
37  import org.apache.hadoop.hbase.client.ConnectionFactory;
38  import org.apache.hadoop.hbase.client.Table;
39  import org.apache.hadoop.hbase.client.security.SecurityCapability;
40  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
41  import org.apache.hadoop.hbase.ipc.HBaseRpcController;
42  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
43  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
44  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService.BlockingInterface;
45  import org.apache.hadoop.hbase.util.Bytes;
46  
47  /**
48   * Utility client for doing access control admin operations.
49   */
50  @InterfaceAudience.Public
51  @InterfaceStability.Evolving
52  public class AccessControlClient {
53    public static final TableName ACL_TABLE_NAME =
54        TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl");
55  
56    /**
57     * Return true if authorization is supported and enabled
58     * @param connection The connection to use
59     * @return true if authorization is supported and enabled, false otherwise
60     * @throws IOException
61     */
62    public static boolean isAuthorizationEnabled(Connection connection) throws IOException {
63      return connection.getAdmin().getSecurityCapabilities()
64          .contains(SecurityCapability.AUTHORIZATION);
65    }
66  
67    /**
68     * Return true if cell authorization is supported and enabled
69     * @param connection The connection to use
70     * @return true if cell authorization is supported and enabled, false otherwise
71     * @throws IOException
72     */
73    public static boolean isCellAuthorizationEnabled(Connection connection) throws IOException {
74      return connection.getAdmin().getSecurityCapabilities()
75          .contains(SecurityCapability.CELL_AUTHORIZATION);
76    }
77  
78    private static BlockingInterface getAccessControlServiceStub(Table ht)
79        throws IOException {
80      CoprocessorRpcChannel service = ht.coprocessorService(HConstants.EMPTY_START_ROW);
81      BlockingInterface protocol =
82          AccessControlProtos.AccessControlService.newBlockingStub(service);
83      return protocol;
84    }
85  
86    /**
87     * Grants permission on the specified table for the specified user
88     * @param connection The Connection instance to use
89     * @param tableName
90     * @param userName
91     * @param family
92     * @param qual
93     * @param mergeExistingPermissions If set to false, later granted permissions will override
94     *          previous granted permissions. otherwise, it'll merge with previous granted
95     *          permissions.
96     * @param actions
97     * @throws Throwable
98     */
99    public static void grant(Connection connection, final TableName tableName, final String userName,
100       final byte[] family, final byte[] qual, boolean mergeExistingPermissions,
101       final Permission.Action... actions) throws Throwable {
102     HBaseRpcController controller =
103         ((ClusterConnection) connection).getRpcControllerFactory().newController();
104     controller.setPriority(tableName);
105     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
106       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, tableName,
107         family, qual, mergeExistingPermissions, actions);
108     }
109   }
110 
111   /**
112    * Grants permission on the specified table for the specified user. If permissions for a specified
113    * user exists, later granted permissions will override previous granted permissions.
114    * @param connection
115    * @param tableName
116    * @param userName
117    * @param family
118    * @param qual
119    * @param actions
120    * @throws Throwable
121    */
122   public static void grant(final Connection connection, final TableName tableName,
123       final String userName, final byte[] family, final byte[] qual,
124       final Permission.Action... actions) throws Throwable {
125     grant(connection, tableName, userName, family, qual, false, actions);
126   }
127 
128   /**
129    * Grants permission on the specified namespace for the specified user.
130    * @param connection
131    * @param namespace
132    * @param userName
133    * @param mergeExistingPermissions If set to false, later granted permissions will override
134    *          previous granted permissions. otherwise, it'll merge with previous granted
135    *          permissions.
136    * @param actions
137    * @throws Throwable
138    */
139   public static void grant(final Connection connection, final String namespace,
140       final String userName, boolean mergeExistingPermissions, final Permission.Action... actions)
141       throws Throwable {
142     HBaseRpcController controller =
143         ((ClusterConnection) connection).getRpcControllerFactory().newController();
144 
145     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
146       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, namespace,
147         mergeExistingPermissions, actions);
148     }
149   }
150 
151   /**
152    * Grants permission on the specified namespace for the specified user. If permissions for a
153    * specified user exists, later granted permissions will override previous granted permissions.
154    * @param connection The Connection instance to use
155    * @param namespace
156    * @param userName
157    * @param actions
158    * @throws Throwable
159    */
160   public static void grant(final Connection connection, final String namespace,
161       final String userName, final Permission.Action... actions) throws Throwable {
162     grant(connection, namespace, userName, false, actions);
163   }
164 
165   /**
166    * Grant global permissions for the specified user.
167    * @param connection The Connection instance to use
168    * @param userName
169    * @param mergeExistingPermissions If set to false, later granted permissions will override
170    *          previous granted permissions. otherwise, it'll merge with previous granted
171    *          permissions.
172    * @param actions
173    * @throws Throwable
174    */
175   public static void grant(final Connection connection, final String userName,
176       boolean mergeExistingPermissions, final Permission.Action... actions) throws Throwable {
177     HBaseRpcController controller =
178         ((ClusterConnection) connection).getRpcControllerFactory().newController();
179     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
180       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName,
181         mergeExistingPermissions, actions);
182     }
183   }
184 
185   /**
186    * Grant global permissions for the specified user.
187    * @param connection The Connection instance to use
188    * @param userName
189    * @param actions
190    * @throws Throwable
191    */
192   public static void grant(final Connection connection, final String userName,
193       final Permission.Action... actions) throws Throwable {
194     grant(connection, userName, false, actions);
195   }
196 
197   public static boolean isAccessControllerRunning(final Connection connection)
198       throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
199     try (Admin admin = connection.getAdmin()) {
200       return admin.isTableAvailable(ACL_TABLE_NAME);
201     }
202   }
203 
204   /**
205    * Revokes the permission on the table
206    * @param connection The Connection instance to use
207    * @param tableName
208    * @param username
209    * @param family
210    * @param qualifier
211    * @param actions
212    * @throws Throwable
213    */
214   public static void revoke(final Connection connection, final TableName tableName,
215       final String username, final byte[] family, final byte[] qualifier,
216       final Permission.Action... actions) throws Throwable {
217     HBaseRpcController controller
218       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
219     controller.setPriority(tableName);
220     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
221       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), username, tableName,
222         family, qualifier, actions);
223     }
224   }
225 
226   /**
227    * Revokes the permission on the table for the specified user.
228    * @param connection The Connection instance to use
229    * @param namespace
230    * @param userName
231    * @param actions
232    * @throws Throwable
233    */
234   public static void revoke(final Connection connection, final String namespace,
235       final String userName, final Permission.Action... actions) throws Throwable {
236     HBaseRpcController controller
237       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
238     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
239       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), userName, namespace,
240         actions);
241     }
242   }
243 
244   /**
245    * Revoke global permissions for the specified user.
246    * @param connection The Connection instance to use
247    */
248   public static void revoke(final Connection connection, final String userName,
249       final Permission.Action... actions) throws Throwable {
250     HBaseRpcController controller
251       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
252     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
253       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), userName, actions);
254     }
255   }
256 
257   /**
258    * List all the userPermissions matching the given pattern.
259    * @param connection The Connection instance to use
260    * @param tableRegex The regular expression string to match against
261    * @return - returns an array of UserPermissions
262    * @throws Throwable
263    */
264   public static List<UserPermission> getUserPermissions(Connection connection, String tableRegex)
265       throws Throwable {
266     HBaseRpcController controller
267       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
268     List<UserPermission> permList = new ArrayList<UserPermission>();
269     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
270       try (Admin admin = connection.getAdmin()) {
271         CoprocessorRpcChannel service = table.coprocessorService(HConstants.EMPTY_START_ROW);
272         BlockingInterface protocol =
273             AccessControlProtos.AccessControlService.newBlockingStub(service);
274         HTableDescriptor[] htds = null;
275         if (tableRegex == null || tableRegex.isEmpty()) {
276           permList = ProtobufUtil.getUserPermissions(controller, protocol);
277         } else if (tableRegex.charAt(0) == '@') {  // Namespaces
278           String namespaceRegex = tableRegex.substring(1);
279           for (NamespaceDescriptor nsds : admin.listNamespaceDescriptors()) {  // Read out all namespaces
280             String namespace = nsds.getName();
281             if (namespace.matches(namespaceRegex)) {  // Match the given namespace regex?
282               permList.addAll(ProtobufUtil.getUserPermissions(controller, protocol,
283                 Bytes.toBytes(namespace)));
284             }
285           }
286         } else {  // Tables
287           htds = admin.listTables(Pattern.compile(tableRegex), true);
288           for (HTableDescriptor hd : htds) {
289             permList.addAll(ProtobufUtil.getUserPermissions(controller, protocol,
290               hd.getTableName()));
291           }
292         }
293       }
294     }
295     return permList;
296   }
297 
298   /**
299    * Grants permission on the specified table for the specified user
300    * @param conf
301    * @param tableName
302    * @param userName
303    * @param family
304    * @param qual
305    * @param actions
306    * @throws Throwable
307    * @deprecated Use {@link #grant(Connection, TableName, String, byte[], byte[],
308    * Permission.Action...)} instead.
309    */
310   @Deprecated
311   public static void grant(Configuration conf, final TableName tableName,
312       final String userName, final byte[] family, final byte[] qual,
313       final Permission.Action... actions) throws Throwable {
314     try (Connection connection = ConnectionFactory.createConnection(conf)) {
315       grant(connection, tableName, userName, family, qual, actions);
316     }
317   }
318 
319   /**
320    * Grants permission on the specified namespace for the specified user.
321    * @param conf
322    * @param namespace
323    * @param userName
324    * @param actions
325    * @throws Throwable
326    * @deprecated Use {@link #grant(Connection, String, String, Permission.Action...)}
327    * instead.
328    */
329   @Deprecated
330   public static void grant(Configuration conf, final String namespace,
331       final String userName, final Permission.Action... actions) throws Throwable {
332     try (Connection connection = ConnectionFactory.createConnection(conf)) {
333       grant(connection, namespace, userName, actions);
334     }
335   }
336 
337   /**
338    * Grant global permissions for the specified user.
339    * @deprecated Use {@link #grant(Connection, String, Permission.Action...)} instead.
340    */
341   @Deprecated
342   public static void grant(Configuration conf, final String userName,
343       final Permission.Action... actions) throws Throwable {
344     try (Connection connection = ConnectionFactory.createConnection(conf)) {
345       grant(connection, userName, actions);
346     }
347   }
348 
349   /**
350    * @deprecated Use {@link #isAccessControllerRunning(Connection)} instead.
351    */
352   @Deprecated
353   public static boolean isAccessControllerRunning(Configuration conf)
354       throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
355     try (Connection connection = ConnectionFactory.createConnection(conf)) {
356       return isAccessControllerRunning(connection);
357     }
358   }
359 
360   /**
361    * Revokes the permission on the table
362    * @param conf
363    * @param tableName
364    * @param username
365    * @param family
366    * @param qualifier
367    * @param actions
368    * @throws Throwable
369    * @deprecated Use {@link #revoke(Connection, TableName, String, byte[], byte[],
370    * Permission.Action...)} instead.
371    */
372   @Deprecated
373   public static void revoke(Configuration conf, final TableName tableName,
374       final String username, final byte[] family, final byte[] qualifier,
375       final Permission.Action... actions) throws Throwable {
376     try (Connection connection = ConnectionFactory.createConnection(conf)) {
377       revoke(connection, tableName, username, family, qualifier, actions);
378     }
379   }
380 
381   /**
382    * Revokes the permission on the table for the specified user.
383    * @param conf
384    * @param namespace
385    * @param userName
386    * @param actions
387    * @throws Throwable
388    * @deprecated Use {@link #revoke(Connection, String, String, Permission.Action...)} instead.
389    */
390   @Deprecated
391   public static void revoke(Configuration conf, final String namespace,
392       final String userName, final Permission.Action... actions) throws Throwable {
393     try (Connection connection = ConnectionFactory.createConnection(conf)) {
394       revoke(connection, namespace, userName, actions);
395     }
396   }
397 
398   /**
399    * Revoke global permissions for the specified user.
400    * @deprecated Use {@link #revoke(Connection, String, Permission.Action...)} instead.
401    */
402   @Deprecated
403   public static void revoke(Configuration conf, final String userName,
404       final Permission.Action... actions) throws Throwable {
405     try (Connection connection = ConnectionFactory.createConnection(conf)) {
406       revoke(connection, userName, actions);
407     }
408   }
409 
410   /**
411    * List all the userPermissions matching the given pattern. If pattern is null, the behavior is
412    * dependent on whether user has global admin privileges or not. If yes, the global permissions
413    * along with the list of superusers would be returned. Else, no rows get returned.
414    * @param conf
415    * @param tableRegex The regular expression string to match against
416    * @return - returns an array of UserPermissions
417    * @throws Throwable
418    * @deprecated Use {@link #getUserPermissions(Connection, String)} instead.
419    */
420   @Deprecated
421   public static List<UserPermission> getUserPermissions(Configuration conf, String tableRegex)
422   throws Throwable {
423     try (Connection connection = ConnectionFactory.createConnection(conf)) {
424       return getUserPermissions(connection, tableRegex);
425     }
426   }
427 }