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.visibility; 019 020import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME; 021 022import com.google.protobuf.ByteString; 023import com.google.protobuf.ServiceException; 024import java.io.IOException; 025import java.util.Map; 026import java.util.regex.Pattern; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.HConstants; 029import org.apache.hadoop.hbase.client.Connection; 030import org.apache.hadoop.hbase.client.ConnectionFactory; 031import org.apache.hadoop.hbase.client.Table; 032import org.apache.hadoop.hbase.client.coprocessor.Batch; 033import org.apache.hadoop.hbase.client.security.SecurityCapability; 034import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; 035import org.apache.hadoop.hbase.ipc.ServerRpcController; 036import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsRequest; 037import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse; 038import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsRequest; 039import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsResponse; 040import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.SetAuthsRequest; 041import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel; 042import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest; 043import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 044import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService; 045import org.apache.hadoop.hbase.util.ByteStringer; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.yetus.audience.InterfaceAudience; 048 049/** 050 * Utility client for doing visibility labels admin operations. 051 */ 052@InterfaceAudience.Public 053public class VisibilityClient { 054 055 /** 056 * Return true if cell visibility features are supported and enabled 057 * @param connection The connection to use 058 * @return true if cell visibility features are supported and enabled, false otherwise 059 */ 060 public static boolean isCellVisibilityEnabled(Connection connection) throws IOException { 061 return connection.getAdmin().getSecurityCapabilities() 062 .contains(SecurityCapability.CELL_VISIBILITY); 063 } 064 065 /** 066 * Utility method for adding label to the system. 067 * @deprecated Use {@link #addLabel(Connection,String)} instead. 068 */ 069 @Deprecated 070 public static VisibilityLabelsResponse addLabel(Configuration conf, final String label) 071 throws Throwable { 072 try (Connection connection = ConnectionFactory.createConnection(conf)) { 073 return addLabels(connection, new String[] { label }); 074 } 075 } 076 077 /** 078 * Utility method for adding label to the system. 079 */ 080 public static VisibilityLabelsResponse addLabel(Connection connection, final String label) 081 throws Throwable { 082 return addLabels(connection, new String[] { label }); 083 } 084 085 /** 086 * Utility method for adding labels to the system. 087 * @deprecated Use {@link #addLabels(Connection,String[])} instead. 088 */ 089 @Deprecated 090 public static VisibilityLabelsResponse addLabels(Configuration conf, final String[] labels) 091 throws Throwable { 092 try (Connection connection = ConnectionFactory.createConnection(conf)) { 093 return addLabels(connection, labels); 094 } 095 } 096 097 /** 098 * Utility method for adding labels to the system. 099 */ 100 public static VisibilityLabelsResponse addLabels(Connection connection, final String[] labels) 101 throws Throwable { 102 try (Table table = connection.getTable(LABELS_TABLE_NAME)) { 103 Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse> callable = 104 new Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse>() { 105 ServerRpcController controller = new ServerRpcController(); 106 CoprocessorRpcUtils.BlockingRpcCallback<VisibilityLabelsResponse> rpcCallback = 107 new CoprocessorRpcUtils.BlockingRpcCallback<>(); 108 109 @Override 110 public VisibilityLabelsResponse call(VisibilityLabelsService service) throws IOException { 111 VisibilityLabelsRequest.Builder builder = VisibilityLabelsRequest.newBuilder(); 112 for (String label : labels) { 113 if (label.length() > 0) { 114 VisibilityLabel.Builder newBuilder = VisibilityLabel.newBuilder(); 115 newBuilder.setLabel(ByteStringer.wrap(Bytes.toBytes(label))); 116 builder.addVisLabel(newBuilder.build()); 117 } 118 } 119 service.addLabels(controller, builder.build(), rpcCallback); 120 VisibilityLabelsResponse response = rpcCallback.get(); 121 if (controller.failedOnException()) { 122 throw controller.getFailedOn(); 123 } 124 return response; 125 } 126 }; 127 Map<byte[], VisibilityLabelsResponse> result = 128 table.coprocessorService(VisibilityLabelsService.class, HConstants.EMPTY_BYTE_ARRAY, 129 HConstants.EMPTY_BYTE_ARRAY, callable); 130 return result.values().iterator().next(); // There will be exactly one region for labels 131 // table and so one entry in result Map. 132 } 133 } 134 135 /** 136 * Sets given labels globally authorized for the user. 137 * @deprecated Use {@link #setAuths(Connection,String[],String)} instead. 138 */ 139 @Deprecated 140 public static VisibilityLabelsResponse setAuths(Configuration conf, final String[] auths, 141 final String user) throws Throwable { 142 try (Connection connection = ConnectionFactory.createConnection(conf)) { 143 return setOrClearAuths(connection, auths, user, true); 144 } 145 } 146 147 /** 148 * Sets given labels globally authorized for the user. 149 */ 150 public static VisibilityLabelsResponse setAuths(Connection connection, final String[] auths, 151 final String user) throws Throwable { 152 return setOrClearAuths(connection, auths, user, true); 153 } 154 155 /** 156 * Returns labels, the given user is globally authorized for. 157 * @deprecated Use {@link #getAuths(Connection,String)} instead. 158 */ 159 @Deprecated 160 public static GetAuthsResponse getAuths(Configuration conf, final String user) throws Throwable { 161 try (Connection connection = ConnectionFactory.createConnection(conf)) { 162 return getAuths(connection, user); 163 } 164 } 165 166 /** 167 * Get the authorization for a given user 168 * @param connection the Connection instance to use 169 * @param user the user 170 * @return labels the given user is globally authorized for 171 */ 172 public static GetAuthsResponse getAuths(Connection connection, final String user) 173 throws Throwable { 174 try (Table table = connection.getTable(LABELS_TABLE_NAME)) { 175 Batch.Call<VisibilityLabelsService, GetAuthsResponse> callable = 176 new Batch.Call<VisibilityLabelsService, GetAuthsResponse>() { 177 ServerRpcController controller = new ServerRpcController(); 178 CoprocessorRpcUtils.BlockingRpcCallback<GetAuthsResponse> rpcCallback = 179 new CoprocessorRpcUtils.BlockingRpcCallback<>(); 180 181 @Override 182 public GetAuthsResponse call(VisibilityLabelsService service) throws IOException { 183 GetAuthsRequest.Builder getAuthReqBuilder = GetAuthsRequest.newBuilder(); 184 getAuthReqBuilder.setUser(ByteStringer.wrap(Bytes.toBytes(user))); 185 service.getAuths(controller, getAuthReqBuilder.build(), rpcCallback); 186 GetAuthsResponse response = rpcCallback.get(); 187 if (controller.failedOnException()) { 188 throw controller.getFailedOn(); 189 } 190 return response; 191 } 192 }; 193 Map<byte[], GetAuthsResponse> result = table.coprocessorService(VisibilityLabelsService.class, 194 HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, callable); 195 return result.values().iterator().next(); // There will be exactly one region for labels 196 // table and so one entry in result Map. 197 } 198 } 199 200 /** 201 * Retrieve the list of visibility labels defined in the system. 202 * @param conf the configuration to use 203 * @param regex The regular expression to filter which labels are returned. 204 * @return labels The list of visibility labels defined in the system. 205 * @deprecated Use {@link #listLabels(Connection,String)} instead. 206 */ 207 @Deprecated 208 public static ListLabelsResponse listLabels(Configuration conf, final String regex) 209 throws Throwable { 210 try (Connection connection = ConnectionFactory.createConnection(conf)) { 211 return listLabels(connection, regex); 212 } 213 } 214 215 /** 216 * Retrieve the list of visibility labels defined in the system. 217 * @param connection The Connection instance to use. 218 * @param regex The regular expression to filter which labels are returned. 219 * @return labels The list of visibility labels defined in the system. 220 */ 221 public static ListLabelsResponse listLabels(Connection connection, final String regex) 222 throws Throwable { 223 try (Table table = connection.getTable(LABELS_TABLE_NAME)) { 224 Batch.Call<VisibilityLabelsService, ListLabelsResponse> callable = 225 new Batch.Call<VisibilityLabelsService, ListLabelsResponse>() { 226 ServerRpcController controller = new ServerRpcController(); 227 CoprocessorRpcUtils.BlockingRpcCallback<ListLabelsResponse> rpcCallback = 228 new CoprocessorRpcUtils.BlockingRpcCallback<>(); 229 230 @Override 231 public ListLabelsResponse call(VisibilityLabelsService service) throws IOException { 232 ListLabelsRequest.Builder listAuthLabelsReqBuilder = ListLabelsRequest.newBuilder(); 233 if (regex != null) { 234 // Compile the regex here to catch any regex exception earlier. 235 Pattern pattern = Pattern.compile(regex); 236 listAuthLabelsReqBuilder.setRegex(pattern.toString()); 237 } 238 service.listLabels(controller, listAuthLabelsReqBuilder.build(), rpcCallback); 239 ListLabelsResponse response = rpcCallback.get(); 240 if (controller.failedOnException()) { 241 throw controller.getFailedOn(); 242 } 243 return response; 244 } 245 }; 246 Map<byte[], ListLabelsResponse> result = 247 table.coprocessorService(VisibilityLabelsService.class, HConstants.EMPTY_BYTE_ARRAY, 248 HConstants.EMPTY_BYTE_ARRAY, callable); 249 return result.values().iterator().next(); // There will be exactly one region for labels 250 // table and so one entry in result Map. 251 } 252 } 253 254 /** 255 * Removes given labels from user's globally authorized list of labels. 256 * @deprecated Use {@link #clearAuths(Connection,String[],String)} instead. 257 */ 258 @Deprecated 259 public static VisibilityLabelsResponse clearAuths(Configuration conf, final String[] auths, 260 final String user) throws Throwable { 261 try (Connection connection = ConnectionFactory.createConnection(conf)) { 262 return setOrClearAuths(connection, auths, user, false); 263 } 264 } 265 266 /** 267 * Removes given labels from user's globally authorized list of labels. 268 */ 269 public static VisibilityLabelsResponse clearAuths(Connection connection, final String[] auths, 270 final String user) throws Throwable { 271 return setOrClearAuths(connection, auths, user, false); 272 } 273 274 private static VisibilityLabelsResponse setOrClearAuths(Connection connection, 275 final String[] auths, final String user, final boolean setOrClear) 276 throws IOException, ServiceException, Throwable { 277 278 try (Table table = connection.getTable(LABELS_TABLE_NAME)) { 279 Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse> callable = 280 new Batch.Call<VisibilityLabelsService, VisibilityLabelsResponse>() { 281 ServerRpcController controller = new ServerRpcController(); 282 CoprocessorRpcUtils.BlockingRpcCallback<VisibilityLabelsResponse> rpcCallback = 283 new CoprocessorRpcUtils.BlockingRpcCallback<>(); 284 285 @Override 286 public VisibilityLabelsResponse call(VisibilityLabelsService service) throws IOException { 287 SetAuthsRequest.Builder setAuthReqBuilder = SetAuthsRequest.newBuilder(); 288 setAuthReqBuilder.setUser(ByteStringer.wrap(Bytes.toBytes(user))); 289 for (String auth : auths) { 290 if (auth.length() > 0) { 291 setAuthReqBuilder.addAuth(ByteString.copyFromUtf8(auth)); 292 } 293 } 294 if (setOrClear) { 295 service.setAuths(controller, setAuthReqBuilder.build(), rpcCallback); 296 } else { 297 service.clearAuths(controller, setAuthReqBuilder.build(), rpcCallback); 298 } 299 VisibilityLabelsResponse response = rpcCallback.get(); 300 if (controller.failedOnException()) { 301 throw controller.getFailedOn(); 302 } 303 return response; 304 } 305 }; 306 Map<byte[], VisibilityLabelsResponse> result = 307 table.coprocessorService(VisibilityLabelsService.class, HConstants.EMPTY_BYTE_ARRAY, 308 HConstants.EMPTY_BYTE_ARRAY, callable); 309 return result.values().iterator().next(); // There will be exactly one region for labels 310 // table and so one entry in result Map. 311 } 312 } 313}