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.visibility; 19 20 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME; 21 22 import java.io.IOException; 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.client.Table; 29 import org.apache.hadoop.hbase.util.ByteStringer; 30 import org.apache.hadoop.hbase.classification.InterfaceAudience; 31 import org.apache.hadoop.hbase.classification.InterfaceStability; 32 import org.apache.hadoop.hbase.client.Connection; 33 import org.apache.hadoop.hbase.client.ConnectionFactory; 34 import org.apache.hadoop.hbase.client.Table; 35 import org.apache.hadoop.hbase.client.coprocessor.Batch; 36 import org.apache.hadoop.hbase.client.security.SecurityCapability; 37 import org.apache.hadoop.hbase.ipc.BlockingRpcCallback; 38 import org.apache.hadoop.hbase.ipc.ServerRpcController; 39 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsRequest; 40 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse; 41 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsRequest; 42 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsResponse; 43 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.SetAuthsRequest; 44 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel; 45 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest; 46 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 47 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService; 48 import org.apache.hadoop.hbase.util.Bytes; 49 50 import com.google.protobuf.ServiceException; 51 52 /** 53 * Utility client for doing visibility labels admin operations. 54 */ 55 @InterfaceAudience.Public 56 @InterfaceStability.Evolving 57 public class VisibilityClient { 58 59 /** 60 * Return true if cell visibility features are supported and enabled 61 * @param connection The connection to use 62 * @return true if cell visibility features are supported and enabled, false otherwise 63 * @throws IOException 64 */ isCellVisibilityEnabled(Connection connection)65 public static boolean isCellVisibilityEnabled(Connection connection) throws IOException { 66 return connection.getAdmin().getSecurityCapabilities() 67 .contains(SecurityCapability.CELL_VISIBILITY); 68 } 69 70 /** 71 * Utility method for adding label to the system. 72 * 73 * @param conf 74 * @param label 75 * @return VisibilityLabelsResponse 76 * @throws Throwable 77 * @deprecated Use {@link #addLabel(Connection,String)} instead. 78 */ 79 @Deprecated addLabel(Configuration conf, final String label)80 public static VisibilityLabelsResponse addLabel(Configuration conf, final String label) 81 throws Throwable { 82 try (Connection connection = ConnectionFactory.createConnection(conf)) { 83 return addLabels(connection, new String[] { label }); 84 } 85 } 86 87 /** 88 * Utility method for adding label to the system. 89 * 90 * @param connection 91 * @param label 92 * @return VisibilityLabelsResponse 93 * @throws Throwable 94 */ addLabel(Connection connection, final String label)95 public static VisibilityLabelsResponse addLabel(Connection connection, final String label) 96 throws Throwable { 97 return addLabels(connection, new String[] { label }); 98 } 99 100 /** 101 * Utility method for adding labels to the system. 102 * 103 * @param conf 104 * @param labels 105 * @return VisibilityLabelsResponse 106 * @throws Throwable 107 * @deprecated Use {@link #addLabels(Connection,String[])} instead. 108 */ 109 @Deprecated addLabels(Configuration conf, final String[] labels)110 public static VisibilityLabelsResponse addLabels(Configuration conf, final String[] labels) 111 throws Throwable { 112 try (Connection connection = ConnectionFactory.createConnection(conf)) { 113 return addLabels(connection, labels); 114 } 115 } 116 117 /** 118 * Utility method for adding labels to the system. 119 * 120 * @param connection 121 * @param labels 122 * @return VisibilityLabelsResponse 123 * @throws Throwable 124 */ addLabels(Connection connection, final String[] labels)125 public static VisibilityLabelsResponse addLabels(Connection connection, final String[] labels) 126 throws Throwable { 127 128 try (Table table = connection.getTable(LABELS_TABLE_NAME)) { 129 Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse> callable = 130 new Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse>() { 131 ServerRpcController controller = new ServerRpcController(); 132 BlockingRpcCallback<VisibilityLabelsResponse> rpcCallback = 133 new BlockingRpcCallback<VisibilityLabelsResponse>(); 134 135 public VisibilityLabelsResponse call(VisibilityLabelsService service) 136 throws IOException { 137 VisibilityLabelsRequest.Builder builder = VisibilityLabelsRequest.newBuilder(); 138 for (String label : labels) { 139 if (label.length() > 0) { 140 VisibilityLabel.Builder newBuilder = VisibilityLabel.newBuilder(); 141 newBuilder.setLabel(ByteStringer.wrap(Bytes.toBytes(label))); 142 builder.addVisLabel(newBuilder.build()); 143 } 144 } 145 service.addLabels(controller, builder.build(), rpcCallback); 146 VisibilityLabelsResponse response = rpcCallback.get(); 147 if (controller.failedOnException()) { 148 throw controller.getFailedOn(); 149 } 150 return response; 151 } 152 }; 153 Map<byte[], VisibilityLabelsResponse> result = 154 table.coprocessorService(VisibilityLabelsService.class, HConstants.EMPTY_BYTE_ARRAY, 155 HConstants.EMPTY_BYTE_ARRAY, callable); 156 return result.values().iterator().next(); // There will be exactly one region for labels 157 // table and so one entry in result Map. 158 } 159 } 160 161 /** 162 * Sets given labels globally authorized for the user. 163 * @param conf 164 * @param auths 165 * @param user 166 * @return VisibilityLabelsResponse 167 * @throws Throwable 168 * @deprecated Use {@link #setAuths(Connection,String[],String)} instead. 169 */ 170 @Deprecated setAuths(Configuration conf, final String[] auths, final String user)171 public static VisibilityLabelsResponse setAuths(Configuration conf, final String[] auths, 172 final String user) throws Throwable { 173 try (Connection connection = ConnectionFactory.createConnection(conf)) { 174 return setOrClearAuths(connection, auths, user, true); 175 } 176 } 177 178 /** 179 * Sets given labels globally authorized for the user. 180 * @param connection 181 * @param auths 182 * @param user 183 * @return VisibilityLabelsResponse 184 * @throws Throwable 185 */ setAuths(Connection connection, final String[] auths, final String user)186 public static VisibilityLabelsResponse setAuths(Connection connection, final String[] auths, 187 final String user) throws Throwable { 188 return setOrClearAuths(connection, auths, user, true); 189 } 190 191 /** 192 * @param conf 193 * @param user 194 * @return labels, the given user is globally authorized for. 195 * @throws Throwable 196 * @deprecated Use {@link #getAuths(Connection,String)} instead. 197 */ 198 @Deprecated getAuths(Configuration conf, final String user)199 public static GetAuthsResponse getAuths(Configuration conf, final String user) throws Throwable { 200 try (Connection connection = ConnectionFactory.createConnection(conf)) { 201 return getAuths(connection, user); 202 } 203 } 204 205 /** 206 * @param connection the Connection instance to use. 207 * @param user 208 * @return labels, the given user is globally authorized for. 209 * @throws Throwable 210 */ getAuths(Connection connection, final String user)211 public static GetAuthsResponse getAuths(Connection connection, final String user) 212 throws Throwable { 213 try (Table table = connection.getTable(LABELS_TABLE_NAME)) { 214 Batch.Call<VisibilityLabelsService, GetAuthsResponse> callable = 215 new Batch.Call<VisibilityLabelsService, GetAuthsResponse>() { 216 ServerRpcController controller = new ServerRpcController(); 217 BlockingRpcCallback<GetAuthsResponse> rpcCallback = 218 new BlockingRpcCallback<GetAuthsResponse>(); 219 220 public GetAuthsResponse call(VisibilityLabelsService service) throws IOException { 221 GetAuthsRequest.Builder getAuthReqBuilder = GetAuthsRequest.newBuilder(); 222 getAuthReqBuilder.setUser(ByteStringer.wrap(Bytes.toBytes(user))); 223 service.getAuths(controller, getAuthReqBuilder.build(), rpcCallback); 224 GetAuthsResponse response = rpcCallback.get(); 225 if (controller.failedOnException()) { 226 throw controller.getFailedOn(); 227 } 228 return response; 229 } 230 }; 231 Map<byte[], GetAuthsResponse> result = 232 table.coprocessorService(VisibilityLabelsService.class, 233 HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, callable); 234 return result.values().iterator().next(); // There will be exactly one region for labels 235 // table and so one entry in result Map. 236 } 237 } 238 239 /** 240 * Retrieve the list of visibility labels defined in the system. 241 * @param conf 242 * @param regex The regular expression to filter which labels are returned. 243 * @return labels The list of visibility labels defined in the system. 244 * @throws Throwable 245 * @deprecated Use {@link #listLabels(Connection,String)} instead. 246 */ 247 @Deprecated listLabels(Configuration conf, final String regex)248 public static ListLabelsResponse listLabels(Configuration conf, final String regex) 249 throws Throwable { 250 try(Connection connection = ConnectionFactory.createConnection(conf)){ 251 return listLabels(connection, regex); 252 } 253 } 254 255 /** 256 * Retrieve the list of visibility labels defined in the system. 257 * @param connection The Connection instance to use. 258 * @param regex The regular expression to filter which labels are returned. 259 * @return labels The list of visibility labels defined in the system. 260 * @throws Throwable 261 */ listLabels(Connection connection, final String regex)262 public static ListLabelsResponse listLabels(Connection connection, final String regex) 263 throws Throwable { 264 Table table = null; 265 try { 266 table = connection.getTable(LABELS_TABLE_NAME); 267 Batch.Call<VisibilityLabelsService, ListLabelsResponse> callable = 268 new Batch.Call<VisibilityLabelsService, ListLabelsResponse>() { 269 ServerRpcController controller = new ServerRpcController(); 270 BlockingRpcCallback<ListLabelsResponse> rpcCallback = 271 new BlockingRpcCallback<ListLabelsResponse>(); 272 273 public ListLabelsResponse call(VisibilityLabelsService service) throws IOException { 274 ListLabelsRequest.Builder listAuthLabelsReqBuilder = ListLabelsRequest.newBuilder(); 275 if (regex != null) { 276 // Compile the regex here to catch any regex exception earlier. 277 Pattern pattern = Pattern.compile(regex); 278 listAuthLabelsReqBuilder.setRegex(pattern.toString()); 279 } 280 service.listLabels(controller, listAuthLabelsReqBuilder.build(), rpcCallback); 281 ListLabelsResponse response = rpcCallback.get(); 282 if (controller.failedOnException()) { 283 throw controller.getFailedOn(); 284 } 285 return response; 286 } 287 }; 288 Map<byte[], ListLabelsResponse> result = 289 table.coprocessorService(VisibilityLabelsService.class, HConstants.EMPTY_BYTE_ARRAY, 290 HConstants.EMPTY_BYTE_ARRAY, callable); 291 return result.values().iterator().next(); // There will be exactly one region for labels 292 // table and so one entry in result Map. 293 } 294 finally { 295 if (table != null) { 296 table.close(); 297 } 298 if (connection != null) { 299 connection.close(); 300 } 301 } 302 } 303 304 /** 305 * Removes given labels from user's globally authorized list of labels. 306 * @param conf 307 * @param auths 308 * @param user 309 * @return VisibilityLabelsResponse 310 * @throws Throwable 311 * @deprecated Use {@link #clearAuths(Connection,String[],String)} instead. 312 */ 313 @Deprecated clearAuths(Configuration conf, final String[] auths, final String user)314 public static VisibilityLabelsResponse clearAuths(Configuration conf, final String[] auths, 315 final String user) throws Throwable { 316 try (Connection connection = ConnectionFactory.createConnection(conf)) { 317 return setOrClearAuths(connection, auths, user, false); 318 } 319 } 320 321 /** 322 * Removes given labels from user's globally authorized list of labels. 323 * @param connection 324 * @param auths 325 * @param user 326 * @return VisibilityLabelsResponse 327 * @throws Throwable 328 */ clearAuths(Connection connection, final String[] auths, final String user)329 public static VisibilityLabelsResponse clearAuths(Connection connection, final String[] auths, 330 final String user) throws Throwable { 331 return setOrClearAuths(connection, auths, user, false); 332 } 333 setOrClearAuths(Connection connection, final String[] auths, final String user, final boolean setOrClear)334 private static VisibilityLabelsResponse setOrClearAuths(Connection connection, 335 final String[] auths, final String user, final boolean setOrClear) 336 throws IOException, ServiceException, Throwable { 337 338 try (Table table = connection.getTable(LABELS_TABLE_NAME)) { 339 Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse> callable = 340 new Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse>() { 341 ServerRpcController controller = new ServerRpcController(); 342 BlockingRpcCallback<VisibilityLabelsResponse> rpcCallback = 343 new BlockingRpcCallback<VisibilityLabelsResponse>(); 344 345 public VisibilityLabelsResponse call(VisibilityLabelsService service) throws IOException { 346 SetAuthsRequest.Builder setAuthReqBuilder = SetAuthsRequest.newBuilder(); 347 setAuthReqBuilder.setUser(ByteStringer.wrap(Bytes.toBytes(user))); 348 for (String auth : auths) { 349 if (auth.length() > 0) { 350 setAuthReqBuilder.addAuth(ByteStringer.wrap(Bytes.toBytes(auth))); 351 } 352 } 353 if (setOrClear) { 354 service.setAuths(controller, setAuthReqBuilder.build(), rpcCallback); 355 } else { 356 service.clearAuths(controller, setAuthReqBuilder.build(), rpcCallback); 357 } 358 VisibilityLabelsResponse response = rpcCallback.get(); 359 if (controller.failedOnException()) { 360 throw controller.getFailedOn(); 361 } 362 return response; 363 } 364 }; 365 Map<byte[], VisibilityLabelsResponse> result = table.coprocessorService( 366 VisibilityLabelsService.class, HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, 367 callable); 368 return result.values().iterator().next(); // There will be exactly one region for labels 369 // table and so one entry in result Map. 370 } 371 } 372 }