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.hbase.HConstants;
26  import org.apache.hadoop.hbase.HTableDescriptor;
27  import org.apache.hadoop.hbase.MasterNotRunningException;
28  import org.apache.hadoop.hbase.NamespaceDescriptor;
29  import org.apache.hadoop.hbase.TableName;
30  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  import org.apache.hadoop.hbase.classification.InterfaceStability;
33  import org.apache.hadoop.hbase.client.Admin;
34  import org.apache.hadoop.hbase.client.ClusterConnection;
35  import org.apache.hadoop.hbase.client.Connection;
36  import org.apache.hadoop.hbase.client.Table;
37  import org.apache.hadoop.hbase.client.security.SecurityCapability;
38  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
39  import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
40  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
41  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
42  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService.BlockingInterface;
43  import org.apache.hadoop.hbase.util.Bytes;
44  
45  /**
46   * Utility client for doing access control admin operations.
47   */
48  @InterfaceAudience.Public
49  @InterfaceStability.Evolving
50  public class AccessControlClient {
51    public static final TableName ACL_TABLE_NAME =
52        TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl");
53  
54    /**
55     * Return true if authorization is supported and enabled
56     * @param connection The connection to use
57     * @return true if authorization is supported and enabled, false otherwise
58     * @throws IOException
59     */
60    public static boolean isAuthorizationEnabled(Connection connection) throws IOException {
61      return connection.getAdmin().getSecurityCapabilities()
62          .contains(SecurityCapability.AUTHORIZATION);
63    }
64  
65    /**
66     * Return true if cell authorization is supported and enabled
67     * @param connection The connection to use
68     * @return true if cell authorization is supported and enabled, false otherwise
69     * @throws IOException
70     */
71    public static boolean isCellAuthorizationEnabled(Connection connection) throws IOException {
72      return connection.getAdmin().getSecurityCapabilities()
73          .contains(SecurityCapability.CELL_AUTHORIZATION);
74    }
75  
76    private static BlockingInterface getAccessControlServiceStub(Table ht)
77        throws IOException {
78      CoprocessorRpcChannel service = ht.coprocessorService(HConstants.EMPTY_START_ROW);
79      BlockingInterface protocol =
80          AccessControlProtos.AccessControlService.newBlockingStub(service);
81      return protocol;
82    }
83  
84    /**
85     * Grants permission on the specified table for the specified user
86     * @param connection The Connection instance to use
87     * @param tableName
88     * @param userName
89     * @param family
90     * @param qual
91     * @param actions
92     * @throws Throwable
93     */
94    public static void grant(Connection connection, final TableName tableName,
95        final String userName, final byte[] family, final byte[] qual,
96        final Permission.Action... actions) throws Throwable {
97      PayloadCarryingRpcController controller
98        = ((ClusterConnection) connection).getRpcControllerFactory().newController();
99      controller.setPriority(tableName);
100     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
101       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, tableName,
102         family, qual, actions);
103     }
104   }
105 
106   /**
107    * Grants permission on the specified namespace for the specified user.
108    * @param connection The Connection instance to use
109    * @param namespace
110    * @param userName
111    * @param actions
112    * @throws Throwable
113    */
114   public static void grant(Connection connection, final String namespace,
115       final String userName, final Permission.Action... actions) throws Throwable {
116     PayloadCarryingRpcController controller
117       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
118 
119     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
120       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, namespace,
121         actions);
122     }
123   }
124 
125   /**
126    * @param connection The Connection instance to use
127    * Grant global permissions for the specified user.
128    */
129   public static void grant(Connection connection, final String userName,
130        final Permission.Action... actions) throws Throwable {
131     PayloadCarryingRpcController controller
132       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
133     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
134       ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, actions);
135     }
136   }
137 
138   public static boolean isAccessControllerRunning(Connection connection)
139       throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
140     try (Admin admin = connection.getAdmin()) {
141       return admin.isTableAvailable(ACL_TABLE_NAME);
142     }
143   }
144 
145   /**
146    * Revokes the permission on the table
147    * @param connection The Connection instance to use
148    * @param tableName
149    * @param username
150    * @param family
151    * @param qualifier
152    * @param actions
153    * @throws Throwable
154    */
155   public static void revoke(Connection connection, final TableName tableName,
156       final String username, final byte[] family, final byte[] qualifier,
157       final Permission.Action... actions) throws Throwable {
158     PayloadCarryingRpcController controller
159       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
160     controller.setPriority(tableName);
161     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
162       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), username, tableName,
163         family, qualifier, actions);
164     }
165   }
166 
167   /**
168    * Revokes the permission on the table for the specified user.
169    * @param connection The Connection instance to use
170    * @param namespace
171    * @param userName
172    * @param actions
173    * @throws Throwable
174    */
175   public static void revoke(Connection connection, final String namespace,
176       final String userName, final Permission.Action... actions) throws Throwable {
177     PayloadCarryingRpcController controller
178       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
179     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
180       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), userName, namespace,
181         actions);
182     }
183   }
184 
185   /**
186    * Revoke global permissions for the specified user.
187    * @param connection The Connection instance to use
188    */
189   public static void revoke(Connection connection, final String userName,
190       final Permission.Action... actions) throws Throwable {
191     PayloadCarryingRpcController controller
192       = ((ClusterConnection) connection).getRpcControllerFactory().newController();
193     try (Table table = connection.getTable(ACL_TABLE_NAME)) {
194       ProtobufUtil.revoke(controller, getAccessControlServiceStub(table), userName, actions);
195     }
196   }
197 
198   /**
199    * List all the userPermissions matching the given pattern. If pattern is null, the behavior is
200    * dependent on whether user has global admin privileges or not. If yes, the global permissions
201    * along with the list of superusers would be returned. Else, no rows get returned.
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) == '@') {
221           String namespace = tableRegex.substring(1);
222           permList = ProtobufUtil.getUserPermissions(controller, protocol,
223             Bytes.toBytes(namespace));
224         } else {
225           htds = admin.listTables(Pattern.compile(tableRegex), true);
226           for (HTableDescriptor hd : htds) {
227             permList.addAll(ProtobufUtil.getUserPermissions(controller, protocol,
228               hd.getTableName()));
229           }
230         }
231       }
232     }
233     return permList;
234   }
235 }