@Test
  public void testPostGrantRevokeAtQualifierLevel() throws Exception {
    final TableName tableName = TableName.valueOf(name.getMethodName());
    final byte[] family1 = Bytes.toBytes("f1");
    final byte[] family2 = Bytes.toBytes("f2");
    final byte[] qualifier = Bytes.toBytes("q");

    // create table
    Admin admin = TEST_UTIL.getAdmin();
    if (admin.tableExists(tableName)) {
      deleteTable(TEST_UTIL, tableName);
    }
    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family1))
      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family2)).build();
    createTable(TEST_UTIL, tableDescriptor);

    try {
      // create temp users
      User user = User.createUserForTesting(TEST_UTIL.getConfiguration(), "user", new String[0]);

      AccessTestAction getQualifierAction = new AccessTestAction() {
        @Override
        public Object run() throws Exception {
          Get g = new Get(TEST_ROW);
          g.addColumn(family1, qualifier);
          try (Connection conn = ConnectionFactory.createConnection(conf);
            Table t = conn.getTable(tableName)) {
            t.get(g);
          }
          return null;
        }
      };

      AccessTestAction putQualifierAction = new AccessTestAction() {
        @Override
        public Object run() throws Exception {
          Put p = new Put(TEST_ROW);
          p.addColumn(family1, qualifier, Bytes.toBytes("v1"));
          try (Connection conn = ConnectionFactory.createConnection(conf);
            Table t = conn.getTable(tableName)) {
            t.put(p);
          }
          return null;
        }
      };

      AccessTestAction deleteQualifierAction = new AccessTestAction() {
        @Override
        public Object run() throws Exception {
          Delete d = new Delete(TEST_ROW);
          d.addColumn(family1, qualifier);
          // d.deleteFamily(family1);
          try (Connection conn = ConnectionFactory.createConnection(conf);
            Table t = conn.getTable(tableName)) {
            t.delete(d);
          }
          return null;
        }
      };

      revokeFromTable(TEST_UTIL, user.getShortName(), tableName, family1, null);

      verifyDenied(user, getQualifierAction);
      verifyDenied(user, putQualifierAction);
      verifyDenied(user, deleteQualifierAction);

      grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier,
        Permission.Action.READ);

      verifyAllowed(user, getQualifierAction);
      verifyDenied(user, putQualifierAction);
      verifyDenied(user, deleteQualifierAction);

      // only grant write permission
      // TODO: comment this portion after HBASE-3583
      grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier,
        Permission.Action.WRITE);

      verifyDenied(user, getQualifierAction);
      verifyAllowed(user, putQualifierAction);
      verifyAllowed(user, deleteQualifierAction);

      // grant both read and write permission
      grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier,
        Permission.Action.READ, Permission.Action.WRITE);

      verifyAllowed(user, getQualifierAction);
      verifyAllowed(user, putQualifierAction);
      verifyAllowed(user, deleteQualifierAction);

      // revoke family level permission won't impact column level
      revokeFromTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier);

      verifyDenied(user, getQualifierAction);
      verifyDenied(user, putQualifierAction);
      verifyDenied(user, deleteQualifierAction);
    } finally {
      // delete table
      deleteTable(TEST_UTIL, tableName);
    }
  }
The systemUserConnection created here is tied to the system user. In case, you are planning to
create AccessTestAction, DON'T use this systemUserConnection as the 'doAs' user gets eclipsed
by the system user.
hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java