public static List<String> filterShowDatabases(
      HiveAuthzBinding hiveAuthzBinding,
      List<String> queryResult,
      HiveOperation operation,
      String userName)
      throws SemanticException {
    List<String> filteredResult = new ArrayList<String>();
    Subject subject = new Subject(userName);
    HiveAuthzBinding hiveBindingWithPrivilegeCache =
        getHiveBindingWithPrivilegeCache(hiveAuthzBinding, userName);

    HiveAuthzPrivileges anyPrivilege =
        new HiveAuthzPrivileges.AuthzPrivilegeBuilder()
            .addInputObjectPriviledge(
                AuthorizableType.Column, EnumSet.of(DBModelAction.SELECT, DBModelAction.INSERT))
            .addInputObjectPriviledge(AuthorizableType.URI, EnumSet.of(DBModelAction.SELECT))
            .setOperationScope(HiveOperationScope.CONNECT)
            .setOperationType(HiveOperationType.QUERY)
            .build();

    for (String dbName : queryResult) {
      // if user has privileges on database, add to filtered list, else discard
      Database database = null;

      // if default is not restricted, continue
      if (DEFAULT_DATABASE_NAME.equalsIgnoreCase(dbName)
          && "false"
              .equalsIgnoreCase(
                  hiveAuthzBinding
                      .getAuthzConf()
                      .get(
                          HiveAuthzConf.AuthzConfVars.AUTHZ_RESTRICT_DEFAULT_DB.getVar(),
                          "false"))) {
        filteredResult.add(DEFAULT_DATABASE_NAME);
        continue;
      }

      database = new Database(dbName);

      List<List<DBModelAuthorizable>> inputHierarchy = new ArrayList<List<DBModelAuthorizable>>();
      List<List<DBModelAuthorizable>> outputHierarchy = new ArrayList<List<DBModelAuthorizable>>();
      List<DBModelAuthorizable> externalAuthorizableHierarchy =
          new ArrayList<DBModelAuthorizable>();
      externalAuthorizableHierarchy.add(hiveAuthzBinding.getAuthServer());
      externalAuthorizableHierarchy.add(database);
      externalAuthorizableHierarchy.add(Table.ALL);
      externalAuthorizableHierarchy.add(Column.ALL);
      inputHierarchy.add(externalAuthorizableHierarchy);

      try {
        // do the authorization by new HiveAuthzBinding with PrivilegeCache
        hiveBindingWithPrivilegeCache.authorize(
            operation, anyPrivilege, subject, inputHierarchy, outputHierarchy);
        filteredResult.add(database.getName());
      } catch (AuthorizationException e) {
        // squash the exception, user doesn't have privileges, so the table is
        // not added to
        // filtered list.
      }
    }

    return filteredResult;
  }
  private List<HivePrivilegeObject> filterShowDatabases(
      List<HivePrivilegeObject> listObjs, String userName, HiveAuthzBinding hiveAuthzBinding) {
    List<HivePrivilegeObject> filteredResult = new ArrayList<HivePrivilegeObject>();
    Subject subject = new Subject(userName);
    HiveAuthzPrivileges anyPrivilege =
        new HiveAuthzPrivileges.AuthzPrivilegeBuilder()
            .addInputObjectPriviledge(
                AuthorizableType.Column,
                EnumSet.of(
                    DBModelAction.SELECT,
                    DBModelAction.INSERT,
                    DBModelAction.ALTER,
                    DBModelAction.CREATE,
                    DBModelAction.DROP,
                    DBModelAction.INDEX,
                    DBModelAction.LOCK))
            .setOperationScope(HiveOperationScope.CONNECT)
            .setOperationType(
                org.apache.sentry.binding.hive.authz.HiveAuthzPrivileges.HiveOperationType.QUERY)
            .build();

    for (HivePrivilegeObject obj : listObjs) {
      // if user has privileges on database, add to filtered list, else discard
      Database database = null;

      // if default is not restricted, continue
      if (DEFAULT_DATABASE_NAME.equalsIgnoreCase(obj.getObjectName())
          && "false"
              .equalsIgnoreCase(
                  hiveAuthzBinding
                      .getAuthzConf()
                      .get(
                          HiveAuthzConf.AuthzConfVars.AUTHZ_RESTRICT_DEFAULT_DB.getVar(),
                          "false"))) {
        filteredResult.add(obj);
        continue;
      }

      database = new Database(obj.getObjectName());

      List<List<DBModelAuthorizable>> inputHierarchy = new ArrayList<List<DBModelAuthorizable>>();
      List<List<DBModelAuthorizable>> outputHierarchy = new ArrayList<List<DBModelAuthorizable>>();
      List<DBModelAuthorizable> externalAuthorizableHierarchy =
          new ArrayList<DBModelAuthorizable>();
      externalAuthorizableHierarchy.add(hiveAuthzBinding.getAuthServer());
      externalAuthorizableHierarchy.add(database);
      externalAuthorizableHierarchy.add(Table.ALL);
      externalAuthorizableHierarchy.add(Column.ALL);
      inputHierarchy.add(externalAuthorizableHierarchy);

      try {
        hiveAuthzBinding.authorize(
            HiveOperation.SHOWDATABASES, anyPrivilege, subject, inputHierarchy, outputHierarchy);
        filteredResult.add(obj);
      } catch (AuthorizationException e) {
        // squash the exception, user doesn't have privileges, so the table is
        // not added to
        // filtered list.
      }
    }
    return filteredResult;
  }
  /**
   * Convert the input/output entities into authorizables. generate authorizables for cases like
   * Database and metadata operations where the compiler doesn't capture entities. invoke the hive
   * binding to validate permissions
   *
   * @param context
   * @param stmtAuthObject
   * @param stmtOperation
   * @throws AuthorizationException
   */
  private void authorizeWithHiveBindings(
      HiveSemanticAnalyzerHookContext context,
      HiveAuthzPrivileges stmtAuthObject,
      HiveOperation stmtOperation)
      throws AuthorizationException {
    Set<ReadEntity> inputs = context.getInputs();
    Set<WriteEntity> outputs = context.getOutputs();
    List<List<DBModelAuthorizable>> inputHierarchy = new ArrayList<List<DBModelAuthorizable>>();
    List<List<DBModelAuthorizable>> outputHierarchy = new ArrayList<List<DBModelAuthorizable>>();

    if (LOG.isDebugEnabled()) {
      LOG.debug("stmtAuthObject.getOperationScope() = " + stmtAuthObject.getOperationScope());
      LOG.debug("context.getInputs() = " + context.getInputs());
      LOG.debug("context.getOutputs() = " + context.getOutputs());
    }

    // Workaround to allow DESCRIBE <table> to be executed with only column-level privileges, while
    // still authorizing DESCRIBE [EXTENDED|FORMATTED] as table-level.
    // This is done by treating DESCRIBE <table> the same as SHOW COLUMNS, which only requires
    // column
    // level privs.
    if (isDescTableBasic) {
      stmtAuthObject = HiveAuthzPrivilegesMap.getHiveAuthzPrivileges(HiveOperation.SHOWCOLUMNS);
    }

    switch (stmtAuthObject.getOperationScope()) {
      case SERVER:
        // validate server level privileges if applicable. Eg create UDF,register jar etc ..
        List<DBModelAuthorizable> serverHierarchy = new ArrayList<DBModelAuthorizable>();
        serverHierarchy.add(hiveAuthzBinding.getAuthServer());
        inputHierarchy.add(serverHierarchy);
        break;
      case DATABASE:
        // workaround for database scope statements (create/alter/drop db)
        List<DBModelAuthorizable> dbHierarchy = new ArrayList<DBModelAuthorizable>();
        dbHierarchy.add(hiveAuthzBinding.getAuthServer());
        dbHierarchy.add(currDB);
        inputHierarchy.add(dbHierarchy);
        outputHierarchy.add(dbHierarchy);

        getInputHierarchyFromInputs(inputHierarchy, inputs);
        break;
      case TABLE:
        // workaround for add partitions
        if (partitionURI != null) {
          inputHierarchy.add(ImmutableList.of(hiveAuthzBinding.getAuthServer(), partitionURI));
        }

        getInputHierarchyFromInputs(inputHierarchy, inputs);
        for (WriteEntity writeEntity : outputs) {
          if (filterWriteEntity(writeEntity)) {
            continue;
          }
          List<DBModelAuthorizable> entityHierarchy = new ArrayList<DBModelAuthorizable>();
          entityHierarchy.add(hiveAuthzBinding.getAuthServer());
          entityHierarchy.addAll(getAuthzHierarchyFromEntity(writeEntity));
          outputHierarchy.add(entityHierarchy);
        }
        // workaround for metadata queries.
        // Capture the table name in pre-analyze and include that in the input entity list
        if (currTab != null) {
          List<DBModelAuthorizable> externalAuthorizableHierarchy =
              new ArrayList<DBModelAuthorizable>();
          externalAuthorizableHierarchy.add(hiveAuthzBinding.getAuthServer());
          externalAuthorizableHierarchy.add(currDB);
          externalAuthorizableHierarchy.add(currTab);
          inputHierarchy.add(externalAuthorizableHierarchy);
        }

        // workaround for DDL statements
        // Capture the table name in pre-analyze and include that in the output entity list
        if (currOutTab != null) {
          List<DBModelAuthorizable> externalAuthorizableHierarchy =
              new ArrayList<DBModelAuthorizable>();
          externalAuthorizableHierarchy.add(hiveAuthzBinding.getAuthServer());
          externalAuthorizableHierarchy.add(currOutDB);
          externalAuthorizableHierarchy.add(currOutTab);
          outputHierarchy.add(externalAuthorizableHierarchy);
        }
        break;
      case FUNCTION:
        /* The 'FUNCTION' privilege scope currently used for
         *  - CREATE TEMP FUNCTION
         *  - DROP TEMP FUNCTION.
         */
        if (udfURI != null) {
          List<DBModelAuthorizable> udfUriHierarchy = new ArrayList<DBModelAuthorizable>();
          udfUriHierarchy.add(hiveAuthzBinding.getAuthServer());
          udfUriHierarchy.add(udfURI);
          inputHierarchy.add(udfUriHierarchy);
          for (WriteEntity writeEntity : outputs) {
            List<DBModelAuthorizable> entityHierarchy = new ArrayList<DBModelAuthorizable>();
            entityHierarchy.add(hiveAuthzBinding.getAuthServer());
            entityHierarchy.addAll(getAuthzHierarchyFromEntity(writeEntity));
            outputHierarchy.add(entityHierarchy);
          }
        }
        break;
      case CONNECT:
        /* The 'CONNECT' is an implicit privilege scope currently used for
         *  - USE <db>
         *  It's allowed when the user has any privilege on the current database. For application
         *  backward compatibility, we allow (optional) implicit connect permission on 'default' db.
         */
        List<DBModelAuthorizable> connectHierarchy = new ArrayList<DBModelAuthorizable>();
        connectHierarchy.add(hiveAuthzBinding.getAuthServer());
        // by default allow connect access to default db
        Table currTbl = Table.ALL;
        Column currCol = Column.ALL;
        if (DEFAULT_DATABASE_NAME.equalsIgnoreCase(currDB.getName())
            && "false"
                .equalsIgnoreCase(
                    authzConf.get(
                        HiveAuthzConf.AuthzConfVars.AUTHZ_RESTRICT_DEFAULT_DB.getVar(), "false"))) {
          currDB = Database.ALL;
          currTbl = Table.SOME;
        }

        connectHierarchy.add(currDB);
        connectHierarchy.add(currTbl);
        connectHierarchy.add(currCol);

        inputHierarchy.add(connectHierarchy);
        outputHierarchy.add(connectHierarchy);
        break;
      case COLUMN:
        for (ReadEntity readEntity : inputs) {
          if (readEntity.getAccessedColumns() != null
              && !readEntity.getAccessedColumns().isEmpty()) {
            addColumnHierarchy(inputHierarchy, readEntity);
          } else {
            List<DBModelAuthorizable> entityHierarchy = new ArrayList<DBModelAuthorizable>();
            entityHierarchy.add(hiveAuthzBinding.getAuthServer());
            entityHierarchy.addAll(getAuthzHierarchyFromEntity(readEntity));
            entityHierarchy.add(Column.ALL);
            inputHierarchy.add(entityHierarchy);
          }
        }
        break;
      default:
        throw new AuthorizationException(
            "Unknown operation scope type " + stmtAuthObject.getOperationScope().toString());
    }

    HiveAuthzBinding binding = null;
    try {
      binding = getHiveBindingWithPrivilegeCache(hiveAuthzBinding, context.getUserName());
    } catch (SemanticException e) {
      // Will use the original hiveAuthzBinding
      binding = hiveAuthzBinding;
    }
    // validate permission
    binding.authorize(
        stmtOperation, stmtAuthObject, getCurrentSubject(context), inputHierarchy, outputHierarchy);
  }
  private void addExtendHierarchy(
      HiveOperation hiveOp,
      HiveAuthzPrivileges stmtAuthPrivileges,
      List<List<DBModelAuthorizable>> inputHierarchyList,
      List<List<DBModelAuthorizable>> outputHierarchyList,
      String command,
      HiveAuthzBinding hiveAuthzBinding)
      throws HiveAuthzPluginException, HiveAccessControlException {
    String currDatabase = null;
    switch (stmtAuthPrivileges.getOperationScope()) {
      case SERVER:
        // validate server level privileges if applicable. Eg create UDF,register jar etc ..
        List<DBModelAuthorizable> serverHierarchy = new ArrayList<DBModelAuthorizable>();
        serverHierarchy.add(hiveAuthzBinding.getAuthServer());
        inputHierarchyList.add(serverHierarchy);
        break;
      case DATABASE:
        // workaround for metadata queries.
        if (EX_DB_ALL.contains(hiveOp)) {
          SimpleSemanticAnalyzer analyzer = new SimpleSemanticAnalyzer(hiveOp, command);
          currDatabase = analyzer.getCurrentDb();

          List<DBModelAuthorizable> externalAuthorizableHierarchy =
              new ArrayList<DBModelAuthorizable>();
          externalAuthorizableHierarchy.add(hiveAuthzBinding.getAuthServer());
          externalAuthorizableHierarchy.add(new Database(currDatabase));

          if (EX_DB_INPUT.contains(hiveOp)) {
            inputHierarchyList.add(externalAuthorizableHierarchy);
          } else {
            outputHierarchyList.add(externalAuthorizableHierarchy);
          }
        }
        break;
      case TABLE:
      case COLUMN:
        // workaround for drop table/view.
        if (EX_TB_ALL.contains(hiveOp)) {
          SimpleSemanticAnalyzer analyzer = new SimpleSemanticAnalyzer(hiveOp, command);
          currDatabase = analyzer.getCurrentDb();
          String currTable = analyzer.getCurrentTb();

          List<DBModelAuthorizable> externalAuthorizableHierarchy =
              new ArrayList<DBModelAuthorizable>();
          externalAuthorizableHierarchy.add(hiveAuthzBinding.getAuthServer());
          externalAuthorizableHierarchy.add(new Database(currDatabase));
          externalAuthorizableHierarchy.add(new Table(currTable));

          if (EX_TB_INPUT.contains(hiveOp)) {
            inputHierarchyList.add(externalAuthorizableHierarchy);
          } else if (META_TB_INPUT.contains(hiveOp)) {
            externalAuthorizableHierarchy.add(Column.SOME);
            inputHierarchyList.add(externalAuthorizableHierarchy);
          } else {
            outputHierarchyList.add(externalAuthorizableHierarchy);
          }
        }
        break;
      case FUNCTION:
        if (hiveOp.equals(HiveOperation.CREATEFUNCTION)) {
          SimpleSemanticAnalyzer analyzer = new SimpleSemanticAnalyzer(hiveOp, command);
          currDatabase = analyzer.getCurrentDb();
          String udfClassName = analyzer.getCurrentTb();
          try {
            CodeSource udfSrc = Class.forName(udfClassName).getProtectionDomain().getCodeSource();
            if (udfSrc == null) {
              throw new HiveAuthzPluginException(
                  "Could not resolve the jar for UDF class " + udfClassName);
            }
            String udfJar = udfSrc.getLocation().getPath();
            if (udfJar == null || udfJar.isEmpty()) {
              throw new HiveAuthzPluginException(
                  "Could not find the jar for UDF class "
                      + udfClassName
                      + "to validate privileges");
            }
            AccessURI udfURI = SentryAuthorizerUtil.parseURI(udfSrc.getLocation().toString(), true);
            List<DBModelAuthorizable> udfUriHierarchy = new ArrayList<DBModelAuthorizable>();
            udfUriHierarchy.add(hiveAuthzBinding.getAuthServer());
            udfUriHierarchy.add(udfURI);
            inputHierarchyList.add(udfUriHierarchy);
          } catch (Exception e) {
            throw new HiveAuthzPluginException("Error retrieving udf class", e);
          }
        }
        break;
      case CONNECT:
        /*
         * The 'CONNECT' is an implicit privilege scope currently used for - USE <db> It's allowed
         * when the user has any privilege on the current database. For application backward
         * compatibility, we allow (optional) implicit connect permission on 'default' db.
         */
        List<DBModelAuthorizable> connectHierarchy = new ArrayList<DBModelAuthorizable>();
        connectHierarchy.add(hiveAuthzBinding.getAuthServer());
        if (hiveOp.equals(HiveOperation.SWITCHDATABASE)) {
          currDatabase = command.split(" ")[1];
        }
        // by default allow connect access to default db
        Table currTbl = Table.ALL;
        Database currDB = new Database(currDatabase);
        Column currCol = Column.ALL;
        if (DEFAULT_DATABASE_NAME.equalsIgnoreCase(currDatabase)
            && "false"
                .equalsIgnoreCase(
                    authzConf.get(
                        HiveAuthzConf.AuthzConfVars.AUTHZ_RESTRICT_DEFAULT_DB.getVar(), "false"))) {
          currDB = Database.ALL;
          currTbl = Table.SOME;
        }

        connectHierarchy.add(currDB);
        connectHierarchy.add(currTbl);
        connectHierarchy.add(currCol);

        inputHierarchyList.add(connectHierarchy);
        break;
    }
  }