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.Map;
24  import java.util.regex.Pattern;
25  
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.hbase.HConstants;
28  import org.apache.hadoop.hbase.HTableDescriptor;
29  import org.apache.hadoop.hbase.MasterNotRunningException;
30  import org.apache.hadoop.hbase.NamespaceDescriptor;
31  import org.apache.hadoop.hbase.TableName;
32  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
33  import org.apache.hadoop.hbase.classification.InterfaceAudience;
34  import org.apache.hadoop.hbase.classification.InterfaceStability;
35  import org.apache.hadoop.hbase.client.HBaseAdmin;
36  import org.apache.hadoop.hbase.client.HTable;
37  import org.apache.hadoop.hbase.client.coprocessor.Batch;
38  import org.apache.hadoop.hbase.ipc.BlockingRpcCallback;
39  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
40  import org.apache.hadoop.hbase.ipc.ServerRpcController;
41  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
42  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
43  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
44  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService.BlockingInterface;
45  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.GrantRequest;
46  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.GrantResponse;
47  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.RevokeRequest;
48  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.RevokeResponse;
49  import org.apache.hadoop.hbase.util.ByteStringer;
50  import org.apache.hadoop.hbase.util.Bytes;
51  
52  import com.google.protobuf.ByteString;
53  
54  /**
55   * Utility client for doing access control admin operations.
56   */
57  @InterfaceAudience.Public
58  @InterfaceStability.Evolving
59  public class AccessControlClient {
60    public static final TableName ACL_TABLE_NAME =
61        TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl");
62  
63    private static HTable getAclTable(Configuration conf) throws IOException {
64      return new HTable(conf, ACL_TABLE_NAME);
65    }
66  
67    private static BlockingInterface getAccessControlServiceStub(HTable ht)
68        throws IOException {
69      CoprocessorRpcChannel service = ht.coprocessorService(HConstants.EMPTY_START_ROW);
70      BlockingInterface protocol =
71          AccessControlProtos.AccessControlService.newBlockingStub(service);
72      return protocol;
73    }
74  
75  
76    /**
77     * Grants permission on the specified table for the specified user
78     * @param conf
79     * @param tableName
80     * @param userName
81     * @param family
82     * @param qual
83     * @param actions
84     * @throws Throwable
85     */
86    public static void grant(Configuration conf, final TableName tableName,
87        final String userName, final byte[] family, final byte[] qual,
88        final Permission.Action... actions) throws Throwable {
89      HTable ht = null;
90      try {
91        ht = getAclTable(conf);
92        ProtobufUtil.grant(getAccessControlServiceStub(ht), userName, tableName, family, qual,
93            actions);
94      } finally {
95        if (ht != null) {
96          ht.close();
97        }
98      }
99    }
100   /**
101    * Grants permission on the specified namespace for the specified user.
102    * @param conf
103    * @param namespace
104    * @param userName
105    * @param actions
106    * @throws Throwable
107    */
108   public static void grant(Configuration conf, final String namespace,
109       final String userName, final Permission.Action... actions) throws Throwable {
110     HTable ht = null;
111     try {
112       ht = getAclTable(conf);
113       ProtobufUtil.grant(getAccessControlServiceStub(ht), userName, namespace, actions);
114     } finally {
115       if (ht != null) {
116         ht.close();
117       }
118     }
119   }
120 
121   /**
122    * Grant global permissions for the specified user.
123    */
124   public static void grant(Configuration conf, final String userName,
125        final Permission.Action... actions) throws Throwable {
126     HTable ht = null;
127     try {
128       ht = getAclTable(conf);
129       ProtobufUtil.grant(getAccessControlServiceStub(ht), userName, actions);
130     } finally {
131       if (ht != null) {
132         ht.close();
133       }
134     }
135   }
136 
137   public static boolean isAccessControllerRunning(Configuration conf)
138       throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
139     HBaseAdmin ha = null;
140     try {
141       ha = new HBaseAdmin(conf);
142       return ha.isTableAvailable(ACL_TABLE_NAME);
143     } finally {
144       if (ha != null) {
145         ha.close();
146       }
147     }
148   }
149   /**
150    * Revokes the permission on the table
151    * @param conf
152    * @param tableName
153    * @param username
154    * @param family
155    * @param qualifier
156    * @param actions
157    * @throws Throwable
158    */
159   public static void revoke(Configuration conf, final TableName tableName,
160       final String username, final byte[] family, final byte[] qualifier,
161       final Permission.Action... actions) throws Throwable {
162     HTable ht = null;
163     try {
164       ht = getAclTable(conf);
165       ProtobufUtil.revoke(getAccessControlServiceStub(ht), username, tableName, family, qualifier,
166           actions);
167     } finally {
168       if (ht != null) {
169         ht.close();
170       }
171     }
172   }
173 
174   /**
175    * Revokes the permission on the table for the specified user.
176    * @param conf
177    * @param namespace
178    * @param userName
179    * @param actions
180    * @throws Throwable
181    */
182   public static void revoke(Configuration conf, final String namespace,
183       final String userName, final Permission.Action... actions) throws Throwable {
184     HTable ht = null;
185     try {
186       ht = getAclTable(conf);
187       ProtobufUtil.revoke(getAccessControlServiceStub(ht), userName, namespace, actions);
188     } finally {
189       if (ht != null) {
190         ht.close();
191       }
192     }
193   }
194 
195   /**
196    * Revoke global permissions for the specified user.
197    */
198   public static void revoke(Configuration conf, final String userName,
199       final Permission.Action... actions) throws Throwable {
200     HTable ht = null;
201     try {
202       ht = getAclTable(conf);
203       ProtobufUtil.revoke(getAccessControlServiceStub(ht), userName, actions);
204     } finally {
205       if (ht != null) {
206         ht.close();
207       }
208     }
209   }
210 
211   /**
212    * List all the userPermissions matching the given pattern.
213    * @param conf
214    * @param tableRegex The regular expression string to match against
215    * @return - returns an array of UserPermissions
216    * @throws Throwable
217    */
218   public static List<UserPermission> getUserPermissions(Configuration conf, String tableRegex)
219       throws Throwable {
220     List<UserPermission> permList = new ArrayList<UserPermission>();
221     HTable ht = null;
222     HBaseAdmin ha = null;
223     try {
224       ha = new HBaseAdmin(conf);
225       ht = new HTable(conf, ACL_TABLE_NAME);
226       CoprocessorRpcChannel service = ht.coprocessorService(HConstants.EMPTY_START_ROW);
227       BlockingInterface protocol =
228           AccessControlProtos.AccessControlService.newBlockingStub(service);
229       HTableDescriptor[] htds = null;
230 
231       if (tableRegex == null || tableRegex.isEmpty()) {
232         permList = ProtobufUtil.getUserPermissions(protocol);
233       } else if (tableRegex.charAt(0) == '@') {
234         String namespace = tableRegex.substring(1);
235         permList = ProtobufUtil.getUserPermissions(protocol, Bytes.toBytes(namespace));
236       } else {
237         htds = ha.listTables(Pattern.compile(tableRegex));
238         for (HTableDescriptor hd : htds) {
239           permList.addAll(ProtobufUtil.getUserPermissions(protocol, hd.getTableName()));
240         }
241       }
242     } finally {
243       if (ht != null) {
244         ht.close();
245       }
246       if (ha != null) {
247         ha.close();
248       }
249     }
250     return permList;
251   }
252 
253   /**
254    * Grants permission on the specified table for the specified user
255    * @param conf
256    * @param tableName
257    * @param userName
258    * @param family
259    * @param qual
260    * @param actions
261    * @return GrantResponse
262    * @throws Throwable
263    * @deprecated Use {@link #grant(Configuration, TableName, String, byte[], byte[], Permission.Action...)}  instead.
264    */
265   @Deprecated
266   public static GrantResponse grant(Configuration conf, final TableName tableName,
267                                     final String userName, final byte[] family, final byte[] qual,
268                                     final AccessControlProtos.Permission.Action... actions) throws Throwable {
269     HTable ht = null;
270     try {
271       TableName aclTableName =
272           TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl");
273       ht = new HTable(conf, aclTableName.getName());
274       Batch.Call<AccessControlService, GrantResponse> callable =
275           new Batch.Call<AccessControlService, GrantResponse>() {
276             ServerRpcController controller = new ServerRpcController();
277             BlockingRpcCallback<GrantResponse> rpcCallback =
278                 new BlockingRpcCallback<GrantResponse>();
279             @Override
280             public GrantResponse call(AccessControlService service) throws IOException {
281               GrantRequest.Builder builder = GrantRequest.newBuilder();
282               AccessControlProtos.Permission.Builder ret =
283                   AccessControlProtos.Permission.newBuilder();
284               AccessControlProtos.TablePermission.Builder permissionBuilder =
285                   AccessControlProtos.TablePermission
286                       .newBuilder();
287               for (AccessControlProtos.Permission.Action a : actions) {
288                 permissionBuilder.addAction(a);
289               }
290               permissionBuilder.setTableName(ProtobufUtil.toProtoTableName(tableName));
291               if (family != null) {
292                 permissionBuilder.setFamily(ByteStringer.wrap(family));
293               }
294               if (qual != null) {
295                 permissionBuilder.setQualifier(ByteStringer.wrap(qual));
296               }
297               ret.setType(AccessControlProtos.Permission.Type.Table).setTablePermission(
298                   permissionBuilder);
299               builder.setUserPermission(AccessControlProtos.UserPermission.newBuilder()
300                   .setUser(ByteString.copyFromUtf8(userName)).setPermission(ret));
301               service.grant(controller, builder.build(), rpcCallback);
302               return rpcCallback.get();
303             }
304           };
305       Map<byte[], GrantResponse> result = ht.coprocessorService(AccessControlService.class,
306           HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, callable);
307       return result.values().iterator().next(); // There will be exactly one
308                                                 // region for labels
309                                                 // table and so one entry in
310                                                 // result Map.
311     } finally {
312       if (ht != null) {
313         ht.close();
314       }
315     }
316   }
317 
318   /**
319    * Revokes the permission on the table
320    * @param conf
321    * @param username
322    * @param tableName
323    * @param family
324    * @param qualifier
325    * @param actions
326    * @return RevokeResponse
327    * @throws Throwable
328    * @deprecated Use {@link #revoke(Configuration, TableName, String, byte[], byte[], Permission.Action...)} instead
329    */
330   @Deprecated
331   public static RevokeResponse revoke(Configuration conf, final String username,
332                                       final TableName tableName, final byte[] family, final byte[] qualifier,
333                                       final AccessControlProtos.Permission.Action... actions) throws Throwable {
334     HTable ht = null;
335     try {
336       TableName aclTableName = TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR,
337           "acl");
338       ht = new HTable(conf, aclTableName.getName());
339       Batch.Call<AccessControlService, AccessControlProtos.RevokeResponse> callable =
340           new Batch.Call<AccessControlService, AccessControlProtos.RevokeResponse>() {
341             ServerRpcController controller = new ServerRpcController();
342             BlockingRpcCallback<AccessControlProtos.RevokeResponse> rpcCallback =
343                 new BlockingRpcCallback<AccessControlProtos.RevokeResponse>();
344             @Override
345             public RevokeResponse call(AccessControlService service) throws IOException {
346               AccessControlProtos.Permission.Builder ret =
347                   AccessControlProtos.Permission.newBuilder();
348               AccessControlProtos.TablePermission.Builder permissionBuilder =
349                   AccessControlProtos.TablePermission.newBuilder();
350               for (AccessControlProtos.Permission.Action a : actions) {
351                 permissionBuilder.addAction(a);
352               }
353               if (tableName != null) {
354                 permissionBuilder.setTableName(ProtobufUtil.toProtoTableName(tableName));
355               }
356               if (family != null) {
357                 permissionBuilder.setFamily(ByteStringer.wrap(family));
358               }
359               if (qualifier != null) {
360                 permissionBuilder.setQualifier(ByteStringer.wrap(qualifier));
361               }
362               ret.setType(AccessControlProtos.Permission.Type.Table).setTablePermission(
363                   permissionBuilder);
364               RevokeRequest builder = AccessControlProtos.RevokeRequest
365                   .newBuilder()
366                   .setUserPermission(
367                       AccessControlProtos.UserPermission.newBuilder()
368                           .setUser(ByteString.copyFromUtf8(username)).setPermission(ret)).build();
369               service.revoke(controller, builder, rpcCallback);
370               return rpcCallback.get();
371             }
372           };
373       Map<byte[], RevokeResponse> result = ht.coprocessorService(AccessControlService.class,
374           HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, callable);
375       return result.values().iterator().next();
376     } finally {
377       if (ht != null) {
378         ht.close();
379       }
380     }
381   }
382 }