HBASE-11194 [AccessController] issue with covering permission check in case of concurrent op on same row (Anoop)
This commit is contained in:
parent
0e647de3ee
commit
463fc9fbd8
|
@ -84,6 +84,7 @@ import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessCont
|
|||
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
|
||||
import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||
import org.apache.hadoop.hbase.regionserver.InternalScanner;
|
||||
import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress;
|
||||
import org.apache.hadoop.hbase.regionserver.RegionScanner;
|
||||
import org.apache.hadoop.hbase.regionserver.ScanType;
|
||||
import org.apache.hadoop.hbase.regionserver.Store;
|
||||
|
@ -151,6 +152,8 @@ public class AccessController extends BaseRegionObserver
|
|||
|
||||
private static final Log AUDITLOG =
|
||||
LogFactory.getLog("SecurityLogger."+AccessController.class.getName());
|
||||
private static final String CHECK_COVERING_PERM = "check_covering_perm";
|
||||
private static final byte[] TRUE = Bytes.toBytes(true);
|
||||
|
||||
TableAuthManager authManager = null;
|
||||
|
||||
|
@ -1463,14 +1466,13 @@ public class AccessController extends BaseRegionObserver
|
|||
Map<byte[],? extends Collection<Cell>> families = put.getFamilyCellMap();
|
||||
User user = getActiveUser();
|
||||
AuthResult authResult = permissionGranted(OpType.PUT, user, env, families, Action.WRITE);
|
||||
if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
authResult.setAllowed(checkCoveringPermission(OpType.PUT, env, put.getRow(), families,
|
||||
put.getTimeStamp(), Action.WRITE));
|
||||
authResult.setReason("Covering cell set");
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
if (cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
put.setAttribute(CHECK_COVERING_PERM, TRUE);
|
||||
} else {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
|
||||
if (bytes != null) {
|
||||
|
@ -1507,14 +1509,43 @@ public class AccessController extends BaseRegionObserver
|
|||
Map<byte[],? extends Collection<Cell>> families = delete.getFamilyCellMap();
|
||||
User user = getActiveUser();
|
||||
AuthResult authResult = permissionGranted(OpType.DELETE, user, env, families, Action.WRITE);
|
||||
if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
authResult.setAllowed(checkCoveringPermission(OpType.DELETE, env, delete.getRow(), families,
|
||||
delete.getTimeStamp(), Action.WRITE));
|
||||
authResult.setReason("Covering cell set");
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
if (cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
delete.setAttribute(CHECK_COVERING_PERM, TRUE);
|
||||
} else {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
|
||||
MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
|
||||
if (cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
|
||||
for (int i = 0; i < miniBatchOp.size(); i++) {
|
||||
Mutation m = miniBatchOp.getOperation(i);
|
||||
if (m.getAttribute(CHECK_COVERING_PERM) != null) {
|
||||
// We have a failure with table, cf and q perm checks and now giving a chance for cell
|
||||
// perm check
|
||||
OpType opType = (m instanceof Put) ? OpType.PUT : OpType.DELETE;
|
||||
AuthResult authResult = null;
|
||||
if (checkCoveringPermission(opType, c.getEnvironment(), m.getRow(), m.getFamilyCellMap(),
|
||||
m.getTimeStamp(), Action.WRITE)) {
|
||||
authResult = AuthResult.allow(opType.toString(), "Covering cell set", getActiveUser(),
|
||||
Action.WRITE, table, m.getFamilyCellMap());
|
||||
} else {
|
||||
authResult = AuthResult.deny(opType.toString(), "Covering cell set", getActiveUser(),
|
||||
Action.WRITE, table, m.getFamilyCellMap());
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions "
|
||||
+ authResult.toContextString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1539,14 +1570,13 @@ public class AccessController extends BaseRegionObserver
|
|||
User user = getActiveUser();
|
||||
AuthResult authResult = permissionGranted(OpType.CHECK_AND_PUT, user, env, families,
|
||||
Action.READ, Action.WRITE);
|
||||
if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
authResult.setAllowed(checkCoveringPermission(OpType.CHECK_AND_PUT, env, row, families,
|
||||
HConstants.LATEST_TIMESTAMP, Action.READ));
|
||||
authResult.setReason("Covering cell set");
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
if (cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
put.setAttribute(CHECK_COVERING_PERM, TRUE);
|
||||
} else {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
|
||||
if (bytes != null) {
|
||||
|
@ -1559,6 +1589,33 @@ public class AccessController extends BaseRegionObserver
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preCheckAndPutAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
|
||||
final byte[] row, final byte[] family, final byte[] qualifier,
|
||||
final CompareFilter.CompareOp compareOp, final ByteArrayComparable comparator, final Put put,
|
||||
final boolean result) throws IOException {
|
||||
if (put.getAttribute(CHECK_COVERING_PERM) != null) {
|
||||
// We had failure with table, cf and q perm checks and now giving a chance for cell
|
||||
// perm check
|
||||
TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
|
||||
Map<byte[], ? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
|
||||
AuthResult authResult = null;
|
||||
if (checkCoveringPermission(OpType.CHECK_AND_PUT, c.getEnvironment(), row, families,
|
||||
HConstants.LATEST_TIMESTAMP, Action.READ)) {
|
||||
authResult = AuthResult.allow(OpType.CHECK_AND_PUT.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.READ, table, families);
|
||||
} else {
|
||||
authResult = AuthResult.deny(OpType.CHECK_AND_PUT.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.READ, table, families);
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preCheckAndDelete(final ObserverContext<RegionCoprocessorEnvironment> c,
|
||||
final byte [] row, final byte [] family, final byte [] qualifier,
|
||||
|
@ -1577,14 +1634,41 @@ public class AccessController extends BaseRegionObserver
|
|||
User user = getActiveUser();
|
||||
AuthResult authResult = permissionGranted(OpType.CHECK_AND_DELETE, user, env, families,
|
||||
Action.READ, Action.WRITE);
|
||||
if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
authResult.setAllowed(checkCoveringPermission(OpType.CHECK_AND_DELETE, env, row, families,
|
||||
HConstants.LATEST_TIMESTAMP, Action.READ));
|
||||
authResult.setReason("Covering cell set");
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
if (cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
delete.setAttribute(CHECK_COVERING_PERM, TRUE);
|
||||
} else {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preCheckAndDeleteAfterRowLock(
|
||||
final ObserverContext<RegionCoprocessorEnvironment> c, final byte[] row, final byte[] family,
|
||||
final byte[] qualifier, final CompareFilter.CompareOp compareOp,
|
||||
final ByteArrayComparable comparator, final Delete delete, final boolean result)
|
||||
throws IOException {
|
||||
if (delete.getAttribute(CHECK_COVERING_PERM) != null) {
|
||||
// We had failure with table, cf and q perm checks and now giving a chance for cell
|
||||
// perm check
|
||||
TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
|
||||
Map<byte[], ? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier);
|
||||
AuthResult authResult = null;
|
||||
if (checkCoveringPermission(OpType.CHECK_AND_DELETE, c.getEnvironment(), row, families,
|
||||
HConstants.LATEST_TIMESTAMP, Action.READ)) {
|
||||
authResult = AuthResult.allow(OpType.CHECK_AND_DELETE.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.READ, table, families);
|
||||
} else {
|
||||
authResult = AuthResult.deny(OpType.CHECK_AND_DELETE.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.READ, table, families);
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1621,14 +1705,13 @@ public class AccessController extends BaseRegionObserver
|
|||
Map<byte[],? extends Collection<Cell>> families = append.getFamilyCellMap();
|
||||
User user = getActiveUser();
|
||||
AuthResult authResult = permissionGranted(OpType.APPEND, user, env, families, Action.WRITE);
|
||||
if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
authResult.setAllowed(checkCoveringPermission(OpType.APPEND, env, append.getRow(),
|
||||
families, HConstants.LATEST_TIMESTAMP, Action.WRITE));
|
||||
authResult.setReason("Covering cell set");
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
if (cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
append.setAttribute(CHECK_COVERING_PERM, TRUE);
|
||||
} else {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
byte[] bytes = append.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
|
||||
if (bytes != null) {
|
||||
|
@ -1641,6 +1724,30 @@ public class AccessController extends BaseRegionObserver
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result preAppendAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
|
||||
final Append append) throws IOException {
|
||||
if (append.getAttribute(CHECK_COVERING_PERM) != null) {
|
||||
// We had failure with table, cf and q perm checks and now giving a chance for cell
|
||||
// perm check
|
||||
TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
|
||||
AuthResult authResult = null;
|
||||
if (checkCoveringPermission(OpType.APPEND, c.getEnvironment(), append.getRow(),
|
||||
append.getFamilyCellMap(), HConstants.LATEST_TIMESTAMP, Action.WRITE)) {
|
||||
authResult = AuthResult.allow(OpType.APPEND.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.WRITE, table, append.getFamilyCellMap());
|
||||
} else {
|
||||
authResult = AuthResult.deny(OpType.APPEND.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.WRITE, table, append.getFamilyCellMap());
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
|
||||
final Increment increment)
|
||||
|
@ -1652,14 +1759,13 @@ public class AccessController extends BaseRegionObserver
|
|||
User user = getActiveUser();
|
||||
AuthResult authResult = permissionGranted(OpType.INCREMENT, user, env, families,
|
||||
Action.WRITE);
|
||||
if (!authResult.isAllowed() && cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
authResult.setAllowed(checkCoveringPermission(OpType.INCREMENT, env, increment.getRow(),
|
||||
families, increment.getTimeRange().getMax(), Action.WRITE));
|
||||
authResult.setReason("Covering cell set");
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
if (cellFeaturesEnabled && !compatibleEarlyTermination) {
|
||||
increment.setAttribute(CHECK_COVERING_PERM, TRUE);
|
||||
} else {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
byte[] bytes = increment.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
|
||||
if (bytes != null) {
|
||||
|
@ -1672,6 +1778,30 @@ public class AccessController extends BaseRegionObserver
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result preIncrementAfterRowLock(final ObserverContext<RegionCoprocessorEnvironment> c,
|
||||
final Increment increment) throws IOException {
|
||||
if (increment.getAttribute(CHECK_COVERING_PERM) != null) {
|
||||
// We had failure with table, cf and q perm checks and now giving a chance for cell
|
||||
// perm check
|
||||
TableName table = c.getEnvironment().getRegion().getRegionInfo().getTable();
|
||||
AuthResult authResult = null;
|
||||
if (checkCoveringPermission(OpType.INCREMENT, c.getEnvironment(), increment.getRow(),
|
||||
increment.getFamilyCellMap(), increment.getTimeRange().getMax(), Action.WRITE)) {
|
||||
authResult = AuthResult.allow(OpType.INCREMENT.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.WRITE, table, increment.getFamilyCellMap());
|
||||
} else {
|
||||
authResult = AuthResult.deny(OpType.INCREMENT.toString(), "Covering cell set",
|
||||
getActiveUser(), Action.WRITE, table, increment.getFamilyCellMap());
|
||||
}
|
||||
logResult(authResult);
|
||||
if (!authResult.isAllowed()) {
|
||||
throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
|
||||
MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException {
|
||||
|
|
|
@ -872,7 +872,7 @@ public class TestCellACLWithMultipleVersions extends SecureTestUtil {
|
|||
HTable t = new HTable(conf, TEST_TABLE.getTableName());
|
||||
try {
|
||||
Delete d = new Delete(TEST_ROW1);
|
||||
d.deleteColumns(TEST_FAMILY1, TEST_Q1);
|
||||
d.deleteColumns(TEST_FAMILY1, TEST_Q1, 120);
|
||||
t.checkAndDelete(TEST_ROW1, TEST_FAMILY1, TEST_Q1, ZERO, d);
|
||||
} finally {
|
||||
t.close();
|
||||
|
|
Loading…
Reference in New Issue