001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security.access; 019 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.regex.Pattern; 024 025import org.apache.hadoop.hbase.HConstants; 026import org.apache.hadoop.hbase.HTableDescriptor; 027import org.apache.hadoop.hbase.MasterNotRunningException; 028import org.apache.hadoop.hbase.NamespaceDescriptor; 029import org.apache.hadoop.hbase.TableName; 030import org.apache.hadoop.hbase.ZooKeeperConnectionException; 031import org.apache.yetus.audience.InterfaceAudience; 032import org.apache.hadoop.hbase.client.Admin; 033import org.apache.hadoop.hbase.client.Connection; 034import org.apache.hadoop.hbase.client.Table; 035import org.apache.hadoop.hbase.client.security.SecurityCapability; 036import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; 037import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos; 038import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService.BlockingInterface; 039import org.apache.hadoop.hbase.util.Bytes; 040 041/** 042 * Utility client for doing access control admin operations. 043 */ 044@InterfaceAudience.Public 045public class AccessControlClient { 046 public static final TableName ACL_TABLE_NAME = 047 TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl"); 048 049 /** 050 * Return true if authorization is supported and enabled 051 * @param connection The connection to use 052 * @return true if authorization is supported and enabled, false otherwise 053 * @throws IOException 054 */ 055 public static boolean isAuthorizationEnabled(Connection connection) throws IOException { 056 return connection.getAdmin().getSecurityCapabilities() 057 .contains(SecurityCapability.AUTHORIZATION); 058 } 059 060 /** 061 * Return true if cell authorization is supported and enabled 062 * @param connection The connection to use 063 * @return true if cell authorization is supported and enabled, false otherwise 064 * @throws IOException 065 */ 066 public static boolean isCellAuthorizationEnabled(Connection connection) throws IOException { 067 return connection.getAdmin().getSecurityCapabilities() 068 .contains(SecurityCapability.CELL_AUTHORIZATION); 069 } 070 071 private static BlockingInterface getAccessControlServiceStub(Table ht) 072 throws IOException { 073 CoprocessorRpcChannel service = ht.coprocessorService(HConstants.EMPTY_START_ROW); 074 BlockingInterface protocol = 075 AccessControlProtos.AccessControlService.newBlockingStub(service); 076 return protocol; 077 } 078 079 /** 080 * Grants permission on the specified table for the specified user 081 * @param connection The Connection instance to use 082 * @param tableName 083 * @param userName 084 * @param family 085 * @param qual 086 * @param mergeExistingPermissions If set to false, later granted permissions will override 087 * previous granted permissions. otherwise, it'll merge with previous granted 088 * permissions. 089 * @param actions 090 * @throws Throwable 091 */ 092 private static void grant(Connection connection, final TableName tableName, 093 final String userName, final byte[] family, final byte[] qual, boolean mergeExistingPermissions, 094 final Permission.Action... actions) throws Throwable { 095 // TODO: Priority is not used. 096 try (Table table = connection.getTable(ACL_TABLE_NAME)) { 097 AccessControlUtil.grant(null, getAccessControlServiceStub(table), userName, tableName, 098 family, qual, mergeExistingPermissions, actions); 099 } 100 } 101 102 /** 103 * Grants permission on the specified table for the specified user. 104 * If permissions for a specified user exists, later granted permissions will override previous granted permissions. 105 * @param connection The Connection instance to use 106 * @param tableName 107 * @param userName 108 * @param family 109 * @param qual 110 * @param actions 111 * @throws Throwable 112 */ 113 public static void grant(Connection connection, final TableName tableName, final String userName, 114 final byte[] family, final byte[] qual, final Permission.Action... actions) throws Throwable { 115 grant(connection, tableName, userName, family, qual, true, actions); 116 } 117 118 /** 119 * Grants permission on the specified namespace for the specified user. 120 * @param connection 121 * @param namespace 122 * @param userName 123 * @param mergeExistingPermissions If set to false, later granted permissions will override 124 * previous granted permissions. otherwise, it'll merge with previous granted 125 * permissions. 126 * @param actions 127 * @throws Throwable 128 */ 129 private static void grant(Connection connection, final String namespace, final String userName, 130 boolean mergeExistingPermissions, final Permission.Action... actions) throws Throwable { 131 // TODO: Pass an rpcController. 132 try (Table table = connection.getTable(ACL_TABLE_NAME)) { 133 AccessControlUtil.grant(null, getAccessControlServiceStub(table), userName, namespace, 134 mergeExistingPermissions, actions); 135 } 136 } 137 138 /** 139 * Grants permission on the specified namespace for the specified user. 140 * If permissions on the specified namespace exists, later granted permissions will override previous granted 141 * permissions. 142 * @param connection The Connection instance to use 143 * @param namespace 144 * @param userName 145 * @param actions 146 * @throws Throwable 147 */ 148 public static void grant(Connection connection, final String namespace, final String userName, 149 final Permission.Action... actions) throws Throwable { 150 grant(connection, namespace, userName, true, actions); 151 } 152 153 /** 154 * Grants permission on the specified namespace for the specified user. 155 * @param connection 156 * @param userName 157 * @param mergeExistingPermissions If set to false, later granted permissions will override 158 * previous granted permissions. otherwise, it'll merge with previous granted 159 * permissions. 160 * @param actions 161 * @throws Throwable 162 */ 163 private static void grant(Connection connection, final String userName, 164 boolean mergeExistingPermissions, final Permission.Action... actions) throws Throwable { 165 // TODO: Pass an rpcController 166 try (Table table = connection.getTable(ACL_TABLE_NAME)) { 167 AccessControlUtil.grant(null, getAccessControlServiceStub(table), userName, 168 mergeExistingPermissions, actions); 169 } 170 } 171 172 /** 173 * Grant global permissions for the specified user. 174 * If permissions for the specified user exists, later granted permissions will override previous granted 175 * permissions. 176 * @param connection 177 * @param userName 178 * @param actions 179 * @throws Throwable 180 */ 181 public static void grant(Connection connection, final String userName, 182 final Permission.Action... actions) throws Throwable { 183 grant(connection, userName, true, actions); 184 } 185 186 public static boolean isAccessControllerRunning(Connection connection) 187 throws MasterNotRunningException, ZooKeeperConnectionException, IOException { 188 try (Admin admin = connection.getAdmin()) { 189 return admin.isTableAvailable(ACL_TABLE_NAME); 190 } 191 } 192 193 /** 194 * Revokes the permission on the table 195 * @param connection The Connection instance to use 196 * @param tableName 197 * @param username 198 * @param family 199 * @param qualifier 200 * @param actions 201 * @throws Throwable 202 */ 203 public static void revoke(Connection connection, final TableName tableName, 204 final String username, final byte[] family, final byte[] qualifier, 205 final Permission.Action... actions) throws Throwable { 206 /** TODO: Pass an rpcController 207 HBaseRpcController controller 208 = ((ClusterConnection) connection).getRpcControllerFactory().newController(); 209 controller.setPriority(tableName); 210 */ 211 try (Table table = connection.getTable(ACL_TABLE_NAME)) { 212 AccessControlUtil.revoke(null, getAccessControlServiceStub(table), username, tableName, 213 family, qualifier, actions); 214 } 215 } 216 217 /** 218 * Revokes the permission on the table for the specified user. 219 * @param connection The Connection instance to use 220 * @param namespace 221 * @param userName 222 * @param actions 223 * @throws Throwable 224 */ 225 public static void revoke(Connection connection, final String namespace, 226 final String userName, final Permission.Action... actions) throws Throwable { 227 /** TODO: Pass an rpcController 228 HBaseRpcController controller 229 = ((ClusterConnection) connection).getRpcControllerFactory().newController(); 230 */ 231 try (Table table = connection.getTable(ACL_TABLE_NAME)) { 232 AccessControlUtil.revoke(null, getAccessControlServiceStub(table), userName, namespace, 233 actions); 234 } 235 } 236 237 /** 238 * Revoke global permissions for the specified user. 239 * @param connection The Connection instance to use 240 */ 241 public static void revoke(Connection connection, final String userName, 242 final Permission.Action... actions) throws Throwable { 243 /** TODO: Pass an rpc controller. 244 HBaseRpcController controller 245 = ((ClusterConnection) connection).getRpcControllerFactory().newController(); 246 */ 247 try (Table table = connection.getTable(ACL_TABLE_NAME)) { 248 AccessControlUtil.revoke(null, getAccessControlServiceStub(table), userName, actions); 249 } 250 } 251 252 /** 253 * List all the userPermissions matching the given pattern. If pattern is null, the behavior is 254 * dependent on whether user has global admin privileges or not. If yes, the global permissions 255 * along with the list of superusers would be returned. Else, no rows get returned. 256 * @param connection The Connection instance to use 257 * @param tableRegex The regular expression string to match against 258 * @return - returns an array of UserPermissions 259 * @throws Throwable 260 */ 261 public static List<UserPermission> getUserPermissions(Connection connection, String tableRegex) 262 throws Throwable { 263 /** TODO: Pass an rpcController 264 HBaseRpcController controller 265 = ((ClusterConnection) connection).getRpcControllerFactory().newController(); 266 */ 267 List<UserPermission> permList = new ArrayList<>(); 268 try (Table table = connection.getTable(ACL_TABLE_NAME)) { 269 try (Admin admin = connection.getAdmin()) { 270 CoprocessorRpcChannel service = table.coprocessorService(HConstants.EMPTY_START_ROW); 271 BlockingInterface protocol = 272 AccessControlProtos.AccessControlService.newBlockingStub(service); 273 HTableDescriptor[] htds = null; 274 if (tableRegex == null || tableRegex.isEmpty()) { 275 permList = AccessControlUtil.getUserPermissions(null, protocol); 276 } else if (tableRegex.charAt(0) == '@') { // Namespaces 277 String namespaceRegex = tableRegex.substring(1); 278 for (NamespaceDescriptor nsds : admin.listNamespaceDescriptors()) { // Read out all namespaces 279 String namespace = nsds.getName(); 280 if (namespace.matches(namespaceRegex)) { // Match the given namespace regex? 281 permList.addAll(AccessControlUtil.getUserPermissions(null, protocol, 282 Bytes.toBytes(namespace))); 283 } 284 } 285 } else { // Tables 286 htds = admin.listTables(Pattern.compile(tableRegex), true); 287 for (HTableDescriptor hd : htds) { 288 permList.addAll(AccessControlUtil.getUserPermissions(null, protocol, 289 hd.getTableName())); 290 } 291 } 292 } 293 } 294 return permList; 295 } 296}