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.PayloadCarryingRpcController;
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 actions
94     * @throws Throwable
95     */
96    public static void grant(final Connection connection, final TableName tableName,
97        final String userName, final byte[] family, final byte[] qual,
98        final Permission.Action... actions) throws Throwable {
99      PayloadCarryingRpcController controller
100       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
101     controller.setPriority(tableName);
102     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
103       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, tableName,
104         family, qual, actions);
105     }
106   }
107 
108   /**
109    * Grants permission on the specified namespace for the specified user.
110    * @param connection The Connection instance to use
111    * @param namespace
112    * @param userName
113    * @param actions
114    * @throws Throwable
115    */
116   public static void grant(final Connection connection, final String namespace,
117       final String userName, final Permission.Action... actions) throws Throwable {
118     PayloadCarryingRpcController controller
119       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
120 
121     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
122       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, namespace,
123         actions);
124     }
125   }
126 
127   /**
128    * @param connection The Connection instance to use
129    * Grant global permissions for the specified user.
130    */
131   public static void grant(final Connection connection, final String userName,
132        final Permission.Action... actions) throws Throwable {
133     PayloadCarryingRpcController controller
134       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
135     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
136       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, actions);
137     }
138   }
139 
140   public static boolean isAccessControllerRunning(final Connection connection)
141       throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
142     try (Admin admin = connection.getAdmin()) {
143       return admin.isTableAvailable(ACL_TABLE_NAME);
144     }
145   }
146 
147   /**
148    * Revokes the permission on the table
149    * @param connection The Connection instance to use
150    * @param tableName
151    * @param username
152    * @param family
153    * @param qualifier
154    * @param actions
155    * @throws Throwable
156    */
157   public static void revoke(final Connection connection, final TableName tableName,
158       final String username, final byte[] family, final byte[] qualifier,
159       final Permission.Action... actions) throws Throwable {
160     PayloadCarryingRpcController controller
161       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
162     controller.setPriority(tableName);
163     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
164       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), username, tableName,
165         family, qualifier, actions);
166     }
167   }
168 
169   /**
170    * Revokes the permission on the table for the specified user.
171    * @param connection The Connection instance to use
172    * @param namespace
173    * @param userName
174    * @param actions
175    * @throws Throwable
176    */
177   public static void revoke(final Connection connection, final String namespace,
178       final String userName, final Permission.Action... actions) throws Throwable {
179     PayloadCarryingRpcController controller
180       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
181     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
182       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), userName, namespace,
183         actions);
184     }
185   }
186 
187   /**
188    * Revoke global permissions for the specified user.
189    * @param connection The Connection instance to use
190    */
191   public static void revoke(final Connection connection, final String userName,
192       final Permission.Action... actions) throws Throwable {
193     PayloadCarryingRpcController controller
194       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
195     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
196       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), userName, actions);
197     }
198   }
199 
200   /**
201    * List all the userPermissions matching the given pattern.
202    * @param connection The Connection instance to use
203    * @param tableRegex The regular expression string to match against
204    * @return - returns an array of UserPermissions
205    * @throws Throwable
206    */
207   public static List<UserPermission> getUserPermissions(Connection connection, String tableRegex)
208       throws Throwable {
209     PayloadCarryingRpcController controller
210       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
211     List<UserPermission> permList = new ArrayList<UserPermission>();
212     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
213       try (Admin admin = connection.getAdmin()) {
214         CoprocessorRpcChannel service = table.coprocessorService(HConstants.EMPTY_START_ROW);
215         BlockingInterface protocol =
216             AccessControlProtos.AccessControlService.newBlockingStub(service);
217         HTableDescriptor[] htds = null;
218         if (tableRegex == null || tableRegex.isEmpty()) {
219           permList = ProtobufUtil.getUserPermissions(controller, protocol);
220         } else if (tableRegex.charAt(0) == '@') {  // Namespaces
221           String namespaceRegex = tableRegex.substring(1);
222           for (NamespaceDescriptor nsds : admin.listNamespaceDescriptors()) {  // Read out all namespaces
223             String namespace = nsds.getName();
224             if (namespace.matches(namespaceRegex)) {  // Match the given namespace regex?
225               permList.addAll(ProtobufUtil.getUserPermissions(controller, protocol,
226                 Bytes.toBytes(namespace)));
227             }
228           }
229         } else {  // Tables
230           htds = admin.listTables(Pattern.compile(tableRegex), true);
231           for (HTableDescriptor hd : htds) {
232             permList.addAll(ProtobufUtil.getUserPermissions(controller, protocol,
233               hd.getTableName()));
234           }
235         }
236       }
237     }
238     return permList;
239   }
240 
241   /**
242    * Grants permission on the specified table for the specified user
243    * @param conf
244    * @param tableName
245    * @param userName
246    * @param family
247    * @param qual
248    * @param actions
249    * @throws Throwable
250    * @deprecated Use {@link #grant(Connection, TableName, String, byte[], byte[],
251    * Permission.Action...)} instead.
252    */
253   @Deprecated
254   public static void grant(Configuration conf, final TableName tableName,
255       final String userName, final byte[] family, final byte[] qual,
256       final Permission.Action... actions) throws Throwable {
257     try (Connection connection = ConnectionFactory.createConnection(conf)) {
258       grant(connection, tableName, userName, family, qual, actions);
259     }
260   }
261 
262   /**
263    * Grants permission on the specified namespace for the specified user.
264    * @param conf
265    * @param namespace
266    * @param userName
267    * @param actions
268    * @throws Throwable
269    * @deprecated Use {@link #grant(Connection, String, String, Permission.Action...)}
270    * instead.
271    */
272   @Deprecated
273   public static void grant(Configuration conf, final String namespace,
274       final String userName, final Permission.Action... actions) throws Throwable {
275     try (Connection connection = ConnectionFactory.createConnection(conf)) {
276       grant(connection, namespace, userName, actions);
277     }
278   }
279 
280   /**
281    * Grant global permissions for the specified user.
282    * @deprecated Use {@link #grant(Connection, String, Permission.Action...)} instead.
283    */
284   @Deprecated
285   public static void grant(Configuration conf, final String userName,
286       final Permission.Action... actions) throws Throwable {
287     try (Connection connection = ConnectionFactory.createConnection(conf)) {
288       grant(connection, userName, actions);
289     }
290   }
291 
292   /**
293    * @deprecated Use {@link #isAccessControllerRunning(Connection)} instead.
294    */
295   @Deprecated
296   public static boolean isAccessControllerRunning(Configuration conf)
297       throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
298     try (Connection connection = ConnectionFactory.createConnection(conf)) {
299       return isAccessControllerRunning(connection);
300     }
301   }
302 
303   /**
304    * Revokes the permission on the table
305    * @param conf
306    * @param tableName
307    * @param username
308    * @param family
309    * @param qualifier
310    * @param actions
311    * @throws Throwable
312    * @deprecated Use {@link #revoke(Connection, TableName, String, byte[], byte[],
313    * Permission.Action...)} instead.
314    */
315   @Deprecated
316   public static void revoke(Configuration conf, final TableName tableName,
317       final String username, final byte[] family, final byte[] qualifier,
318       final Permission.Action... actions) throws Throwable {
319     try (Connection connection = ConnectionFactory.createConnection(conf)) {
320       revoke(connection, tableName, username, family, qualifier, actions);
321     }
322   }
323 
324   /**
325    * Revokes the permission on the table for the specified user.
326    * @param conf
327    * @param namespace
328    * @param userName
329    * @param actions
330    * @throws Throwable
331    * @deprecated Use {@link #revoke(Connection, String, String, Permission.Action...)} instead.
332    */
333   @Deprecated
334   public static void revoke(Configuration conf, final String namespace,
335       final String userName, final Permission.Action... actions) throws Throwable {
336     try (Connection connection = ConnectionFactory.createConnection(conf)) {
337       revoke(connection, namespace, userName, actions);
338     }
339   }
340 
341   /**
342    * Revoke global permissions for the specified user.
343    * @deprecated Use {@link #revoke(Connection, String, Permission.Action...)} instead.
344    */
345   @Deprecated
346   public static void revoke(Configuration conf, final String userName,
347       final Permission.Action... actions) throws Throwable {
348     try (Connection connection = ConnectionFactory.createConnection(conf)) {
349       revoke(connection, userName, actions);
350     }
351   }
352 
353   /**
354    * List all the userPermissions matching the given pattern.
355    * @param conf
356    * @param tableRegex The regular expression string to match against
357    * @return - returns an array of UserPermissions
358    * @throws Throwable
359    * @deprecated Use {@link #getUserPermissions(Connection, String)} instead.
360    */
361   @Deprecated
362   public static List<UserPermission> getUserPermissions(Configuration conf, String tableRegex)
363   throws Throwable {
364     try (Connection connection = ConnectionFactory.createConnection(conf)) {
365       return getUserPermissions(connection, tableRegex);
366     }
367   }
368 }