org.apache.sentry.binding.hive.authz.SentryConfigTool.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sentry.binding.hive.authz.SentryConfigTool.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.sentry.binding.hive.authz;

import java.security.CodeSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.Parser;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.ql.Driver;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.processors.CommandProcessorResponse;
import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.sentry.Command;
import org.apache.sentry.binding.hive.HiveAuthzBindingHook;
import org.apache.sentry.binding.hive.HiveAuthzBindingSessionHook;
import org.apache.sentry.binding.hive.SentryPolicyFileFormatFactory;
import org.apache.sentry.binding.hive.SentryPolicyFileFormatter;
import org.apache.sentry.binding.hive.conf.HiveAuthzConf;
import org.apache.sentry.binding.hive.conf.HiveAuthzConf.AuthzConfVars;
import org.apache.sentry.core.common.SentryConfigurationException;
import org.apache.sentry.core.common.Subject;
import org.apache.sentry.core.model.db.Server;
import org.apache.sentry.provider.common.AuthorizationProvider;
import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient;
import org.apache.sentry.service.thrift.SentryServiceClientFactory;

/**
 * set the required system property to be read by HiveConf and AuthzConf
 * 
 * @throws Exception
 */
// Hack, hiveConf doesn't provide a reliable way check if it found a valid
// hive-site
// load auth provider
// get the configured sentry provider
// validate policy files
// import policy files
public class SentryConfigTool {
    private String sentrySiteFile = null;
    private String policyFile = null;
    private String query = null;
    private String jdbcURL = null;
    private String user = null;
    private String passWord = null;
    private String importPolicyFilePath = null;
    private String exportPolicyFilePath = null;
    private boolean listPrivs = false;
    private boolean validate = false;
    private boolean importOverwriteRole = false;
    private HiveConf hiveConf = null;
    private HiveAuthzConf authzConf = null;
    private AuthorizationProvider sentryProvider = null;

    public SentryConfigTool() {

    }

    public AuthorizationProvider getSentryProvider() {
        return sentryProvider;
    }

    public void setSentryProvider(AuthorizationProvider sentryProvider) {
        this.sentryProvider = sentryProvider;
    }

    public HiveConf getHiveConf() {
        return hiveConf;
    }

    public void setHiveConf(HiveConf hiveConf) {
        this.hiveConf = hiveConf;
    }

    public HiveAuthzConf getAuthzConf() {
        return authzConf;
    }

    public void setAuthzConf(HiveAuthzConf authzConf) {
        this.authzConf = authzConf;
    }

    public boolean isValidate() {
        return validate;
    }

    public void setValidate(boolean validate) {
        this.validate = validate;
    }

    public String getImportPolicyFilePath() {
        return importPolicyFilePath;
    }

    public void setImportPolicyFilePath(String importPolicyFilePath) {
        this.importPolicyFilePath = importPolicyFilePath;
    }

    public String getExportPolicyFilePath() {
        return exportPolicyFilePath;
    }

    public void setExportPolicyFilePath(String exportPolicyFilePath) {
        this.exportPolicyFilePath = exportPolicyFilePath;
    }

    public String getSentrySiteFile() {
        return sentrySiteFile;
    }

    public void setSentrySiteFile(String sentrySiteFile) {
        this.sentrySiteFile = sentrySiteFile;
    }

    public String getPolicyFile() {
        return policyFile;
    }

    public void setPolicyFile(String policyFile) {
        this.policyFile = policyFile;
    }

    public String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    public String getJdbcURL() {
        return jdbcURL;
    }

    public void setJdbcURL(String jdbcURL) {
        this.jdbcURL = jdbcURL;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public boolean isListPrivs() {
        return listPrivs;
    }

    public void setListPrivs(boolean listPrivs) {
        this.listPrivs = listPrivs;
    }

    public boolean isImportOverwriteRole() {
        return importOverwriteRole;
    }

    public void setImportOverwriteRole(boolean importOverwriteRole) {
        this.importOverwriteRole = importOverwriteRole;
    }

    /**
     * set the required system property to be read by HiveConf and AuthzConf
     * @throws Exception
     */
    public void setupConfig() throws Exception {
        System.out.println("Configuration: ");
        CodeSource src = SentryConfigTool.class.getProtectionDomain().getCodeSource();
        if (src != null) {
            System.out.println("Sentry package jar: " + src.getLocation());
        }

        if (getPolicyFile() != null) {
            System.setProperty(AuthzConfVars.AUTHZ_PROVIDER_RESOURCE.getVar(), getPolicyFile());
        }
        System.setProperty(AuthzConfVars.SENTRY_TESTING_MODE.getVar(), "true");
        setHiveConf(new HiveConf(SessionState.class));
        getHiveConf().setVar(ConfVars.SEMANTIC_ANALYZER_HOOK, HiveAuthzBindingHook.class.getName());
        try {
            System.out.println("Hive config: " + getHiveConf().getHiveSiteLocation());
        } catch (NullPointerException e) {
            // Hack, hiveConf doesn't provide a reliable way check if it found a valid
            // hive-site
            throw new SentryConfigurationException("Didn't find a hive-site.xml");

        }

        if (getSentrySiteFile() != null) {
            getHiveConf().set(HiveAuthzConf.HIVE_SENTRY_CONF_URL, getSentrySiteFile());
        }

        setAuthzConf(HiveAuthzConf.getAuthzConf(getHiveConf()));
        System.out.println("Sentry config: " + getAuthzConf().getHiveAuthzSiteFile());
        System.out.println("Sentry Policy: " + getAuthzConf().get(AuthzConfVars.AUTHZ_PROVIDER_RESOURCE.getVar()));
        System.out.println("Sentry server: " + getAuthzConf().get(AuthzConfVars.AUTHZ_SERVER_NAME.getVar()));

        setSentryProvider(getAuthorizationProvider());
    }

    // load auth provider
    private AuthorizationProvider getAuthorizationProvider()
            throws IllegalStateException, SentryConfigurationException {
        String serverName = new Server(getAuthzConf().get(AuthzConfVars.AUTHZ_SERVER_NAME.getVar())).getName();
        // get the configured sentry provider
        AuthorizationProvider sentryProvider = null;
        try {
            sentryProvider = HiveAuthzBinding.getAuthProvider(getHiveConf(), authzConf, serverName);
        } catch (SentryConfigurationException eC) {
            printConfigErrors(eC);
        } catch (Exception e) {
            throw new IllegalStateException("Couldn't load sentry provider ", e);
        }
        return sentryProvider;
    }

    // validate policy files
    public void validatePolicy() throws Exception {
        try {
            getSentryProvider().validateResource(true);
        } catch (SentryConfigurationException e) {
            printConfigErrors(e);
        }
        System.out.println("No errors found in the policy file");
    }

    // import the sentry mapping data to database
    public void importPolicy() throws Exception {
        String requestorUserName = System.getProperty("user.name", "");
        // get the FileFormatter according to the configuration
        SentryPolicyFileFormatter sentryPolicyFileFormatter = SentryPolicyFileFormatFactory
                .createFileFormatter(authzConf);
        // parse the input file, get the mapping data in map structure
        Map<String, Map<String, Set<String>>> policyFileMappingData = sentryPolicyFileFormatter
                .parse(importPolicyFilePath, authzConf);
        // todo: here should be an validator to check the data's value, format, hierarchy
        SentryPolicyServiceClient client = SentryServiceClientFactory.create(getAuthzConf());
        // import the mapping data to database
        client.importPolicy(policyFileMappingData, requestorUserName, importOverwriteRole);
    }

    // export the sentry mapping data to file
    public void exportPolicy() throws Exception {
        String requestorUserName = System.getProperty("user.name", "");
        SentryPolicyServiceClient client = SentryServiceClientFactory.create(getAuthzConf());
        // export the sentry mapping data from database to map structure
        Map<String, Map<String, Set<String>>> policyFileMappingData = client.exportPolicy(requestorUserName);
        // get the FileFormatter according to the configuration
        SentryPolicyFileFormatter sentryPolicyFileFormatter = SentryPolicyFileFormatFactory
                .createFileFormatter(authzConf);
        // write the sentry mapping data to exportPolicyFilePath with the data in map structure
        sentryPolicyFileFormatter.write(exportPolicyFilePath, policyFileMappingData);
    }

    // list permissions for given user
    public void listPrivs() throws Exception {
        getSentryProvider().validateResource(true);
        System.out.println("Available privileges for user " + getUser() + ":");
        Set<String> permList = getSentryProvider().listPrivilegesForSubject(new Subject(getUser()));
        for (String perms : permList) {
            System.out.println("\t" + perms);
        }
        if (permList.isEmpty()) {
            System.out.println("\t*** No permissions available ***");
        }
    }

    // Verify the given query
    public void verifyLocalQuery(String queryStr) throws Exception {
        // setup Hive driver
        SessionState session = new SessionState(getHiveConf());
        SessionState.start(session);
        Driver driver = new Driver(session.getConf(), getUser());

        // compile the query
        CommandProcessorResponse compilerStatus = driver.compileAndRespond(queryStr);
        if (compilerStatus.getResponseCode() != 0) {
            String errMsg = compilerStatus.getErrorMessage();
            if (errMsg.contains(HiveAuthzConf.HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE)) {
                printMissingPerms(getHiveConf().get(HiveAuthzConf.HIVE_SENTRY_AUTH_ERRORS));
            }
            throw new SemanticException("Compilation error: " + compilerStatus.getErrorMessage());
        }
        driver.close();
        System.out.println("User " + getUser() + " has privileges to run the query");
    }

    // connect to remote HS2 and run mock query
    public void verifyRemoteQuery(String queryStr) throws Exception {
        Class.forName("org.apache.hive.jdbc.HiveDriver");
        Connection conn = DriverManager.getConnection(getJdbcURL(), getUser(), getPassWord());
        Statement stmt = conn.createStatement();
        if (!isSentryEnabledOnHiveServer(stmt)) {
            throw new IllegalStateException("Sentry is not enabled on HiveServer2");
        }
        stmt.execute("set " + HiveAuthzConf.HIVE_SENTRY_MOCK_COMPILATION + "=true");
        try {
            stmt.execute(queryStr);
        } catch (SQLException e) {
            String errMsg = e.getMessage();
            if (errMsg.contains(HiveAuthzConf.HIVE_SENTRY_MOCK_ERROR)) {
                System.out.println("User " + readConfig(stmt, HiveAuthzConf.HIVE_SENTRY_SUBJECT_NAME)
                        + " has privileges to run the query");
                return;
            } else if (errMsg.contains(HiveAuthzConf.HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE)) {
                printMissingPerms(readConfig(stmt, HiveAuthzConf.HIVE_SENTRY_AUTH_ERRORS));
                throw e;
            } else {
                throw e;
            }
        } finally {
            if (!stmt.isClosed()) {
                stmt.close();
            }
            conn.close();
        }

    }

    // verify senty session hook is set
    private boolean isSentryEnabledOnHiveServer(Statement stmt) throws SQLException {
        return HiveAuthzBindingSessionHook.class.getName()
                .equalsIgnoreCase(readConfig(stmt, HiveConf.ConfVars.HIVE_SERVER2_SESSION_HOOK.varname));
    }

    // read a config value using 'set' statement
    private String readConfig(Statement stmt, String configKey) throws SQLException {
        ResultSet res = stmt.executeQuery("set " + configKey);
        if (!res.next()) {
            return null;
        }
        // parse key=value result format
        String result = res.getString(1);
        res.close();
        return result.substring(result.indexOf("=") + 1);
    }

    // print configuration/policy file errors and warnings
    private void printConfigErrors(SentryConfigurationException configException)
            throws SentryConfigurationException {
        System.out.println(" *** Found configuration problems *** ");
        for (String errMsg : configException.getConfigErrors()) {
            System.out.println("ERROR: " + errMsg);
        }
        for (String warnMsg : configException.getConfigWarnings()) {
            System.out.println("Warning: " + warnMsg);
        }
        throw configException;
    }

    // extract the authorization errors from config property and print
    private void printMissingPerms(String errMsg) {
        if (errMsg == null || errMsg.isEmpty()) {
            return;
        }
        System.out.println("*** Query compilation failed ***");
        String perms[] = errMsg.replaceFirst(".*" + HiveAuthzConf.HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE, "")
                .split(";");
        System.out.println("Required privileges for given query:");
        for (int count = 0; count < perms.length; count++) {
            System.out.println(" \t " + perms[count]);
        }
    }

    // print usage
    private void usage(Options sentryOptions) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("sentry --command config-tool", sentryOptions);
        System.exit(-1);
    }

    /**
     * parse arguments
     * 
     * <pre>
     *   -d,--debug                  Enable debug output
     *   -e,--query <arg>            Query privilege verification, requires -u
     *   -h,--help                   Print usage
     *   -i,--policyIni <arg>        Policy file path
     *   -j,--jdbcURL <arg>          JDBC URL
     *   -l,--listPrivs,--listPerms  List privilges for given user, requires -u
     *   -p,--password <arg>         Password
     *   -s,--sentry-site <arg>      sentry-site file path
     *   -u,--user <arg>             user name
     *   -v,--validate               Validate policy file
     *   -I,--import                 Import policy file
     *   -E,--export                 Export policy file
     *   -o,--overwrite              Overwrite the exist role data when do the import
     * </pre>
     * 
     * @param args
     */
    private void parseArgs(String[] args) {
        boolean enableDebug = false;

        Options sentryOptions = new Options();

        Option helpOpt = new Option("h", "help", false, "Print usage");
        helpOpt.setRequired(false);

        Option validateOpt = new Option("v", "validate", false, "Validate policy file");
        validateOpt.setRequired(false);

        Option queryOpt = new Option("e", "query", true, "Query privilege verification, requires -u");
        queryOpt.setRequired(false);

        Option listPermsOpt = new Option("l", "listPerms", false, "list permissions for given user, requires -u");
        listPermsOpt.setRequired(false);
        Option listPrivsOpt = new Option("listPrivs", false, "list privileges for given user, requires -u");
        listPrivsOpt.setRequired(false);

        Option importOpt = new Option("I", "import", true, "Import policy file");
        importOpt.setRequired(false);

        Option exportOpt = new Option("E", "export", true, "Export policy file");
        exportOpt.setRequired(false);
        // required args
        OptionGroup sentryOptGroup = new OptionGroup();
        sentryOptGroup.addOption(helpOpt);
        sentryOptGroup.addOption(validateOpt);
        sentryOptGroup.addOption(queryOpt);
        sentryOptGroup.addOption(listPermsOpt);
        sentryOptGroup.addOption(listPrivsOpt);
        sentryOptGroup.addOption(importOpt);
        sentryOptGroup.addOption(exportOpt);
        sentryOptGroup.setRequired(true);
        sentryOptions.addOptionGroup(sentryOptGroup);

        // optional args
        Option jdbcArg = new Option("j", "jdbcURL", true, "JDBC URL");
        jdbcArg.setRequired(false);
        sentryOptions.addOption(jdbcArg);

        Option sentrySitePath = new Option("s", "sentry-site", true, "sentry-site file path");
        sentrySitePath.setRequired(false);
        sentryOptions.addOption(sentrySitePath);

        Option globalPolicyPath = new Option("i", "policyIni", true, "Policy file path");
        globalPolicyPath.setRequired(false);
        sentryOptions.addOption(globalPolicyPath);

        Option userOpt = new Option("u", "user", true, "user name");
        userOpt.setRequired(false);
        sentryOptions.addOption(userOpt);

        Option passWordOpt = new Option("p", "password", true, "Password");
        userOpt.setRequired(false);
        sentryOptions.addOption(passWordOpt);

        Option debugOpt = new Option("d", "debug", false, "enable debug output");
        debugOpt.setRequired(false);
        sentryOptions.addOption(debugOpt);

        Option overwriteOpt = new Option("o", "overwrite", false, "enable import overwrite");
        overwriteOpt.setRequired(false);
        sentryOptions.addOption(overwriteOpt);

        try {
            Parser parser = new GnuParser();
            CommandLine cmd = parser.parse(sentryOptions, args);

            for (Option opt : cmd.getOptions()) {
                if (opt.getOpt().equals("s")) {
                    setSentrySiteFile(opt.getValue());
                } else if (opt.getOpt().equals("i")) {
                    setPolicyFile(opt.getValue());
                } else if (opt.getOpt().equals("e")) {
                    setQuery(opt.getValue());
                } else if (opt.getOpt().equals("j")) {
                    setJdbcURL(opt.getValue());
                } else if (opt.getOpt().equals("u")) {
                    setUser(opt.getValue());
                } else if (opt.getOpt().equals("p")) {
                    setPassWord(opt.getValue());
                } else if (opt.getOpt().equals("l") || opt.getOpt().equals("listPrivs")) {
                    setListPrivs(true);
                } else if (opt.getOpt().equals("v")) {
                    setValidate(true);
                } else if (opt.getOpt().equals("I")) {
                    setImportPolicyFilePath(opt.getValue());
                } else if (opt.getOpt().equals("E")) {
                    setExportPolicyFilePath(opt.getValue());
                } else if (opt.getOpt().equals("h")) {
                    usage(sentryOptions);
                } else if (opt.getOpt().equals("d")) {
                    enableDebug = true;
                } else if (opt.getOpt().equals("o")) {
                    setImportOverwriteRole(true);
                }
            }

            if (isListPrivs() && (getUser() == null)) {
                throw new ParseException("Can't use -l without -u ");
            }
            if ((getQuery() != null) && (getUser() == null)) {
                throw new ParseException("Must use -u with -e ");
            }
        } catch (ParseException e1) {
            usage(sentryOptions);
        }

        if (!enableDebug) {
            // turn off log
            LogManager.getRootLogger().setLevel(Level.OFF);
        }
    }

    public static class CommandImpl implements Command {
        @Override
        public void run(String[] args) throws Exception {
            SentryConfigTool sentryTool = new SentryConfigTool();

            try {
                // parse arguments
                sentryTool.parseArgs(args);

                // load configuration
                sentryTool.setupConfig();

                // validate configuration
                if (sentryTool.isValidate()) {
                    sentryTool.validatePolicy();
                }

                if (!StringUtils.isEmpty(sentryTool.getImportPolicyFilePath())) {
                    sentryTool.importPolicy();
                }

                if (!StringUtils.isEmpty(sentryTool.getExportPolicyFilePath())) {
                    sentryTool.exportPolicy();
                }

                // list permissions for give user
                if (sentryTool.isListPrivs()) {
                    sentryTool.listPrivs();
                }

                // verify given query
                if (sentryTool.getQuery() != null) {
                    if (sentryTool.getJdbcURL() != null) {
                        sentryTool.verifyRemoteQuery(sentryTool.getQuery());
                    } else {
                        sentryTool.verifyLocalQuery(sentryTool.getQuery());
                    }
                }
            } catch (Exception e) {
                System.out.println("Sentry tool reported Errors: " + e.getMessage());
                e.printStackTrace(System.out);
                System.exit(1);
            }
        }
    }
}