/** * Create a protocol buffer MutateRequest for a conditioned delete * * @param regionName * @param row * @param family * @param qualifier * @param comparator * @param compareType * @param delete * @return a mutate request * @throws IOException */ public static MutateRequest buildMutateRequest( final byte[] regionName, final byte[] row, final byte[] family, final byte [] qualifier, final ByteArrayComparable comparator, final CompareType compareType, final Delete delete) throws IOException { MutateRequest.Builder builder = MutateRequest.newBuilder(); RegionSpecifier region = buildRegionSpecifier( RegionSpecifierType.REGION_NAME, regionName); builder.setRegion(region); Condition condition = buildCondition( row, family, qualifier, comparator, compareType); builder.setMutation(ProtobufUtil.toMutation(MutationType.DELETE, delete, MutationProto.newBuilder())); builder.setCondition(condition); return builder.build(); }
@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 (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } } return result; }
/** * Convert a protocol buffer Comparator to a ByteArrayComparable * * @param proto the protocol buffer Comparator to convert * @return the converted ByteArrayComparable */ @SuppressWarnings("unchecked") public static ByteArrayComparable toComparator(ComparatorProtos.Comparator proto) throws IOException { String type = proto.getName(); String funcName = "parseFrom"; byte [] value = proto.getSerializedComparator().toByteArray(); try { Class<? extends ByteArrayComparable> c = (Class<? extends ByteArrayComparable>)Class.forName(type, true, CLASS_LOADER); Method parseFrom = c.getMethod(funcName, byte[].class); if (parseFrom == null) { throw new IOException("Unable to locate function: " + funcName + " in type: " + type); } return (ByteArrayComparable)parseFrom.invoke(null, value); } catch (Exception e) { throw new IOException(e); } }
public ByteArrayComparableModel( ByteArrayComparable comparator) { String typeName = comparator.getClass().getSimpleName(); ComparatorType type = ComparatorType.valueOf(typeName); this.type = typeName; switch (type) { case BinaryComparator: case BinaryPrefixComparator: this.value = Base64.encodeBytes(comparator.getValue()); break; case BitComparator: this.value = Base64.encodeBytes(comparator.getValue()); this.op = ((BitComparator)comparator).getOperator().toString(); break; case NullComparator: break; case RegexStringComparator: case SubstringComparator: this.value = Bytes.toString(comparator.getValue()); break; default: throw new RuntimeException("unhandled filter type: " + type); } }
@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; }
/** * Supports Coprocessor 'bypass'. * @param row row to check * @param family column family * @param qualifier column qualifier * @param op the comparison operation * @param comparator the comparator * @param put data to put if check succeeds * @return true or false to return to client if default processing should be bypassed, or null * otherwise */ public Boolean preCheckAndPut(final byte [] row, final byte [] family, final byte [] qualifier, final CompareOperator op, final ByteArrayComparable comparator, final Put put) throws IOException { boolean bypassable = true; boolean defaultResult = false; if (coprocEnvironments.isEmpty()) { return null; } return execOperationWithResult( new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, defaultResult, bypassable) { @Override public Boolean call(RegionObserver observer) throws IOException { return observer.preCheckAndPut(this, row, family, qualifier, op, comparator, put, getResult()); } }); }
/** * Supports Coprocessor 'bypass'. * @param row row to check * @param family column family * @param qualifier column qualifier * @param op the comparison operation * @param comparator the comparator * @param put data to put if check succeeds * @return true or false to return to client if default processing should be bypassed, or null * otherwise */ public Boolean preCheckAndPutAfterRowLock( final byte[] row, final byte[] family, final byte[] qualifier, final CompareOperator op, final ByteArrayComparable comparator, final Put put) throws IOException { boolean bypassable = true; boolean defaultResult = false; if (coprocEnvironments.isEmpty()) { return null; } return execOperationWithResult( new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, defaultResult, bypassable) { @Override public Boolean call(RegionObserver observer) throws IOException { return observer.preCheckAndPutAfterRowLock(this, row, family, qualifier, op, comparator, put, getResult()); } }); }
/** * @param row row to check * @param family column family * @param qualifier column qualifier * @param op the comparison operation * @param comparator the comparator * @param put data to put if check succeeds * @throws IOException e */ public boolean postCheckAndPut(final byte [] row, final byte [] family, final byte [] qualifier, final CompareOperator op, final ByteArrayComparable comparator, final Put put, boolean result) throws IOException { if (this.coprocEnvironments.isEmpty()) { return result; } return execOperationWithResult( new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, result) { @Override public Boolean call(RegionObserver observer) throws IOException { return observer.postCheckAndPut(this, row, family, qualifier, op, comparator, put, getResult()); } }); }
/** * Supports Coprocessor 'bypass'. * @param row row to check * @param family column family * @param qualifier column qualifier * @param op the comparison operation * @param comparator the comparator * @param delete delete to commit if check succeeds * @return true or false to return to client if default processing should be bypassed, * or null otherwise */ public Boolean preCheckAndDelete(final byte [] row, final byte [] family, final byte [] qualifier, final CompareOperator op, final ByteArrayComparable comparator, final Delete delete) throws IOException { boolean bypassable = true; boolean defaultResult = false; if (coprocEnvironments.isEmpty()) { return null; } return execOperationWithResult( new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, defaultResult, bypassable) { @Override public Boolean call(RegionObserver observer) throws IOException { return observer.preCheckAndDelete(this, row, family, qualifier, op, comparator, delete, getResult()); } }); }
/** * Supports Coprocessor 'bypass'. * @param row row to check * @param family column family * @param qualifier column qualifier * @param op the comparison operation * @param comparator the comparator * @param delete delete to commit if check succeeds * @return true or false to return to client if default processing should be bypassed, * or null otherwise */ public Boolean preCheckAndDeleteAfterRowLock(final byte[] row, final byte[] family, final byte[] qualifier, final CompareOperator op, final ByteArrayComparable comparator, final Delete delete) throws IOException { boolean bypassable = true; boolean defaultResult = false; if (coprocEnvironments.isEmpty()) { return null; } return execOperationWithResult( new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, defaultResult, bypassable) { @Override public Boolean call(RegionObserver observer) throws IOException { return observer.preCheckAndDeleteAfterRowLock(this, row, family, qualifier, op, comparator, delete, getResult()); } }); }
/** * @param row row to check * @param family column family * @param qualifier column qualifier * @param op the comparison operation * @param comparator the comparator * @param delete delete to commit if check succeeds * @throws IOException e */ public boolean postCheckAndDelete(final byte [] row, final byte [] family, final byte [] qualifier, final CompareOperator op, final ByteArrayComparable comparator, final Delete delete, boolean result) throws IOException { if (this.coprocEnvironments.isEmpty()) { return result; } return execOperationWithResult( new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, result) { @Override public Boolean call(RegionObserver observer) throws IOException { return observer.postCheckAndDelete(this, row, family, qualifier, op, comparator, delete, getResult()); } }); }
private boolean checkAndPut(MutationProto mutation, Condition condition) throws IOException { boolean success; final byte[] row = condition.getRow().array(); final byte[] cf = condition.getFamily().array(); final byte[] cq = condition.getQualifier().array(); final CompareFilter.CompareOp compareOp = CompareFilter.CompareOp.valueOf(condition.getCompareType().name()); final ByteArrayComparable comparator = ReverseProtobufUtil.toComparator(condition.getComparator()); success = theRegion.checkAndMutate(row, cf, cq, compareOp, comparator, ReverseProtobufUtil.toPut(mutation), true); return success; }
private boolean checkAndDelete(MutationProto mutation, Condition condition) throws IOException { boolean success; final byte[] row = condition.getRow().array(); final byte[] cf = condition.getFamily().array(); final byte[] cq = condition.getQualifier().array(); final CompareFilter.CompareOp compareOp = CompareFilter.CompareOp.valueOf(condition.getCompareType().name()); final ByteArrayComparable comparator = ReverseProtobufUtil.toComparator(condition.getComparator()); success = theRegion.checkAndMutate(row, cf, cq, compareOp, comparator, ReverseProtobufUtil.toDelete(mutation), true); return success; }
/** * @param row row to check * @param family column family * @param qualifier column qualifier * @param compareOp the comparison operation * @param comparator the comparator * @param put data to put if check succeeds * @throws IOException e */ public boolean postCheckAndPut(final byte [] row, final byte [] family, final byte [] qualifier, final CompareOp compareOp, final ByteArrayComparable comparator, final Put put, boolean result) throws IOException { ObserverContext<RegionCoprocessorEnvironment> ctx = null; for (RegionEnvironment env: coprocessors) { if (env.getInstance() instanceof RegionObserver) { ctx = ObserverContext.createAndPrepare(env, ctx); try { result = ((RegionObserver)env.getInstance()).postCheckAndPut(ctx, row, family, qualifier, compareOp, comparator, put, result); } catch (Throwable e) { handleCoprocessorThrowable(env, e); } if (ctx.shouldComplete()) { break; } } } return result; }
/** * @param row row to check * @param family column family * @param qualifier column qualifier * @param compareOp the comparison operation * @param comparator the comparator * @param delete delete to commit if check succeeds * @return true or false to return to client if default processing should * be bypassed, or null otherwise * @throws IOException e */ public Boolean preCheckAndDelete(final byte [] row, final byte [] family, final byte [] qualifier, final CompareOp compareOp, final ByteArrayComparable comparator, Delete delete) throws IOException { boolean bypass = false; boolean result = false; ObserverContext<RegionCoprocessorEnvironment> ctx = null; for (RegionEnvironment env: coprocessors) { if (env.getInstance() instanceof RegionObserver) { ctx = ObserverContext.createAndPrepare(env, ctx); try { result = ((RegionObserver)env.getInstance()).preCheckAndDelete(ctx, row, family, qualifier, compareOp, comparator, delete, result); } catch (Throwable e) { handleCoprocessorThrowable(env, e); } bypass |= ctx.shouldBypass(); if (ctx.shouldComplete()) { break; } } } return bypass ? result : null; }
/** * @param row row to check * @param family column family * @param qualifier column qualifier * @param compareOp the comparison operation * @param comparator the comparator * @param delete delete to commit if check succeeds * @throws IOException e */ public boolean postCheckAndDelete(final byte [] row, final byte [] family, final byte [] qualifier, final CompareOp compareOp, final ByteArrayComparable comparator, final Delete delete, boolean result) throws IOException { ObserverContext<RegionCoprocessorEnvironment> ctx = null; for (RegionEnvironment env: coprocessors) { if (env.getInstance() instanceof RegionObserver) { ctx = ObserverContext.createAndPrepare(env, ctx); try { result = ((RegionObserver)env.getInstance()) .postCheckAndDelete(ctx, row, family, qualifier, compareOp, comparator, delete, result); } catch (Throwable e) { handleCoprocessorThrowable(env, e); } if (ctx.shouldComplete()) { break; } } } return result; }
/** * Needed for hbase-0.95+ * * @throws java.io.IOException */ public static ByteArrayComparable parseFrom( final byte[] pbBytes ) { DataInput in = new DataInputStream( new ByteArrayInputStream( pbBytes ) ); try { boolean m_isInteger = in.readBoolean(); boolean m_isLongOrDouble = in.readBoolean(); long m_longValue = in.readLong(); double m_doubleValue = in.readDouble(); if ( m_isInteger ) { return new DeserializedNumericComparator( m_isInteger, m_isLongOrDouble, m_longValue ); } else { return new DeserializedNumericComparator( m_isInteger, m_isLongOrDouble, m_doubleValue ); } } catch ( IOException e ) { throw new RuntimeException( "Unable to deserialize byte array", e ); } }
@Test public void testAddColumnFilterToScanDate() throws Exception { ColumnFilter cf = new ColumnFilter( "Family" ); cf.setComparisonOperator( ColumnFilter.ComparisonType.LESS_THAN ); cf.setConstant( "07/10/96 4:5 PM" ); cf.setSignedComparison( true ); VariableSpace space = CommonHBaseConnectionTest.mockVariableSpace(); connectionSpy.m_sourceScan = new Scan(); ByteArrayComparable comparator = mock( ByteArrayComparable.class ); doReturn( comparator ).when( connectionSpy ).getDateComparator( eq( cf ), eq( space ), anyString() ); HBaseValueMeta meta = new HBaseValueMeta( "colFamly,colname,Family", 3, 20, 1 ); connectionSpy.addColumnFilterToScan( cf, meta, space, true ); FilterList filter = (FilterList) connectionSpy.m_sourceScan.getFilter(); assertFalse( filter.getFilters().isEmpty() ); assertEquals( filter.getFilters().size(), 1 ); }
@Test public void testAddColumnFilterToScanBoolean() throws Exception { ColumnFilter cf = new ColumnFilter( "Family" ); cf.setComparisonOperator( ColumnFilter.ComparisonType.LESS_THAN ); cf.setConstant( "true" ); cf.setSignedComparison( true ); VariableSpace space = CommonHBaseConnectionTest.mockVariableSpace(); connectionSpy.m_sourceScan = new Scan(); ByteArrayComparable comparator = mock( ByteArrayComparable.class ); doReturn( comparator ).when( connectionSpy ).getBooleanComparator( anyBoolean() ); HBaseValueMeta meta = new HBaseValueMeta( "colFamly,colname,Family", 4, 20, 1 ); connectionSpy.addColumnFilterToScan( cf, meta, space, true ); FilterList filter = (FilterList) connectionSpy.m_sourceScan.getFilter(); assertFalse( filter.getFilters().isEmpty() ); assertEquals( filter.getFilters().size(), 1 ); }
@Test public void testAddColumnFilterToScanNumberSigned() throws Exception { ColumnFilter cf = new ColumnFilter( "Family" ); cf.setComparisonOperator( ColumnFilter.ComparisonType.LESS_THAN ); cf.setConstant( "123" ); cf.setSignedComparison( true ); VariableSpace space = CommonHBaseConnectionTest.mockVariableSpace(); connectionSpy.m_sourceScan = new Scan(); HBaseValueMeta meta = new HBaseValueMeta( "colFamly,colname,Family", 1, 20, 1 ); meta.setIsLongOrDouble( true ); ByteArrayComparable comparator = mock( ByteArrayComparable.class ); doReturn( comparator ).when( connectionSpy ).getNumericComparator( eq( cf ), eq( meta ), eq( space ), anyString() ); connectionSpy.addColumnFilterToScan( cf, meta, space, true ); FilterList filter = (FilterList) connectionSpy.m_sourceScan.getFilter(); assertFalse( filter.getFilters().isEmpty() ); assertEquals( filter.getFilters().size(), 1 ); }
/** * Convert a protocol buffer Comparator to a ByteArrayComparable * * @param proto the protocol buffer Comparator to convert * @return the converted ByteArrayComparable */ @SuppressWarnings("unchecked") public static ByteArrayComparable toComparator(ComparatorProtos.Comparator proto) throws IOException { String type = proto.getName(); String funcName = "parseFrom"; byte [] value = proto.getSerializedComparator().toByteArray(); try { Class<? extends ByteArrayComparable> c = (Class<? extends ByteArrayComparable>)(Class.forName(type)); Method parseFrom = c.getMethod(funcName, byte[].class); if (parseFrom == null) { throw new IOException("Unable to locate function: " + funcName + " in type: " + type); } return (ByteArrayComparable)parseFrom.invoke(null, value); } catch (Exception e) { throw new IOException(e); } }
@Override public boolean preCheckAndPut(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 { User user = getActiveUser(); checkForReservedTagPresence(user, put); // Require READ and WRITE permissions on the table, CF, and KV to update RegionCoprocessorEnvironment env = c.getEnvironment(); Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier); AuthResult authResult = permissionGranted(OpType.CHECK_AND_PUT, user, env, families, Action.READ, Action.WRITE); logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { put.setAttribute(CHECK_COVERING_PERM, TRUE); } else if (authorizationEnabled) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } } byte[] bytes = put.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL); if (bytes != null) { if (cellFeaturesEnabled) { addCellPermissions(bytes, put.getFamilyCellMap()); } else { throw new DoNotRetryIOException("Cell ACLs cannot be persisted"); } } return result; }
@Override public boolean preCheckAndDelete(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 { // An ACL on a delete is useless, we shouldn't allow it if (delete.getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL) != null) { throw new DoNotRetryIOException("ACL on checkAndDelete has no effect: " + delete.toString()); } // Require READ and WRITE permissions on the table, CF, and the KV covered // by the delete RegionCoprocessorEnvironment env = c.getEnvironment(); Map<byte[],? extends Collection<byte[]>> families = makeFamilyMap(family, qualifier); User user = getActiveUser(); AuthResult authResult = permissionGranted(OpType.CHECK_AND_DELETE, user, env, families, Action.READ, Action.WRITE); logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { delete.setAttribute(CHECK_COVERING_PERM, TRUE); } else if (authorizationEnabled) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } } return result; }