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.Connection;
36  import org.apache.hadoop.hbase.client.ConnectionFactory;
37  import org.apache.hadoop.hbase.client.Table;
38  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
39  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
40  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
41  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService.BlockingInterface;
42  import org.apache.hadoop.hbase.util.Bytes;
43  
44  /**
45   * Utility client for doing access control admin operations.
46   */
47  @InterfaceAudience.Public
48  @InterfaceStability.Evolving
49  public class AccessControlClient {
50    public static final TableName ACL_TABLE_NAME =
51        TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl");
52  
53    private static BlockingInterface getAccessControlServiceStub(Table ht)
54        throws IOException {
55      CoprocessorRpcChannel service = ht.coprocessorService(HConstants.EMPTY_START_ROW);
56      BlockingInterface protocol =
57          AccessControlProtos.AccessControlService.newBlockingStub(service);
58      return protocol;
59    }
60  
61    /**
62     * Grants permission on the specified table for the specified user
63     * @param conf
64     * @param tableName
65     * @param userName
66     * @param family
67     * @param qual
68     * @param actions
69     * @throws Throwable
70     */
71    public static void grant(Configuration conf, final TableName tableName,
72        final String userName, final byte[] family, final byte[] qual,
73        final Permission.Action... actions) throws Throwable {
74      // TODO: Make it so caller passes in a Connection rather than have us do this expensive
75      // setup each time.  This class only used in test and shell at moment though.
76      try (Connection connection = ConnectionFactory.createConnection(conf)) {
77        try (Table table = connection.getTable(ACL_TABLE_NAME)) {
78          ProtobufUtil.grant(getAccessControlServiceStub(table), userName, tableName, family, qual,
79            actions);
80        }
81      }
82    }
83  
84    /**
85     * Grants permission on the specified namespace for the specified user.
86     * @param conf
87     * @param namespace
88     * @param userName
89     * @param actions
90     * @throws Throwable
91     */
92    public static void grant(Configuration conf, final String namespace,
93        final String userName, final Permission.Action... actions) throws Throwable {
94      // TODO: Make it so caller passes in a Connection rather than have us do this expensive
95      // setup each time.  This class only used in test and shell at moment though.
96      try (Connection connection = ConnectionFactory.createConnection(conf)) {
97        try (Table table = connection.getTable(ACL_TABLE_NAME)) {
98          ProtobufUtil.grant(getAccessControlServiceStub(table), userName, namespace, actions);
99        }
100     }
101   }
102 
103   /**
104    * Grant global permissions for the specified user.
105    */
106   public static void grant(Configuration conf, final String userName,
107        final Permission.Action... actions) throws Throwable {
108     // TODO: Make it so caller passes in a Connection rather than have us do this expensive
109     // setup each time.  This class only used in test and shell at moment though.
110     try (Connection connection = ConnectionFactory.createConnection(conf)) {
111       try (Table table = connection.getTable(ACL_TABLE_NAME)) {
112         ProtobufUtil.grant(getAccessControlServiceStub(table), userName, actions);
113       }
114     }
115   }
116 
117   public static boolean isAccessControllerRunning(Configuration conf)
118       throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
119     // TODO: Make it so caller passes in a Connection rather than have us do this expensive
120     // setup each time.  This class only used in test and shell at moment though.
121     try (Connection connection = ConnectionFactory.createConnection(conf)) {
122       try (Admin admin = connection.getAdmin()) {
123         return admin.isTableAvailable(ACL_TABLE_NAME);
124       }
125     }
126   }
127 
128   /**
129    * Revokes the permission on the table
130    * @param conf
131    * @param tableName
132    * @param username
133    * @param family
134    * @param qualifier
135    * @param actions
136    * @throws Throwable
137    */
138   public static void revoke(Configuration conf, final TableName tableName,
139       final String username, final byte[] family, final byte[] qualifier,
140       final Permission.Action... actions) throws Throwable {
141     // TODO: Make it so caller passes in a Connection rather than have us do this expensive
142     // setup each time.  This class only used in test and shell at moment though.
143     try (Connection connection = ConnectionFactory.createConnection(conf)) {
144       try (Table table = connection.getTable(ACL_TABLE_NAME)) {
145         ProtobufUtil.revoke(getAccessControlServiceStub(table), username, tableName, family,
146           qualifier, actions);
147       }
148     }
149   }
150 
151   /**
152    * Revokes the permission on the table for the specified user.
153    * @param conf
154    * @param namespace
155    * @param userName
156    * @param actions
157    * @throws Throwable
158    */
159   public static void revoke(Configuration conf, final String namespace,
160     final String userName, final Permission.Action... actions) throws Throwable {
161     // TODO: Make it so caller passes in a Connection rather than have us do this expensive
162     // setup each time.  This class only used in test and shell at moment though.
163     try (Connection connection = ConnectionFactory.createConnection(conf)) {
164       try (Table table = connection.getTable(ACL_TABLE_NAME)) {
165         ProtobufUtil.revoke(getAccessControlServiceStub(table), userName, namespace, actions);
166       }
167     }
168   }
169 
170   /**
171    * Revoke global permissions for the specified user.
172    */
173   public static void revoke(Configuration conf, final String userName,
174       final Permission.Action... actions) throws Throwable {
175     // TODO: Make it so caller passes in a Connection rather than have us do this expensive
176     // setup each time.  This class only used in test and shell at moment though.
177     try (Connection connection = ConnectionFactory.createConnection(conf)) {
178       try (Table table = connection.getTable(ACL_TABLE_NAME)) {
179         ProtobufUtil.revoke(getAccessControlServiceStub(table), userName, actions);
180       }
181     }
182   }
183 
184   /**
185    * List all the userPermissions matching the given pattern.
186    * @param conf
187    * @param tableRegex The regular expression string to match against
188    * @return - returns an array of UserPermissions
189    * @throws Throwable
190    */
191   public static List<UserPermission> getUserPermissions(Configuration conf, String tableRegex)
192       throws Throwable {
193     List<UserPermission> permList = new ArrayList<UserPermission>();
194     // TODO: Make it so caller passes in a Connection rather than have us do this expensive
195     // setup each time.  This class only used in test and shell at moment though.
196     try (Connection connection = ConnectionFactory.createConnection(conf)) {
197       try (Table table = connection.getTable(ACL_TABLE_NAME)) {
198         try (Admin admin = connection.getAdmin()) {
199           CoprocessorRpcChannel service = table.coprocessorService(HConstants.EMPTY_START_ROW);
200           BlockingInterface protocol =
201             AccessControlProtos.AccessControlService.newBlockingStub(service);
202           HTableDescriptor[] htds = null;
203           if (tableRegex == null || tableRegex.isEmpty()) {
204             permList = ProtobufUtil.getUserPermissions(protocol);
205           } else if (tableRegex.charAt(0) == '@') {
206             String namespace = tableRegex.substring(1);
207             permList = ProtobufUtil.getUserPermissions(protocol, Bytes.toBytes(namespace));
208           } else {
209             htds = admin.listTables(Pattern.compile(tableRegex), true);
210             for (HTableDescriptor hd : htds) {
211               permList.addAll(ProtobufUtil.getUserPermissions(protocol, hd.getTableName()));
212             }
213           }
214         }
215       }
216     }
217     return permList;
218   }
219 }