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