| /* |
| * 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.HiveAuthzBindingHookBase; |
| 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.exception.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 String objectPath = 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 getObjectPath() { |
| return objectPath; |
| } |
| |
| public void setObjectPath(String objectPath) { |
| this.objectPath = objectPath; |
| } |
| |
| 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, |
| HiveAuthzBindingHookBase.class.getName()); |
| try { |
| System.out.println("Hive config: " + HiveConf.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 |
| try { |
| return HiveAuthzBinding.getAuthProvider(getHiveConf(), |
| authzConf, serverName); |
| } catch (SentryConfigurationException eC) { |
| printConfigErrors(eC); |
| throw eC; |
| } catch (Exception e) { |
| throw new IllegalStateException("Couldn't load sentry provider ", e); |
| } |
| } |
| |
| // validate policy files |
| public void validatePolicy() throws Exception { |
| try { |
| getSentryProvider().validateResource(true); |
| } catch (SentryConfigurationException e) { |
| printConfigErrors(e); |
| throw 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 |
| try(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", ""); |
| try (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, objectPath); |
| // 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 { |
| String bindingString = readConfig(stmt, HiveConf.ConfVars.HIVE_SERVER2_SESSION_HOOK.varname).toUpperCase(); |
| return bindingString.contains("org.apache.sentry.binding.hive".toUpperCase()) |
| && bindingString.contains("HiveAuthzBindingSessionHook".toUpperCase()); |
| } |
| |
| // read a config value using 'set' statement |
| private String readConfig(Statement stmt, String configKey) |
| throws SQLException { |
| try (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); |
| } |
| } |
| |
| // 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 |
| * -b,--objectPath The path of the object whose privileges will be exported |
| * </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); |
| |
| Option objectPathOpt = new Option("b", "objectPath", |
| false, "The path of the object whose privileges will be exported"); |
| objectPathOpt.setRequired(false); |
| sentryOptions.addOption(objectPathOpt); |
| |
| 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); |
| } else if (opt.getOpt().equals("b")) { |
| setObjectPath(opt.getValue()); |
| } |
| } |
| |
| 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); |
| } |
| } |
| } |
| } |