Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 697E2200D08 for ; Thu, 21 Sep 2017 17:13:37 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 663851609EB; Thu, 21 Sep 2017 15:13:37 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 355931609D0 for ; Thu, 21 Sep 2017 17:13:35 +0200 (CEST) Received: (qmail 95244 invoked by uid 500); 21 Sep 2017 15:13:34 -0000 Mailing-List: contact commits-help@hbase.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@hbase.apache.org Delivered-To: mailing list commits@hbase.apache.org Received: (qmail 95235 invoked by uid 99); 21 Sep 2017 15:13:34 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 21 Sep 2017 15:13:34 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 35ADCF218A; Thu, 21 Sep 2017 15:13:34 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: git-site-role@apache.org To: commits@hbase.apache.org Date: Thu, 21 Sep 2017 15:13:34 -0000 Message-Id: <8d5689eca5904cd1bafec1556f5885a0@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [01/51] [partial] hbase-site git commit: Published site at . archived-at: Thu, 21 Sep 2017 15:13:37 -0000 Repository: hbase-site Updated Branches: refs/heads/asf-site 21bac6593 -> fa7d6c0c6 http://git-wip-us.apache.org/repos/asf/hbase-site/blob/fa7d6c0c/devapidocs/src-html/org/apache/hadoop/hbase/security/access/AccessController.OpType.html ---------------------------------------------------------------------- diff --git a/devapidocs/src-html/org/apache/hadoop/hbase/security/access/AccessController.OpType.html b/devapidocs/src-html/org/apache/hadoop/hbase/security/access/AccessController.OpType.html index 92c2266..ee54e1d 100644 --- a/devapidocs/src-html/org/apache/hadoop/hbase/security/access/AccessController.OpType.html +++ b/devapidocs/src-html/org/apache/hadoop/hbase/security/access/AccessController.OpType.html @@ -26,2736 +26,2735 @@ 018 */ 019package org.apache.hadoop.hbase.security.access; 020 -021import com.google.protobuf.Message; -022import com.google.protobuf.RpcCallback; -023import com.google.protobuf.RpcController; -024import com.google.protobuf.Service; -025import org.apache.commons.logging.Log; -026import org.apache.commons.logging.LogFactory; -027import org.apache.hadoop.conf.Configuration; -028import org.apache.hadoop.hbase.ArrayBackedTag; -029import org.apache.hadoop.hbase.Cell; -030import org.apache.hadoop.hbase.CellScanner; -031import org.apache.hadoop.hbase.CellUtil; -032import org.apache.hadoop.hbase.CompareOperator; -033import org.apache.hadoop.hbase.CompoundConfiguration; -034import org.apache.hadoop.hbase.CoprocessorEnvironment; -035import org.apache.hadoop.hbase.DoNotRetryIOException; -036import org.apache.hadoop.hbase.HBaseInterfaceAudience; -037import org.apache.hadoop.hbase.HConstants; -038import org.apache.hadoop.hbase.HRegionInfo; -039import org.apache.hadoop.hbase.KeyValue; -040import org.apache.hadoop.hbase.KeyValue.Type; -041import org.apache.hadoop.hbase.MetaTableAccessor; -042import org.apache.hadoop.hbase.NamespaceDescriptor; -043import org.apache.hadoop.hbase.ServerName; -044import org.apache.hadoop.hbase.TableName; -045import org.apache.hadoop.hbase.Tag; -046import org.apache.yetus.audience.InterfaceAudience; -047import org.apache.hadoop.hbase.client.Append; -048import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; -049import org.apache.hadoop.hbase.client.Delete; -050import org.apache.hadoop.hbase.client.Durability; -051import org.apache.hadoop.hbase.client.Get; -052import org.apache.hadoop.hbase.client.Increment; -053import org.apache.hadoop.hbase.client.MasterSwitchType; -054import org.apache.hadoop.hbase.client.Mutation; -055import org.apache.hadoop.hbase.client.Put; -056import org.apache.hadoop.hbase.client.Query; -057import org.apache.hadoop.hbase.client.Result; -058import org.apache.hadoop.hbase.client.Scan; -059import org.apache.hadoop.hbase.client.Table; -060import org.apache.hadoop.hbase.client.TableDescriptor; -061import org.apache.hadoop.hbase.coprocessor.BulkLoadObserver; -062import org.apache.hadoop.hbase.coprocessor.CoprocessorException; -063import org.apache.hadoop.hbase.coprocessor.CoprocessorService; -064import org.apache.hadoop.hbase.coprocessor.EndpointObserver; -065import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; -066import org.apache.hadoop.hbase.coprocessor.MasterObserver; -067import org.apache.hadoop.hbase.coprocessor.ObserverContext; -068import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; -069import org.apache.hadoop.hbase.coprocessor.RegionObserver; -070import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; -071import org.apache.hadoop.hbase.coprocessor.RegionServerObserver; -072import org.apache.hadoop.hbase.filter.ByteArrayComparable; -073import org.apache.hadoop.hbase.filter.Filter; -074import org.apache.hadoop.hbase.filter.FilterList; -075import org.apache.hadoop.hbase.io.hfile.HFile; -076import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; -077import org.apache.hadoop.hbase.ipc.RpcServer; -078import org.apache.hadoop.hbase.master.MasterServices; -079import org.apache.hadoop.hbase.master.locking.LockProcedure; -080import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; -081import org.apache.hadoop.hbase.net.Address; -082import org.apache.hadoop.hbase.procedure2.LockType; -083import org.apache.hadoop.hbase.procedure2.Procedure; -084import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; -085import org.apache.hadoop.hbase.protobuf.ProtobufUtil; -086import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos; -087import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService; -088import org.apache.hadoop.hbase.regionserver.InternalScanner; -089import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress; -090import org.apache.hadoop.hbase.regionserver.Region; -091import org.apache.hadoop.hbase.regionserver.RegionScanner; -092import org.apache.hadoop.hbase.regionserver.ScanType; -093import org.apache.hadoop.hbase.regionserver.ScannerContext; -094import org.apache.hadoop.hbase.regionserver.Store; -095import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; -096import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest; -097import org.apache.hadoop.hbase.wal.WALEdit; -098import org.apache.hadoop.hbase.replication.ReplicationEndpoint; -099import org.apache.hadoop.hbase.replication.ReplicationPeerConfig; -100import org.apache.hadoop.hbase.security.AccessDeniedException; -101import org.apache.hadoop.hbase.security.Superusers; -102import org.apache.hadoop.hbase.security.User; -103import org.apache.hadoop.hbase.security.UserProvider; -104import org.apache.hadoop.hbase.security.access.Permission.Action; -105import org.apache.hadoop.hbase.shaded.com.google.common.collect.ArrayListMultimap; -106import org.apache.hadoop.hbase.shaded.com.google.common.collect.ImmutableSet; -107import org.apache.hadoop.hbase.shaded.com.google.common.collect.ListMultimap; -108import org.apache.hadoop.hbase.shaded.com.google.common.collect.Lists; -109import org.apache.hadoop.hbase.shaded.com.google.common.collect.MapMaker; -110import org.apache.hadoop.hbase.shaded.com.google.common.collect.Maps; -111import org.apache.hadoop.hbase.shaded.com.google.common.collect.Sets; -112import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.WALEntry; -113import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.CleanupBulkLoadRequest; -114import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.PrepareBulkLoadRequest; -115import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas; -116import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription; -117import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; -118import org.apache.hadoop.hbase.util.ByteRange; -119import org.apache.hadoop.hbase.util.Bytes; -120import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; -121import org.apache.hadoop.hbase.util.Pair; -122import org.apache.hadoop.hbase.util.SimpleMutableByteRange; -123import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; -124 -125import java.io.IOException; -126import java.net.InetAddress; -127import java.security.PrivilegedExceptionAction; -128import java.util.ArrayList; -129import java.util.Collection; -130import java.util.HashMap; -131import java.util.Iterator; -132import java.util.List; -133import java.util.Map; -134import java.util.Map.Entry; -135import java.util.Set; -136import java.util.TreeMap; -137import java.util.TreeSet; -138 -139/** -140 * Provides basic authorization checks for data access and administrative -141 * operations. -142 * -143 * <p> -144 * {@code AccessController} performs authorization checks for HBase operations -145 * based on: -146 * </p> -147 * <ul> -148 * <li>the identity of the user performing the operation</li> -149 * <li>the scope over which the operation is performed, in increasing -150 * specificity: global, table, column family, or qualifier</li> -151 * <li>the type of action being performed (as mapped to -152 * {@link Permission.Action} values)</li> -153 * </ul> -154 * <p> -155 * If the authorization check fails, an {@link AccessDeniedException} -156 * will be thrown for the operation. -157 * </p> -158 * -159 * <p> -160 * To perform authorization checks, {@code AccessController} relies on the -161 * RpcServerEngine being loaded to provide -162 * the user identities for remote requests. -163 * </p> -164 * -165 * <p> -166 * The access control lists used for authorization can be manipulated via the -167 * exposed {@link AccessControlService} Interface implementation, and the associated -168 * {@code grant}, {@code revoke}, and {@code user_permission} HBase shell -169 * commands. -170 * </p> -171 */ -172@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG) -173public class AccessController implements MasterObserver, RegionObserver, RegionServerObserver, -174 AccessControlService.Interface, CoprocessorService, EndpointObserver, BulkLoadObserver { -175 -176 private static final Log LOG = LogFactory.getLog(AccessController.class); -177 -178 private static final Log AUDITLOG = -179 LogFactory.getLog("SecurityLogger."+AccessController.class.getName()); -180 private static final String CHECK_COVERING_PERM = "check_covering_perm"; -181 private static final String TAG_CHECK_PASSED = "tag_check_passed"; -182 private static final byte[] TRUE = Bytes.toBytes(true); -183 -184 TableAuthManager authManager = null; -185 -186 /** flags if we are running on a region of the _acl_ table */ -187 boolean aclRegion = false; -188 -189 /** defined only for Endpoint implementation, so it can have way to -190 access region services */ -191 private RegionCoprocessorEnvironment regionEnv; -192 -193 /** Mapping of scanner instances to the user who created them */ -194 private Map<InternalScanner,String> scannerOwners = -195 new MapMaker().weakKeys().makeMap(); -196 -197 private Map<TableName, List<UserPermission>> tableAcls; -198 -199 /** Provider for mapping principal names to Users */ -200 private UserProvider userProvider; -201 -202 /** if we are active, usually true, only not true if "hbase.security.authorization" -203 has been set to false in site configuration */ -204 boolean authorizationEnabled; -205 -206 /** if we are able to support cell ACLs */ -207 boolean cellFeaturesEnabled; -208 -209 /** if we should check EXEC permissions */ -210 boolean shouldCheckExecPermission; -211 -212 /** if we should terminate access checks early as soon as table or CF grants -213 allow access; pre-0.98 compatible behavior */ -214 boolean compatibleEarlyTermination; -215 -216 /** if we have been successfully initialized */ -217 private volatile boolean initialized = false; -218 -219 /** if the ACL table is available, only relevant in the master */ -220 private volatile boolean aclTabAvailable = false; -221 -222 public static boolean isAuthorizationSupported(Configuration conf) { -223 return conf.getBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, true); -224 } -225 -226 public static boolean isCellAuthorizationSupported(Configuration conf) { -227 return isAuthorizationSupported(conf) && -228 (HFile.getFormatVersion(conf) >= HFile.MIN_FORMAT_VERSION_WITH_TAGS); -229 } -230 -231 public Region getRegion() { -232 return regionEnv != null ? regionEnv.getRegion() : null; -233 } -234 -235 public TableAuthManager getAuthManager() { -236 return authManager; -237 } -238 -239 void initialize(RegionCoprocessorEnvironment e) throws IOException { -240 final Region region = e.getRegion(); -241 Configuration conf = e.getConfiguration(); -242 Map<byte[], ListMultimap<String,TablePermission>> tables = -243 AccessControlLists.loadAll(region); -244 // For each table, write out the table's permissions to the respective -245 // znode for that table. -246 for (Map.Entry<byte[], ListMultimap<String,TablePermission>> t: -247 tables.entrySet()) { -248 byte[] entry = t.getKey(); -249 ListMultimap<String,TablePermission> perms = t.getValue(); -250 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf); -251 this.authManager.getZKPermissionWatcher().writeToZookeeper(entry, serialized); -252 } -253 initialized = true; -254 } -255 -256 /** -257 * Writes all table ACLs for the tables in the given Map up into ZooKeeper -258 * znodes. This is called to synchronize ACL changes following {@code _acl_} -259 * table updates. -260 */ -261 void updateACL(RegionCoprocessorEnvironment e, -262 final Map<byte[], List<Cell>> familyMap) { -263 Set<byte[]> entries = new TreeSet<>(Bytes.BYTES_RAWCOMPARATOR); -264 for (Map.Entry<byte[], List<Cell>> f : familyMap.entrySet()) { -265 List<Cell> cells = f.getValue(); -266 for (Cell cell: cells) { -267 if (CellUtil.matchingFamily(cell, AccessControlLists.ACL_LIST_FAMILY)) { -268 entries.add(CellUtil.cloneRow(cell)); -269 } -270 } -271 } -272 ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher(); -273 Configuration conf = regionEnv.getConfiguration(); -274 for (byte[] entry: entries) { -275 try { -276 try (Table t = regionEnv.getTable(AccessControlLists.ACL_TABLE_NAME)) { -277 ListMultimap<String,TablePermission> perms = -278 AccessControlLists.getPermissions(conf, entry, t); -279 byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf); -280 zkw.writeToZookeeper(entry, serialized); -281 } -282 } catch (IOException ex) { -283 LOG.error("Failed updating permissions mirror for '" + Bytes.toString(entry) + "'", -284 ex); -285 } -286 } -287 } -288 -289 /** -290 * Check the current user for authorization to perform a specific action -291 * against the given set of row data. -292 * -293 * <p>Note: Ordering of the authorization checks -294 * has been carefully optimized to short-circuit the most common requests -295 * and minimize the amount of processing required.</p> -296 * -297 * @param permRequest the action being requested -298 * @param e the coprocessor environment -299 * @param families the map of column families to qualifiers present in -300 * the request -301 * @return an authorization result -302 */ -303 AuthResult permissionGranted(String request, User user, Action permRequest, -304 RegionCoprocessorEnvironment e, -305 Map<byte [], ? extends Collection<?>> families) { -306 HRegionInfo hri = e.getRegion().getRegionInfo(); -307 TableName tableName = hri.getTable(); -308 -309 // 1. All users need read access to hbase:meta table. -310 // this is a very common operation, so deal with it quickly. -311 if (hri.isMetaRegion()) { -312 if (permRequest == Action.READ) { -313 return AuthResult.allow(request, "All users allowed", user, -314 permRequest, tableName, families); -315 } -316 } -317 -318 if (user == null) { -319 return AuthResult.deny(request, "No user associated with request!", null, -320 permRequest, tableName, families); -321 } -322 -323 // 2. check for the table-level, if successful we can short-circuit -324 if (authManager.authorize(user, tableName, (byte[])null, permRequest)) { -325 return AuthResult.allow(request, "Table permission granted", user, -326 permRequest, tableName, families); -327 } -328 -329 // 3. check permissions against the requested families -330 if (families != null && families.size() > 0) { -331 // all families must pass -332 for (Map.Entry<byte [], ? extends Collection<?>> family : families.entrySet()) { -333 // a) check for family level access -334 if (authManager.authorize(user, tableName, family.getKey(), -335 permRequest)) { -336 continue; // family-level permission overrides per-qualifier -337 } -338 -339 // b) qualifier level access can still succeed -340 if ((family.getValue() != null) && (family.getValue().size() > 0)) { -341 if (family.getValue() instanceof Set) { -342 // for each qualifier of the family -343 Set<byte[]> familySet = (Set<byte[]>)family.getValue(); -344 for (byte[] qualifier : familySet) { -345 if (!authManager.authorize(user, tableName, family.getKey(), -346 qualifier, permRequest)) { -347 return AuthResult.deny(request, "Failed qualifier check", user, -348 permRequest, tableName, makeFamilyMap(family.getKey(), qualifier)); -349 } -350 } -351 } else if (family.getValue() instanceof List) { // List<Cell> -352 List<Cell> cellList = (List<Cell>)family.getValue(); -353 for (Cell cell : cellList) { -354 if (!authManager.authorize(user, tableName, family.getKey(), -355 CellUtil.cloneQualifier(cell), permRequest)) { -356 return AuthResult.deny(request, "Failed qualifier check", user, permRequest, -357 tableName, makeFamilyMap(family.getKey(), CellUtil.cloneQualifier(cell))); -358 } -359 } -360 } -361 } else { -362 // no qualifiers and family-level check already failed -363 return AuthResult.deny(request, "Failed family check", user, permRequest, -364 tableName, makeFamilyMap(family.getKey(), null)); -365 } -366 } -367 -368 // all family checks passed -369 return AuthResult.allow(request, "All family checks passed", user, permRequest, -370 tableName, families); -371 } -372 -373 // 4. no families to check and table level access failed -374 return AuthResult.deny(request, "No families to check and table permission failed", -375 user, permRequest, tableName, families); -376 } -377 -378 /** -379 * Check the current user for authorization to perform a specific action -380 * against the given set of row data. -381 * @param opType the operation type -382 * @param user the user -383 * @param e the coprocessor environment -384 * @param families the map of column families to qualifiers present in -385 * the request -386 * @param actions the desired actions -387 * @return an authorization result -388 */ -389 AuthResult permissionGranted(OpType opType, User user, RegionCoprocessorEnvironment e, -390 Map<byte [], ? extends Collection<?>> families, Action... actions) { -391 AuthResult result = null; -392 for (Action action: actions) { -393 result = permissionGranted(opType.toString(), user, action, e, families); -394 if (!result.isAllowed()) { -395 return result; -396 } -397 } -398 return result; -399 } -400 -401 private void logResult(AuthResult result) { -402 if (AUDITLOG.isTraceEnabled()) { -403 InetAddress remoteAddr = RpcServer.getRemoteAddress(); -404 AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") + -405 " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") + -406 "; reason: " + result.getReason() + -407 "; remote address: " + (remoteAddr != null ? remoteAddr : "") + -408 "; request: " + result.getRequest() + -409 "; context: " + result.toContextString()); -410 } -411 } -412 -413 /** -414 * Returns the active user to which authorization checks should be applied. -415 * If we are in the context of an RPC call, the remote user is used, -416 * otherwise the currently logged in user is used. -417 */ -418 private User getActiveUser(ObserverContext ctx) throws IOException { -419 User user = ctx.getCaller(); -420 if (user == null) { -421 // for non-rpc handling, fallback to system user -422 user = userProvider.getCurrent(); -423 } -424 return user; -425 } -426 -427 /** -428 * Authorizes that the current user has any of the given permissions for the -429 * given table, column family and column qualifier. -430 * @param tableName Table requested -431 * @param family Column family requested -432 * @param qualifier Column qualifier requested -433 * @throws IOException if obtaining the current user fails -434 * @throws AccessDeniedException if user has no authorization -435 */ -436 private void requirePermission(User user, String request, TableName tableName, byte[] family, -437 byte[] qualifier, Action... permissions) throws IOException { -438 AuthResult result = null; -439 -440 for (Action permission : permissions) { -441 if (authManager.authorize(user, tableName, family, qualifier, permission)) { -442 result = AuthResult.allow(request, "Table permission granted", user, -443 permission, tableName, family, qualifier); -444 break; -445 } else { -446 // rest of the world -447 result = AuthResult.deny(request, "Insufficient permissions", user, -448 permission, tableName, family, qualifier); -449 } -450 } -451 logResult(result); -452 if (authorizationEnabled && !result.isAllowed()) { -453 throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); -454 } -455 } -456 -457 /** -458 * Authorizes that the current user has any of the given permissions for the -459 * given table, column family and column qualifier. -460 * @param tableName Table requested -461 * @param family Column family param -462 * @param qualifier Column qualifier param -463 * @throws IOException if obtaining the current user fails -464 * @throws AccessDeniedException if user has no authorization -465 */ -466 private void requireTablePermission(User user, String request, TableName tableName, byte[] family, -467 byte[] qualifier, Action... permissions) throws IOException { -468 AuthResult result = null; -469 -470 for (Action permission : permissions) { -471 if (authManager.authorize(user, tableName, null, null, permission)) { -472 result = AuthResult.allow(request, "Table permission granted", user, -473 permission, tableName, null, null); -474 result.getParams().setFamily(family).setQualifier(qualifier); -475 break; -476 } else { -477 // rest of the world -478 result = AuthResult.deny(request, "Insufficient permissions", user, -479 permission, tableName, family, qualifier); -480 result.getParams().setFamily(family).setQualifier(qualifier); -481 } -482 } -483 logResult(result); -484 if (authorizationEnabled && !result.isAllowed()) { -485 throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); -486 } -487 } -488 -489 /** -490 * Authorizes that the current user has any of the given permissions to access the table. -491 * -492 * @param tableName Table requested -493 * @param permissions Actions being requested -494 * @throws IOException if obtaining the current user fails -495 * @throws AccessDeniedException if user has no authorization -496 */ -497 private void requireAccess(User user, String request, TableName tableName, -498 Action... permissions) throws IOException { -499 AuthResult result = null; -500 -501 for (Action permission : permissions) { -502 if (authManager.hasAccess(user, tableName, permission)) { -503 result = AuthResult.allow(request, "Table permission granted", user, -504 permission, tableName, null, null); -505 break; -506 } else { -507 // rest of the world -508 result = AuthResult.deny(request, "Insufficient permissions", user, -509 permission, tableName, null, null); -510 } -511 } -512 logResult(result); -513 if (authorizationEnabled && !result.isAllowed()) { -514 throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); -515 } -516 } -517 -518 /** -519 * Authorizes that the current user has global privileges for the given action. -520 * @param perm The action being requested -521 * @throws IOException if obtaining the current user fails -522 * @throws AccessDeniedException if authorization is denied -523 */ -524 private void requirePermission(User user, String request, Action perm) throws IOException { -525 requireGlobalPermission(user, request, perm, null, null); -526 } -527 -528 /** -529 * Checks that the user has the given global permission. The generated -530 * audit log message will contain context information for the operation -531 * being authorized, based on the given parameters. -532 * @param perm Action being requested -533 * @param tableName Affected table name. -534 * @param familyMap Affected column families. -535 */ -536 private void requireGlobalPermission(User user, String request, Action perm, TableName tableName, -537 Map<byte[], ? extends Collection<byte[]>> familyMap) throws IOException { -538 AuthResult result = null; -539 if (authManager.authorize(user, perm)) { -540 result = AuthResult.allow(request, "Global check allowed", user, perm, tableName, familyMap); -541 result.getParams().setTableName(tableName).setFamilies(familyMap); -542 logResult(result); -543 } else { -544 result = AuthResult.deny(request, "Global check failed", user, perm, tableName, familyMap); -545 result.getParams().setTableName(tableName).setFamilies(familyMap); -546 logResult(result); -547 if (authorizationEnabled) { -548 throw new AccessDeniedException("Insufficient permissions for user '" + -549 (user != null ? user.getShortName() : "null") +"' (global, action=" + -550 perm.toString() + ")"); -551 } -552 } -553 } -554 -555 /** -556 * Checks that the user has the given global permission. The generated -557 * audit log message will contain context information for the operation -558 * being authorized, based on the given parameters. -559 * @param perm Action being requested -560 * @param namespace -561 */ -562 private void requireGlobalPermission(User user, String request, Action perm, -563 String namespace) throws IOException { -564 AuthResult authResult = null; -565 if (authManager.authorize(user, perm)) { -566 authResult = AuthResult.allow(request, "Global check allowed", user, perm, null); -567 authResult.getParams().setNamespace(namespace); -568 logResult(authResult); -569 } else { -570 authResult = AuthResult.deny(request, "Global check failed", user, perm, null); -571 authResult.getParams().setNamespace(namespace); -572 logResult(authResult); -573 if (authorizationEnabled) { -574 throw new AccessDeniedException("Insufficient permissions for user '" + -575 (user != null ? user.getShortName() : "null") +"' (global, action=" + -576 perm.toString() + ")"); -577 } -578 } -579 } -580 -581 /** -582 * Checks that the user has the given global or namespace permission. -583 * @param namespace -584 * @param permissions Actions being requested -585 */ -586 public void requireNamespacePermission(User user, String request, String namespace, -587 Action... permissions) throws IOException { -588 AuthResult result = null; -589 -590 for (Action permission : permissions) { -591 if (authManager.authorize(user, namespace, permission)) { -592 result = AuthResult.allow(request, "Namespace permission granted", -593 user, permission, namespace); -594 break; -595 } else { -596 // rest of the world -597 result = AuthResult.deny(request, "Insufficient permissions", user, -598 permission, namespace); -599 } -600 } -601 logResult(result); -602 if (authorizationEnabled && !result.isAllowed()) { -603 throw new AccessDeniedException("Insufficient permissions " -604 + result.toContextString()); -605 } -606 } -607 -608 /** -609 * Checks that the user has the given global or namespace permission. -610 * @param namespace -611 * @param permissions Actions being requested -612 */ -613 public void requireNamespacePermission(User user, String request, String namespace, -614 TableName tableName, Map<byte[], ? extends Collection<byte[]>> familyMap, -615 Action... permissions) -616 throws IOException { -617 AuthResult result = null; -618 -619 for (Action permission : permissions) { -620 if (authManager.authorize(user, namespace, permission)) { -621 result = AuthResult.allow(request, "Namespace permission granted", -622 user, permission, namespace); -623 result.getParams().setTableName(tableName).setFamilies(familyMap); -624 break; -625 } else { -626 // rest of the world -627 result = AuthResult.deny(request, "Insufficient permissions", user, -628 permission, namespace); -629 result.getParams().setTableName(tableName).setFamilies(familyMap); -630 } -631 } -632 logResult(result); -633 if (authorizationEnabled && !result.isAllowed()) { -634 throw new AccessDeniedException("Insufficient permissions " -635 + result.toContextString()); -636 } -637 } -638 -639 /** -640 * Returns <code>true</code> if the current user is allowed the given action -641 * over at least one of the column qualifiers in the given column families. -642 */ -643 private boolean hasFamilyQualifierPermission(User user, -644 Action perm, -645 RegionCoprocessorEnvironment env, -646 Map<byte[], ? extends Collection<byte[]>> familyMap) -647 throws IOException { -648 HRegionInfo hri = env.getRegion().getRegionInfo(); -649 TableName tableName = hri.getTable(); -650 -651 if (user == null) { -652 return false; -653 } -654 -655 if (familyMap != null && familyMap.size() > 0) { -656 // at least one family must be allowed -657 for (Map.Entry<byte[], ? extends Collection<byte[]>> family : -658 familyMap.entrySet()) { -659 if (family.getValue() != null && !family.getValue().isEmpty()) { -660 for (byte[] qualifier : family.getValue()) { -661 if (authManager.matchPermission(user, tableName, -662 family.getKey(), qualifier, perm)) { -663 return true; -664 } -665 } -666 } else { -667 if (authManager.matchPermission(user, tableName, family.getKey(), -668 perm)) { -669 return true; -670 } -671 } -672 } -673 } else if (LOG.isDebugEnabled()) { -674 LOG.debug("Empty family map passed for permission check"); -675 } -676 -677 return false; -678 } -679 -680 private enum OpType { -681 GET("get"), -682 EXISTS("exists"), -683 SCAN("scan"), -684 PUT("put"), -685 DELETE("delete"), -686 CHECK_AND_PUT("checkAndPut"), -687 CHECK_AND_DELETE("checkAndDelete"), -688 INCREMENT_COLUMN_VALUE("incrementColumnValue"), -689 APPEND("append"), -690 INCREMENT("increment"); -691 -692 private String type; -693 -694 private OpType(String type) { -695 this.type = type; -696 } -697 -698 @Override -699 public String toString() { -700 return type; -701 } -702 } -703 -704 /** -705 * Determine if cell ACLs covered by the operation grant access. This is expensive. -706 * @return false if cell ACLs failed to grant access, true otherwise -707 * @throws IOException -708 */ -709 private boolean checkCoveringPermission(User user, OpType request, RegionCoprocessorEnvironment e, -710 byte[] row, Map<byte[], ? extends Collection<?>> familyMap, long opTs, Action... actions) -711 throws IOException { -712 if (!cellFeaturesEnabled) { -713 return false; -714 } -715 long cellGrants = 0; -716 long latestCellTs = 0; -717 Get get = new Get(row); -718 // Only in case of Put/Delete op, consider TS within cell (if set for individual cells). -719 // When every cell, within a Mutation, can be linked with diff TS we can not rely on only one -720 // version. We have to get every cell version and check its TS against the TS asked for in -721 // Mutation and skip those Cells which is outside this Mutation TS.In case of Put, we have to -722 // consider only one such passing cell. In case of Delete we have to consider all the cell -723 // versions under this passing version. When Delete Mutation contains columns which are a -724 // version delete just consider only one version for those column cells. -725 boolean considerCellTs = (request == OpType.PUT || request == OpType.DELETE); -726 if (considerCellTs) { -727 get.setMaxVersions(); -728 } else { -729 get.setMaxVersions(1); -730 } -731 boolean diffCellTsFromOpTs = false; -732 for (Map.Entry<byte[], ? extends Collection<?>> entry: familyMap.entrySet()) { -733 byte[] col = entry.getKey(); -734 // TODO: HBASE-7114 could possibly unify the collection type in family -735 // maps so we would not need to do this -736 if (entry.getValue() instanceof Set) { -737 Set<byte[]> set = (Set<byte[]>)entry.getValue(); -738 if (set == null || set.isEmpty()) { -739 get.addFamily(col); -740 } else { -741 for (byte[] qual: set) { -742 get.addColumn(col, qual); -743 } -744 } -745 } else if (entry.getValue() instanceof List) { -746 List<Cell> list = (List<Cell>)entry.getValue(); -747 if (list == null || list.isEmpty()) { -748 get.addFamily(col); -749 } else { -750 // In case of family delete, a Cell will be added into the list with Qualifier as null. -751 for (Cell cell : list) { -752 if (cell.getQualifierLength() == 0 -753 && (cell.getTypeByte() == Type.DeleteFamily.getCode() -754 || cell.getTypeByte() == Type.DeleteFamilyVersion.getCode())) { -755 get.addFamily(col); -756 } else { -757 get.addColumn(col, CellUtil.cloneQualifier(cell)); -758 } -759 if (considerCellTs) { -760 long cellTs = cell.getTimestamp(); -761 latestCellTs = Math.max(latestCellTs, cellTs); -762 diffCellTsFromOpTs = diffCellTsFromOpTs || (opTs != cellTs); -763 } -764 } -765 } -766 } else if (entry.getValue() == null) { -767 get.addFamily(col); -768 } else { -769 throw new RuntimeException("Unhandled collection type " + -770 entry.getValue().getClass().getName()); -771 } -772 } -773 // We want to avoid looking into the future. So, if the cells of the -774 // operation specify a timestamp, or the operation itself specifies a -775 // timestamp, then we use the maximum ts found. Otherwise, we bound -776 // the Get to the current server time. We add 1 to the timerange since -777 // the upper bound of a timerange is exclusive yet we need to examine -778 // any cells found there inclusively. -779 long latestTs = Math.max(opTs, latestCellTs); -780 if (latestTs == 0 || latestTs == HConstants.LATEST_TIMESTAMP) { -781 latestTs = EnvironmentEdgeManager.currentTime(); -782 } -783 get.setTimeRange(0, latestTs + 1); -784 // In case of Put operation we set to read all versions. This was done to consider the case -785 // where columns are added with TS other than the Mutation TS. But normally this wont be the -786 // case with Put. There no need to get all versions but get latest version only. -787 if (!diffCellTsFromOpTs && request == OpType.PUT) { -788 get.setMaxVersions(1); -789 } -790 if (LOG.isTraceEnabled()) { -791 LOG.trace("Scanning for cells with " + get); -792 } -793 // This Map is identical to familyMap. The key is a BR rather than byte[]. -794 // It will be easy to do gets over this new Map as we can create get keys over the Cell cf by -795 // new SimpleByteRange(cell.familyArray, cell.familyOffset, cell.familyLen) -796 Map<ByteRange, List<Cell>> familyMap1 = new HashMap<>(); -797 for (Entry<byte[], ? extends Collection<?>> entry : familyMap.entrySet()) { -798 if (entry.getValue() instanceof List) { -799 familyMap1.put(new SimpleMutableByteRange(entry.getKey()), (List<Cell>) entry.getValue()); -800 } -801 } -802 RegionScanner scanner = getRegion(e).getScanner(new Scan(get)); -803 List<Cell> cells = Lists.newArrayList(); -804 Cell prevCell = null; -805 ByteRange curFam = new SimpleMutableByteRange(); -806 boolean curColAllVersions = (request == OpType.DELETE); -807 long curColCheckTs = opTs; -808 boolean foundColumn = false; -809 try { -810 boolean more = false; -811 ScannerContext scannerContext = ScannerContext.newBuilder().setBatchLimit(1).build(); -812 -813 do { -814 cells.clear(); -815 // scan with limit as 1 to hold down memory use on wide rows -816 more = scanner.next(cells, scannerContext); -817 for (Cell cell: cells) { -818 if (LOG.isTraceEnabled()) { -819 LOG.trace("Found cell " + cell); -820 } -821 boolean colChange = prevCell == null || !CellUtil.matchingColumn(prevCell, cell); -822 if (colChange) foundColumn = false; -823 prevCell = cell; -824 if (!curColAllVersions && foundColumn) { -825 continue; -826 } -827 if (colChange && considerCellTs) { -828 curFam.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()); -829 List<Cell> cols = familyMap1.get(curFam); -830 for (Cell col : cols) { -831 // null/empty qualifier is used to denote a Family delete. The TS and delete type -832 // associated with this is applicable for all columns within the family. That is -833 // why the below (col.getQualifierLength() == 0) check. -834 if ((col.getQualifierLength() == 0 && request == OpType.DELETE) -835 || CellUtil.matchingQualifier(cell, col)) { -836 byte type = col.getTypeByte(); -837 if (considerCellTs) { -838 curColCheckTs = col.getTimestamp(); -839 } -840 // For a Delete op we pass allVersions as true. When a Delete Mutation contains -841 // a version delete for a column no need to check all the covering cells within -842 // that column. Check all versions when Type is DeleteColumn or DeleteFamily -843 // One version delete types are Delete/DeleteFamilyVersion -844 curColAllVersions = (KeyValue.Type.DeleteColumn.getCode() == type) -845 || (KeyValue.Type.DeleteFamily.getCode() == type); -846 break; -847 } -848 } -849 } -850 if (cell.getTimestamp() > curColCheckTs) { -851 // Just ignore this cell. This is not a covering cell. -852 continue; -853 } -854 foundColumn = true; -855 for (Action action: actions) { -856 // Are there permissions for this user for the cell? -857 if (!authManager.authorize(user, getTableName(e), cell, action)) { -858 // We can stop if the cell ACL denies access -859 return false; -860 } -861 } -862 cellGrants++; -863 } -864 } while (more); -865 } catch (AccessDeniedException ex) { -866 throw ex; -867 } catch (IOException ex) { -868 LOG.error("Exception while getting cells to calculate covering permission", ex); -869 } finally { -870 scanner.close(); -871 } -872 // We should not authorize unless we have found one or more cell ACLs that -873 // grant access. This code is used to check for additional permissions -874 // after no table or CF grants ar