http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentrySchemaTool.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentrySchemaTool.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentrySchemaTool.java new file mode 100644 index 0000000..893f80b --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentrySchemaTool.java @@ -0,0 +1,595 @@ +/** + * 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.cli.tools; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.IllegalFormatException; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.OptionGroup; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.io.output.NullOutputStream; +import org.apache.hadoop.conf.Configuration; +import org.apache.hive.beeline.BeeLine; +import org.apache.sentry.Command; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.core.common.exception.SentrySiteConfigurationException; +import org.apache.sentry.provider.db.service.persistent.SentryStoreSchemaInfo; +import org.apache.sentry.cli.tools.SentrySchemaHelper.NestedScriptParser; +import org.apache.sentry.service.thrift.SentryService; +import org.apache.sentry.service.thrift.ServiceConstants; + +public class SentrySchemaTool { + private static final String SENTRY_SCRIP_DIR = File.separatorChar + "scripts" + + File.separatorChar + "sentrystore" + File.separatorChar + "upgrade"; + private String userName = null; + private String passWord = null; + private String connectionURL = null; + private String driver = null; + private boolean dryRun = false; + private String dbOpts = null; + private boolean verbose = false; + private final Configuration sentryConf; + private final String dbType; + private final SentryStoreSchemaInfo sentryStoreSchemaInfo; + + public SentrySchemaTool(Configuration sentryConf, String dbType) + throws SentryUserException, IOException { + this(System.getenv("SENTRY_HOME") + SENTRY_SCRIP_DIR, sentryConf, dbType); + } + + public SentrySchemaTool(String sentryScripPath, Configuration sentryConf, + String dbType) throws SentryUserException, IOException { + if (sentryScripPath == null || sentryScripPath.isEmpty()) { + throw new SentryUserException("No Sentry script dir provided"); + } + this.sentryConf = sentryConf; + this.dbType = dbType; + this.sentryStoreSchemaInfo = new SentryStoreSchemaInfo(sentryScripPath, + dbType); + userName = sentryConf.get(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_USER, + ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_USER_DEFAULT); + //Password will be read from Credential provider specified using property + // CREDENTIAL_PROVIDER_PATH("hadoop.security.credential.provider.path" in sentry-site.xml + // it falls back to reading directly from sentry-site.xml + char[] passTmp = sentryConf.getPassword(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_PASS); + if(passTmp != null) { + passWord = new String(passTmp); + } else { + throw new SentrySiteConfigurationException("Error reading " + ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_PASS); + } + + try { + connectionURL = getValidConfVar(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_URL); + if(dbType.equalsIgnoreCase(SentrySchemaHelper.DB_DERBY)) { + driver = sentryConf.get(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_DRIVER, + ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_DRIVER_DEFAULT); + } else { + driver = getValidConfVar(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_DRIVER); + } + // load required JDBC driver + Class.forName(driver); + } catch (IOException e) { + throw new SentryUserException("Missing property: " + e.getMessage()); + } catch (ClassNotFoundException e) { + throw new SentryUserException("Failed to load driver", e); + } + } + + public Configuration getConfiguration() { + return sentryConf; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setPassWord(String passWord) { + this.passWord = passWord; + } + + public void setDryRun(boolean dryRun) { + this.dryRun = dryRun; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public String getDbOpts() { + return dbOpts; + } + + public void setDbOpts(String dbOpts) { + this.dbOpts = dbOpts; + } + + private static void printAndExit(Options cmdLineOptions) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("schemaTool", cmdLineOptions); + System.exit(1); + } + + /*** + * Print Hive version and schema version + * @throws SentryUserException + */ + public void showInfo() throws SentryUserException { + Connection sentryStoreConn = getConnectionToMetastore(true); + System.out.println("Sentry distribution version:\t " + + SentryStoreSchemaInfo.getSentryVersion()); + System.out.println("SentryStore schema version:\t " + + getMetaStoreSchemaVersion(sentryStoreConn)); + } + + // read schema version from sentry store + private String getMetaStoreSchemaVersion(Connection sentryStoreConn) + throws SentryUserException { + String versionQuery; + if (SentrySchemaHelper.getDbCommandParser(dbType).needsQuotedIdentifier()) { + versionQuery = "select t.\"SCHEMA_VERSION\" from \"SENTRY_VERSION\" t"; + } else { + versionQuery = "select t.SCHEMA_VERSION from SENTRY_VERSION t"; + } + try (Statement stmt = sentryStoreConn.createStatement(); + ResultSet res = stmt.executeQuery(versionQuery)) { + if (!res.next()) { + throw new SentryUserException("Didn't find version data in sentry store"); + } + String currentSchemaVersion = res.getString(1); + sentryStoreConn.close(); + return currentSchemaVersion; + } catch (SQLException e) { + throw new SentryUserException("Failed to get schema version.", e); + } + } + + // test the connection sentry store using the config property + private void testConnectionToMetastore() throws SentryUserException { + try (Connection conn = getConnectionToMetastore(true)) { + conn.close(); + } catch (SQLException e) { + throw new SentryUserException("Failed to close sentry store connection", e); + } + } + + /*** + * get JDBC connection to sentry store db + * + * @param printInfo print connection parameters + * @return + * @throws SentryUserException + */ + private Connection getConnectionToMetastore(boolean printInfo) + throws SentryUserException { + if (printInfo) { + System.out.println("Sentry store connection URL:\t " + connectionURL); + System.out.println("Sentry store Connection Driver :\t " + driver); + System.out.println("Sentry store connection User:\t " + userName); + } + if (userName == null || userName.isEmpty()) { + throw new SentryUserException("UserName empty "); + } + try { + // Connect using the JDBC URL and user/pass from conf + return DriverManager.getConnection(connectionURL, userName, passWord); + } catch (SQLException e) { + throw new SentryUserException("Failed to make connection to Sentry store.", e); + } + } + + /** + * check if the current schema version in sentry store matches the Hive version + * @throws SentryUserException + */ + public void verifySchemaVersion() throws SentryUserException { + // don't check version if its a dry run + if (dryRun) { + return; + } + String newSchemaVersion = + getMetaStoreSchemaVersion(getConnectionToMetastore(false)); + // verify that the new version is added to schema + if (!sentryStoreSchemaInfo.getSentrySchemaVersion().equalsIgnoreCase( + newSchemaVersion)) { + throw new SentryUserException("Found unexpected schema version " + + newSchemaVersion); + } + } + + /** + * Perform sentry store schema upgrade. extract the current schema version from sentry store + * @throws SentryUserException + */ + public void doUpgrade() throws SentryUserException { + String fromVersion = getMetaStoreSchemaVersion(getConnectionToMetastore(false)); + if (fromVersion == null || fromVersion.isEmpty()) { + throw new SentryUserException( + "Schema version not stored in the sentry store. " + + + "Metastore schema is too old or corrupt. Try specifying the version manually"); + } + doUpgrade(fromVersion); + } + + /** + * Perform sentry store schema upgrade + * + * @param fromSchemaVer + * Existing version of the sentry store. If null, then read from the sentry store + * @throws SentryUserException + */ + public void doUpgrade(String fromSchemaVer) throws SentryUserException { + if (sentryStoreSchemaInfo.getSentrySchemaVersion().equals(fromSchemaVer)) { + System.out.println("No schema upgrade required from version " + fromSchemaVer); + return; + } + // Find the list of scripts to execute for this upgrade + List<String> upgradeScripts = + sentryStoreSchemaInfo.getUpgradeScripts(fromSchemaVer); + testConnectionToMetastore(); + System.out.println("Starting upgrade sentry store schema from version " + + fromSchemaVer + " to " + + sentryStoreSchemaInfo.getSentrySchemaVersion()); + String scriptDir = sentryStoreSchemaInfo.getSentryStoreScriptDir(); + try { + for (String scriptFile : upgradeScripts) { + System.out.println("Upgrade script " + scriptFile); + if (!dryRun) { + runBeeLine(scriptDir, scriptFile); + System.out.println("Completed " + scriptFile); + } + } + } catch (IOException eIO) { + throw new SentryUserException( + "Upgrade FAILED! Metastore state would be inconsistent !!", eIO); + } + + // Revalidated the new version after upgrade + verifySchemaVersion(); + } + + /** + * Initialize the sentry store schema to current version + * + * @throws SentryUserException + */ + public void doInit() throws SentryUserException { + doInit(sentryStoreSchemaInfo.getSentrySchemaVersion()); + + // Revalidated the new version after upgrade + verifySchemaVersion(); + } + + /** + * Initialize the sentry store schema + * + * @param toVersion + * If null then current hive version is used + * @throws SentryUserException + */ + public void doInit(String toVersion) throws SentryUserException { + testConnectionToMetastore(); + System.out.println("Starting sentry store schema initialization to " + toVersion); + + String initScriptDir = sentryStoreSchemaInfo.getSentryStoreScriptDir(); + String initScriptFile = sentryStoreSchemaInfo.generateInitFileName(toVersion); + + try { + System.out.println("Initialization script " + initScriptFile); + if (!dryRun) { + runBeeLine(initScriptDir, initScriptFile); + System.out.println("Initialization script completed"); + } + } catch (IOException e) { + throw new SentryUserException("Schema initialization FAILED!" + + " Metastore state would be inconsistent !!", e); + } + } + + // Flatten the nested upgrade script into a buffer + public static String buildCommand(NestedScriptParser dbCommandParser, + String scriptDir, String scriptFile) throws IllegalFormatException, IOException { + + BufferedReader bfReader = + new BufferedReader(new FileReader(scriptDir + File.separatorChar + scriptFile)); + String currLine; + StringBuilder sb = new StringBuilder(); + String currentCommand = null; + while ((currLine = bfReader.readLine()) != null) { + currLine = currLine.trim(); + if (currLine.isEmpty()) { + continue; // skip empty lines + } + + if (currentCommand == null) { + currentCommand = currLine; + } else { + currentCommand = currentCommand + " " + currLine; + } + if (dbCommandParser.isPartialCommand(currLine)) { + // if its a partial line, continue collecting the pieces + continue; + } + + // if this is a valid executable command then add it to the buffer + if (!dbCommandParser.isNonExecCommand(currentCommand)) { + currentCommand = dbCommandParser.cleanseCommand(currentCommand); + + if (dbCommandParser.isNestedScript(currentCommand)) { + // if this is a nested sql script then flatten it + String currScript = dbCommandParser.getScriptName(currentCommand); + sb.append(buildCommand(dbCommandParser, scriptDir, currScript)); + } else { + // Now we have a complete statement, process it + // write the line to buffer + sb.append(currentCommand); + sb.append(System.getProperty("line.separator")); + } + } + currentCommand = null; + } + bfReader.close(); + return sb.toString(); + } + + // run beeline on the given sentry store scrip, flatten the nested scripts into single file + private void runBeeLine(String scriptDir, String scriptFile) throws IOException { + NestedScriptParser dbCommandParser = + SentrySchemaHelper.getDbCommandParser(dbType); + dbCommandParser.setDbOpts(getDbOpts()); + // expand the nested script + String sqlCommands = buildCommand(dbCommandParser, scriptDir, scriptFile); + File tmpFile = File.createTempFile("schematool", ".sql"); + tmpFile.deleteOnExit(); + + // write out the buffer into a file. Add beeline commands for autocommit and close + try (FileWriter fstream = new FileWriter(tmpFile.getPath()); + BufferedWriter out = new BufferedWriter(fstream)) { + + out.write("!set Silent " + verbose + System.getProperty("line.separator")); + out.write("!autocommit on" + System.getProperty("line.separator")); + out.write("!set Isolation TRANSACTION_READ_COMMITTED" + + System.getProperty("line.separator")); + out.write("!set AllowMultiLineCommand false" + + System.getProperty("line.separator")); + out.write(sqlCommands); + out.write("!closeall" + System.getProperty("line.separator")); + out.close(); + } + runBeeLine(tmpFile.getPath()); + } + + // Generate the beeline args per hive conf and execute the given script + public void runBeeLine(String sqlScriptFile) throws IOException { + List<String> argList = new ArrayList<String>(); + argList.add("-u"); + argList.add(connectionURL); + argList.add("-d"); + argList + .add(driver); + argList.add("-n"); + argList.add(userName); + argList.add("-p"); + argList.add(passWord); + argList.add("-f"); + argList.add(sqlScriptFile); + + BeeLine beeLine = new BeeLine(); + if (!verbose) { + beeLine.setOutputStream(new PrintStream(new NullOutputStream())); + // beeLine.getOpts().setSilent(true); + } + // beeLine.getOpts().setAllowMultiLineCommand(false); + // beeLine.getOpts().setIsolation("TRANSACTION_READ_COMMITTED"); + int status = beeLine.begin(argList.toArray(new String[0]), null); + if (status != 0) { + throw new IOException("Schema script failed, errorcode " + status); + } + } + + private String getValidConfVar(String confVar) throws IOException { + String confVarKey = confVar; + String confVarValue = sentryConf.get(confVarKey); + if (confVarValue == null || confVarValue.isEmpty()) { + throw new IOException("Empty " + confVar); + } + return confVarValue; + } + + // Create the required command line options + @SuppressWarnings("static-access") + private static void initOptions(Options cmdLineOptions) { + Option help = new Option("help", "print this message"); + Option upgradeOpt = new Option("upgradeSchema", "Schema upgrade"); + Option upgradeFromOpt = OptionBuilder.withArgName("upgradeFrom").hasArg(). + withDescription("Schema upgrade from a version"). + create("upgradeSchemaFrom"); + Option initOpt = new Option("initSchema", "Schema initialization"); + Option initToOpt = OptionBuilder.withArgName("initTo").hasArg(). + withDescription("Schema initialization to a version"). + create("initSchemaTo"); + Option infoOpt = new Option("info", "Show config and schema details"); + + OptionGroup optGroup = new OptionGroup(); + optGroup.addOption(upgradeOpt).addOption(initOpt). + addOption(help).addOption(upgradeFromOpt). + addOption(initToOpt).addOption(infoOpt); + optGroup.setRequired(true); + + Option userNameOpt = OptionBuilder.withArgName("user") + .hasArg() + .withDescription("Override config file user name") + .create("userName"); + Option passwdOpt = OptionBuilder.withArgName("password") + .hasArg() + .withDescription("Override config file password") + .create("passWord"); + Option dbTypeOpt = OptionBuilder.withArgName("databaseType") + .hasArg().withDescription("Metastore database type [" + + SentrySchemaHelper.DB_DERBY + "," + + SentrySchemaHelper.DB_MYSQL + "," + + SentrySchemaHelper.DB_ORACLE + "," + + SentrySchemaHelper.DB_POSTGRACE + "," + + SentrySchemaHelper.DB_DB2 + "]") + .create("dbType"); + Option dbOpts = OptionBuilder.withArgName("databaseOpts") + .hasArgs().withDescription("Backend DB specific options") + .create("dbOpts"); + + Option dryRunOpt = new Option("dryRun", "list SQL scripts (no execute)"); + Option verboseOpt = new Option("verbose", "only print SQL statements"); + + Option configOpt = OptionBuilder.withArgName("confName").hasArgs() + .withDescription("Sentry Service configuration file").isRequired(true) + .create(ServiceConstants.ServiceArgs.CONFIG_FILE_LONG); + + cmdLineOptions.addOption(help); + cmdLineOptions.addOption(dryRunOpt); + cmdLineOptions.addOption(userNameOpt); + cmdLineOptions.addOption(passwdOpt); + cmdLineOptions.addOption(dbTypeOpt); + cmdLineOptions.addOption(verboseOpt); + cmdLineOptions.addOption(dbOpts); + cmdLineOptions.addOption(configOpt); + cmdLineOptions.addOptionGroup(optGroup); + } + + public static class CommandImpl implements Command { + @Override + public void run(String[] args) throws Exception { + CommandLineParser parser = new GnuParser(); + CommandLine line = null; + String dbType = null; + String schemaVer = null; + Options cmdLineOptions = new Options(); + String configFileName = null; + + // Argument handling + initOptions(cmdLineOptions); + try { + line = parser.parse(cmdLineOptions, args); + } catch (ParseException e) { + System.err.println("SentrySchemaTool:Parsing failed. Reason: " + + e.getLocalizedMessage()); + printAndExit(cmdLineOptions); + } + + if (line.hasOption("help")) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("schemaTool", cmdLineOptions); + return; + } + + if (line.hasOption("dbType")) { + dbType = line.getOptionValue("dbType"); + if (!dbType.equalsIgnoreCase(SentrySchemaHelper.DB_DERBY) + && !dbType.equalsIgnoreCase(SentrySchemaHelper.DB_MYSQL) + && !dbType.equalsIgnoreCase(SentrySchemaHelper.DB_POSTGRACE) + && !dbType.equalsIgnoreCase(SentrySchemaHelper.DB_ORACLE) + && !dbType.equalsIgnoreCase(SentrySchemaHelper.DB_DB2)) { + System.err.println("Unsupported dbType " + dbType); + printAndExit(cmdLineOptions); + } + } else { + System.err.println("no dbType supplied"); + printAndExit(cmdLineOptions); + } + if (line.hasOption(ServiceConstants.ServiceArgs.CONFIG_FILE_LONG)) { + configFileName = line + .getOptionValue(ServiceConstants.ServiceArgs.CONFIG_FILE_LONG); + } else { + System.err.println("no config file specified"); + printAndExit(cmdLineOptions); + } + try { + SentrySchemaTool schemaTool = new SentrySchemaTool( + SentryService.loadConfig(configFileName), dbType); + + if (line.hasOption("userName")) { + schemaTool.setUserName(line.getOptionValue("userName")); + } + if (line.hasOption("passWord")) { + schemaTool.setPassWord(line.getOptionValue("passWord")); + } + if (line.hasOption("dryRun")) { + schemaTool.setDryRun(true); + } + if (line.hasOption("verbose")) { + schemaTool.setVerbose(true); + } + if (line.hasOption("dbOpts")) { + schemaTool.setDbOpts(line.getOptionValue("dbOpts")); + } + + if (line.hasOption("info")) { + schemaTool.showInfo(); + } else if (line.hasOption("upgradeSchema")) { + schemaTool.doUpgrade(); + } else if (line.hasOption("upgradeSchemaFrom")) { + schemaVer = line.getOptionValue("upgradeSchemaFrom"); + schemaTool.doUpgrade(schemaVer); + } else if (line.hasOption("initSchema")) { + schemaTool.doInit(); + } else if (line.hasOption("initSchemaTo")) { + schemaVer = line.getOptionValue("initSchemaTo"); + schemaTool.doInit(schemaVer); + } else { + System.err.println("no valid option supplied"); + printAndExit(cmdLineOptions); + } + } catch (SentryUserException e) { + System.err.println(e); + if (line.hasOption("verbose")) { + e.printStackTrace(); + } + System.err.println("*** Sentry schemaTool failed ***"); + System.exit(1); + } catch (MalformedURLException e) { + System.err.println(e); + if (line.hasOption("verbose")) { + e.printStackTrace(); + } + System.err.println("*** Sentry schemaTool failed ***"); + System.exit(1); + } + System.out.println("Sentry schemaTool completed"); + } + } + +}
http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellCommon.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellCommon.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellCommon.java new file mode 100644 index 0000000..94800a4 --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellCommon.java @@ -0,0 +1,284 @@ +/** + * 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.cli.tools; + +import com.google.common.annotations.VisibleForTesting; + +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.lang.StringUtils; + +/** + * SentryShellCommon provides the function for parsing the argument. + * For hive model and generic model, child class should be implemented as a sentry admin tool. + */ +abstract public class SentryShellCommon { + + public enum TYPE { kafka, hive, solr, sqoop }; + + public static final String OPTION_DESC_HELP = "Shell usage"; + public static final String OPTION_DESC_CONF = "sentry-site file path"; + public static final String OPTION_DESC_ROLE_NAME = "Role name"; + public static final String OPTION_DESC_GROUP_NAME = "Group name"; + public static final String OPTION_DESC_PRIVILEGE = "Privilege string"; + public final static String OPTION_DESC_SERVICE = "Name of the service being managed"; + public static final String PREFIX_MESSAGE_MISSING_OPTION = "Missing required option: "; + + public static final String GROUP_SPLIT_CHAR = ","; + + protected String roleName; + protected String serviceName; + protected String groupName; + protected String privilegeStr; + protected String confPath; + // flag for the command + protected boolean isCreateRole; + protected boolean isDropRole; + protected boolean isAddRoleGroup; + protected boolean isDeleteRoleGroup; + protected boolean isGrantPrivilegeRole; + protected boolean isRevokePrivilegeRole; + protected boolean isListRole; + protected boolean isListPrivilege; + protected boolean isListGroup; + protected boolean isPrintHelp; + // flag for the parameter check + protected boolean roleNameRequired; + protected boolean groupNameRequired; + protected boolean privilegeStrRequired; + protected TYPE type; + + /** + * parse arguments + * + * <pre> + * -conf,--sentry_conf <filepath> sentry config file path + * -cr,--create_role -r <rolename> create role + * -dr,--drop_role -r <rolename> drop role + * -arg,--add_role_group -r <rolename> -g <groupname> add role to group + * -drg,--delete_role_group -r <rolename> -g <groupname> delete role from group + * -gpr,--grant_privilege_role -r <rolename> -p <privilege> grant privilege to role + * -rpr,--revoke_privilege_role -r <rolename> -p <privilege> revoke privilege from role + * -lr,--list_role -g <groupname> list roles for group + * -lp,--list_privilege -r <rolename> list privilege for role + * -lg,--list_group list all groups associated with all roles + * -t,--type <typename> the shell for hive model or generic model + * </pre> + * + * @param args + */ + protected boolean parseArgs(String[] args) { + Options simpleShellOptions = new Options(); + + setupOptions(simpleShellOptions); + + + + // help option + Option helpOpt = new Option("h", "help", false, OPTION_DESC_HELP); + helpOpt.setRequired(false); + simpleShellOptions.addOption(helpOpt); + + // this Options is parsed first for help option + Options helpOptions = new Options(); + helpOptions.addOption(helpOpt); + + try { + Parser parser = new GnuParser(); + + // parse help option first + CommandLine cmd = parser.parse(helpOptions, args, true); + for (Option opt : cmd.getOptions()) { + if (opt.getOpt().equals("h")) { + // get the help option, print the usage and exit + usage(simpleShellOptions); + return false; + } + } + + // without help option + cmd = parser.parse(simpleShellOptions, args); + + parseOptions(cmd); + } catch (ParseException pe) { + System.out.println(pe.getMessage()); + usage(simpleShellOptions); + return false; + } + return true; + } + + protected void setupOptions(Options simpleShellOptions) { + OptionGroup simpleShellOptGroup = getMainOptions(); + simpleShellOptions.addOptionGroup(simpleShellOptGroup); + + Option sOpt = new Option("s", "service", true, OPTION_DESC_SERVICE); + sOpt.setRequired(false); + simpleShellOptions.addOption(sOpt); + + // optional args + Option pOpt = new Option("p", "privilege", true, OPTION_DESC_PRIVILEGE); + pOpt.setRequired(false); + simpleShellOptions.addOption(pOpt); + + Option gOpt = new Option("g", "groupname", true, OPTION_DESC_GROUP_NAME); + gOpt.setRequired(false); + simpleShellOptions.addOption(gOpt); + + Option rOpt = new Option("r", "rolename", true, OPTION_DESC_ROLE_NAME); + rOpt.setRequired(false); + simpleShellOptions.addOption(rOpt); + + // this argument should also be parsed in the bin/sentryShell + Option tOpt = new Option("t", "type", true, "[hive|solr|sqoop|.....]"); + tOpt.setRequired(false); + simpleShellOptions.addOption(tOpt); + + // file path of sentry-site + Option sentrySitePathOpt = new Option("conf", "sentry_conf", true, OPTION_DESC_CONF); + sentrySitePathOpt.setRequired(true); + simpleShellOptions.addOption(sentrySitePathOpt); + } + + protected OptionGroup getMainOptions() { + OptionGroup simpleShellOptGroup = new OptionGroup(); + Option crOpt = new Option("cr", "create_role", false, "Create role"); + crOpt.setRequired(false); + + Option drOpt = new Option("dr", "drop_role", false, "Drop role"); + drOpt.setRequired(false); + + Option argOpt = new Option("arg", "add_role_group", false, "Add role to group"); + argOpt.setRequired(false); + + Option drgOpt = new Option("drg", "delete_role_group", false, "Delete role from group"); + drgOpt.setRequired(false); + + Option gprOpt = new Option("gpr", "grant_privilege_role", false, "Grant privilege to role"); + gprOpt.setRequired(false); + + Option rprOpt = new Option("rpr", "revoke_privilege_role", false, "Revoke privilege from role"); + rprOpt.setRequired(false); + + Option lrOpt = new Option("lr", "list_role", false, "List role"); + lrOpt.setRequired(false); + + Option lpOpt = new Option("lp", "list_privilege", false, "List privilege"); + lpOpt.setRequired(false); + + Option lgOpt = new Option("lg", "list_group", false, "List groups"); + lgOpt.setRequired(false); + + + // required args group + simpleShellOptGroup.addOption(crOpt); + simpleShellOptGroup.addOption(drOpt); + simpleShellOptGroup.addOption(argOpt); + simpleShellOptGroup.addOption(drgOpt); + simpleShellOptGroup.addOption(gprOpt); + simpleShellOptGroup.addOption(rprOpt); + simpleShellOptGroup.addOption(lrOpt); + simpleShellOptGroup.addOption(lpOpt); + simpleShellOptGroup.addOption(lgOpt); + simpleShellOptGroup.setRequired(true); + return simpleShellOptGroup; + } + + protected void parseOptions(CommandLine cmd) throws ParseException { + for (Option opt : cmd.getOptions()) { + if (opt.getOpt().equals("p")) { + privilegeStr = opt.getValue(); + } else if (opt.getOpt().equals("g")) { + groupName = opt.getValue(); + } else if (opt.getOpt().equals("r")) { + roleName = opt.getValue(); + } else if (opt.getOpt().equals("s")) { + serviceName = opt.getValue(); + } else if (opt.getOpt().equals("cr")) { + isCreateRole = true; + roleNameRequired = true; + } else if (opt.getOpt().equals("dr")) { + isDropRole = true; + roleNameRequired = true; + } else if (opt.getOpt().equals("arg")) { + isAddRoleGroup = true; + roleNameRequired = true; + groupNameRequired = true; + } else if (opt.getOpt().equals("drg")) { + isDeleteRoleGroup = true; + roleNameRequired = true; + groupNameRequired = true; + } else if (opt.getOpt().equals("gpr")) { + isGrantPrivilegeRole = true; + roleNameRequired = true; + privilegeStrRequired = true; + } else if (opt.getOpt().equals("rpr")) { + isRevokePrivilegeRole = true; + roleNameRequired = true; + privilegeStrRequired = true; + } else if (opt.getOpt().equals("lr")) { + isListRole = true; + } else if (opt.getOpt().equals("lp")) { + isListPrivilege = true; + roleNameRequired = true; + } else if (opt.getOpt().equals("lg")) { + isListGroup = true; + } else if (opt.getOpt().equals("conf")) { + confPath = opt.getValue(); + } else if (opt.getOpt().equals("t")) { + type = TYPE.valueOf(opt.getValue()); + } + } + checkRequiredParameter(roleNameRequired, roleName, OPTION_DESC_ROLE_NAME); + checkRequiredParameter(groupNameRequired, groupName, OPTION_DESC_GROUP_NAME); + checkRequiredParameter(privilegeStrRequired, privilegeStr, OPTION_DESC_PRIVILEGE); + } + + protected void checkRequiredParameter(boolean isRequired, String paramValue, String paramName) throws ParseException { + if (isRequired && StringUtils.isEmpty(paramValue)) { + throw new ParseException(PREFIX_MESSAGE_MISSING_OPTION + paramName); + } + } + + // print usage + private void usage(Options sentryOptions) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("sentryShell", sentryOptions); + } + + // hive model and generic model should implement this method + public abstract void run() throws Exception; + + @VisibleForTesting + public boolean executeShell(String[] args) throws Exception { + boolean result = true; + if (parseArgs(args)) { + run(); + } else { + result = false; + } + return result; + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellGeneric.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellGeneric.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellGeneric.java new file mode 100644 index 0000000..6d25617 --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellGeneric.java @@ -0,0 +1,157 @@ +/** + * 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.cli.tools; + +import com.google.common.collect.Sets; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.sentry.provider.common.AuthorizationComponent; +import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceClient; +import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceClientFactory; +import org.apache.sentry.cli.tools.command.GenericShellCommand; +import org.apache.sentry.provider.db.generic.tools.GenericPrivilegeConverter; +import org.apache.sentry.provider.db.generic.tools.TSentryPrivilegeConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Set; + +/** + * SentryShellGeneric is an admin tool, and responsible for the management of repository. + * The following commands are supported: + * create role, drop role, add group to role, grant privilege to role, + * revoke privilege from role, list roles, list privilege for role. + */ +public class SentryShellGeneric extends SentryShellCommon { + + private static final Logger LOGGER = LoggerFactory.getLogger(SentryShellGeneric.class); + private static final String KAFKA_SERVICE_NAME = "sentry.service.client.kafka.service.name"; + private static final String SOLR_SERVICE_NAME = "sentry.service.client.solr.service.name"; + private static final String SQOOP_SERVICE_NAME = "sentry.service.client.sqoop.service.name"; + + @Override + public void run() throws Exception { + String component = getComponent(); + Configuration conf = getSentryConf(); + + String service = getService(conf); + try (SentryGenericServiceClient client = + SentryGenericServiceClientFactory.create(conf)) { + UserGroupInformation ugi = UserGroupInformation.getLoginUser(); + String requestorName = ugi.getShortUserName(); + TSentryPrivilegeConverter converter = getPrivilegeConverter(component, service); + ShellCommand command = new GenericShellCommand(client, component, service, converter); + + // check the requestor name + if (StringUtils.isEmpty(requestorName)) { + // The exception message will be recorded in log file. + throw new Exception("The requestor name is empty."); + } + + if (isCreateRole) { + command.createRole(requestorName, roleName); + } else if (isDropRole) { + command.dropRole(requestorName, roleName); + } else if (isAddRoleGroup) { + Set<String> groups = Sets.newHashSet(groupName.split(SentryShellCommon.GROUP_SPLIT_CHAR)); + command.grantRoleToGroups(requestorName, roleName, groups); + } else if (isDeleteRoleGroup) { + Set<String> groups = Sets.newHashSet(groupName.split(SentryShellCommon.GROUP_SPLIT_CHAR)); + command.revokeRoleFromGroups(requestorName, roleName, groups); + } else if (isGrantPrivilegeRole) { + command.grantPrivilegeToRole(requestorName, roleName, privilegeStr); + } else if (isRevokePrivilegeRole) { + command.revokePrivilegeFromRole(requestorName, roleName, privilegeStr); + } else if (isListRole) { + List<String> roles = command.listRoles(requestorName, groupName); + for (String role : roles) { + System.out.println(role); + } + } else if (isListPrivilege) { + List<String> privileges = command.listPrivileges(requestorName, roleName); + for (String privilege : privileges) { + System.out.println(privilege); + } + } else if (isListGroup) { + List<String> groups = command.listGroupRoles(requestorName); + for (String group : groups) { + System.out.println(group); + } + } + } + } + + protected GenericPrivilegeConverter getPrivilegeConverter(String component, String service) { + return new GenericPrivilegeConverter(component, service); + } + + protected String getComponent() throws Exception { + if (type == TYPE.kafka) { + return AuthorizationComponent.KAFKA; + } else if (type == TYPE.solr) { + return "SOLR"; + } else if (type == TYPE.sqoop) { + return AuthorizationComponent.SQOOP; + } + + throw new Exception("Invalid type specified for SentryShellGeneric: " + type); + } + + protected String getService(Configuration conf) throws Exception { + if (type == TYPE.kafka) { + return conf.get(KAFKA_SERVICE_NAME, AuthorizationComponent.KAFKA); + } else if (type == TYPE.solr) { + return conf.get(SOLR_SERVICE_NAME, "service1"); + } else if (type == TYPE.sqoop) { + return conf.get(SQOOP_SERVICE_NAME, "sqoopServer1"); + } + + throw new Exception("Invalid type specified for SentryShellGeneric: " + type); + } + + private Configuration getSentryConf() { + Configuration conf = new Configuration(); + conf.addResource(new Path(confPath), true); + return conf; + } + + public static void main(String[] args) throws Exception { + SentryShellGeneric sentryShell = new SentryShellGeneric(); + try { + sentryShell.executeShell(args); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + Throwable current = e; + // find the first printable message; + while (current != null && current.getMessage() == null) { + current = current.getCause(); + } + String error = ""; + if (current != null && current.getMessage() != null) { + error = "Message: " + current.getMessage(); + } + System.out.println("The operation failed. " + error); + System.exit(1); + } + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellHive.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellHive.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellHive.java new file mode 100644 index 0000000..884ed91 --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellHive.java @@ -0,0 +1,118 @@ +/** + * 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.cli.tools; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient; +import org.apache.sentry.cli.tools.command.hive.*; +import org.apache.sentry.service.thrift.SentryServiceClientFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Sets; + +/** + * SentryShellHive is an admin tool, and responsible for the management of repository. + * The following function are supported: + * create role, drop role, add group to role, delete group from role, grant privilege to role, + * revoke privilege from role, list roles for group, list privilege for role. + */ +public class SentryShellHive extends SentryShellCommon { + + private static final Logger LOGGER = LoggerFactory.getLogger(SentryShellHive.class); + + public void run() throws Exception { + + try(SentryPolicyServiceClient client = + SentryServiceClientFactory.create(getSentryConf())) { + UserGroupInformation ugi = UserGroupInformation.getLoginUser(); + String requestorName = ugi.getShortUserName(); + ShellCommand command = new HiveShellCommand(client); + + // check the requestor name + if (StringUtils.isEmpty(requestorName)) { + // The exception message will be recorded in the log file. + throw new Exception("The requestor name is empty."); + } + + if (isCreateRole) { + command.createRole(requestorName, roleName); + } else if (isDropRole) { + command.dropRole(requestorName, roleName); + } else if (isAddRoleGroup) { + Set<String> groups = Sets.newHashSet(groupName.split(SentryShellCommon.GROUP_SPLIT_CHAR)); + command.grantRoleToGroups(requestorName, roleName, groups); + } else if (isDeleteRoleGroup) { + Set<String> groups = Sets.newHashSet(groupName.split(SentryShellCommon.GROUP_SPLIT_CHAR)); + command.revokeRoleFromGroups(requestorName, roleName, groups); + } else if (isGrantPrivilegeRole) { + command.grantPrivilegeToRole(requestorName, roleName, privilegeStr); + } else if (isRevokePrivilegeRole) { + command.revokePrivilegeFromRole(requestorName, roleName, privilegeStr); + } else if (isListRole) { + List<String> roles = command.listRoles(requestorName, groupName); + for (String role : roles) { + System.out.println(role); + } + } else if (isListPrivilege) { + List<String> privileges = command.listPrivileges(requestorName, roleName); + for (String privilege : privileges) { + System.out.println(privilege); + } + } else if (isListGroup) { + List<String> groups = command.listGroupRoles(requestorName); + for (String group : groups) { + System.out.println(group); + } + } + } + } + + private Configuration getSentryConf() { + Configuration conf = new Configuration(); + conf.addResource(new Path(confPath), true); + return conf; + } + + public static void main(String[] args) throws Exception { + SentryShellHive sentryShell = new SentryShellHive(); + try { + sentryShell.executeShell(args); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + Throwable current = e; + // find the first printable message; + while (current != null && current.getMessage() == null) { + current = current.getCause(); + } + + if (current != null) { + System.out.println("The operation failed." + + (current.getMessage() == null ? "" : " Message: " + current.getMessage())); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellIndexer.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellIndexer.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellIndexer.java new file mode 100644 index 0000000..c7f854f --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/SentryShellIndexer.java @@ -0,0 +1,124 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cli.tools; + +import org.apache.commons.cli.CommandLine; +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.hadoop.conf.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.sentry.provider.common.AuthorizationComponent.HBASE_INDEXER; +import static org.apache.sentry.service.thrift.ServiceConstants.ClientConfig.SERVICE_NAME; + +/** + * SentryShellIndexer is an admin tool, and responsible for the management of repository. + * The following commands are supported: + * create role, drop role, add group to role, grant privilege to role, + * revoke privilege from role, list roles, list privilege for role. + */ +public class SentryShellIndexer extends SentryShellGeneric { + + protected boolean isMigration = false; + + private static final Logger LOGGER = LoggerFactory.getLogger(SentryShellIndexer.class); + + private final SentryConfigToolIndexer configTool = new SentryConfigToolIndexer(); + + @Override + protected void setupOptions(Options simpleShellOptions) { + super.setupOptions(simpleShellOptions); + configTool.setupOptions(simpleShellOptions); + } + + @Override + protected void parseOptions(CommandLine cmd) throws ParseException { + super.parseOptions(cmd); + configTool.parseOptions(cmd); + for (Option opt : cmd.getOptions()) { + if (opt.getOpt().equals("mgr")) { + isMigration = true; + } + } + } + + @Override + protected OptionGroup getMainOptions() { + OptionGroup mainOptions = super.getMainOptions(); + Option mgrOpt = new Option("mgr", "migrate", false, "Migrate ini file to Sentry service"); + mgrOpt.setRequired(false); + mainOptions.addOption(mgrOpt); + return mainOptions; + } + + /** + * Processes the necessary command based on the arguments parsed earlier. + * @throws Exception + */ + @Override + public void run() throws Exception { + + if (isMigration) { + configTool.run(); + return; + } + + super.run(); + } + + @Override + protected String getComponent() throws Exception { + return HBASE_INDEXER; + } + + @Override + protected String getService(Configuration conf) throws Exception { + String service = conf.get(SERVICE_NAME, serviceName); + if (service == null) { + throw new IllegalArgumentException("Service was not defined. Please, use -s command option, or sentry.provider.backend.generic.service-name configuration entry."); + } + return service; + } + + /** + * Entry-point for Hbase indexer cli tool. + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + SentryShellIndexer sentryShell = new SentryShellIndexer(); + try { + sentryShell.executeShell(args); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + Throwable current = e; + // find the first printable message; + while (current != null && current.getMessage() == null) { + current = current.getCause(); + } + System.out.println("The operation failed." + + (current.getMessage() == null ? "" : " Message: " + current.getMessage())); + System.exit(1); + } + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/ShellCommand.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/ShellCommand.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/ShellCommand.java new file mode 100644 index 0000000..9a141c3 --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/ShellCommand.java @@ -0,0 +1,47 @@ +/** + * 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.cli.tools; + +import java.util.List; +import java.util.Set; + +import org.apache.sentry.core.common.exception.SentryUserException; + +/** + * The interface for all admin commands, eg, CreateRoleCmd. It is independent of the underlying mechanism (i.e. Generic or Hive) + */ +public interface ShellCommand { + + void createRole(String requestorName, String roleName) throws SentryUserException; + + void dropRole(String requestorName, String roleName) throws SentryUserException; + + void grantPrivilegeToRole(String requestorName, String roleName, String privilege) throws SentryUserException; + + void grantRoleToGroups(String requestorName, String roleName, Set<String> groups) throws SentryUserException; + + void revokePrivilegeFromRole(String requestorName, String roleName, String privilege) throws SentryUserException; + + void revokeRoleFromGroups(String requestorName, String roleName, Set<String> groups) throws SentryUserException; + + List<String> listRoles(String requestorName, String group) throws SentryUserException; + + List<String> listPrivileges(String requestorName, String roleName) throws SentryUserException; + + List<String> listGroupRoles(String requestorName) throws SentryUserException; +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/GenericShellCommand.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/GenericShellCommand.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/GenericShellCommand.java new file mode 100644 index 0000000..3e0da50 --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/GenericShellCommand.java @@ -0,0 +1,156 @@ +/** + * 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.cli.tools.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceClient; +import org.apache.sentry.provider.db.generic.service.thrift.TSentryPrivilege; +import org.apache.sentry.provider.db.generic.service.thrift.TSentryRole; +import org.apache.sentry.cli.tools.ShellCommand; +import org.apache.sentry.provider.db.generic.tools.TSentryPrivilegeConverter; + +/** + * The ShellCommand implementation for the Generic clients + */ +public class GenericShellCommand implements ShellCommand { + + private final SentryGenericServiceClient client; + private final String component; + private final TSentryPrivilegeConverter converter; + private final String serviceName; + + public GenericShellCommand(SentryGenericServiceClient client, String component, String serviceName, + TSentryPrivilegeConverter converter) { + this.client = client; + this.component = component; + this.serviceName = serviceName; + this.converter = converter; + } + + public void createRole(String requestorName, String roleName) throws SentryUserException { + client.createRole(requestorName, roleName, component); + } + + public void dropRole(String requestorName, String roleName) throws SentryUserException { + client.dropRole(requestorName, roleName, component); + } + + public void grantPrivilegeToRole(String requestorName, String roleName, String privilege) throws SentryUserException { + TSentryPrivilege sentryPrivilege = converter.fromString(privilege); + client.grantPrivilege(requestorName, roleName, component, sentryPrivilege); + } + + public void grantRoleToGroups(String requestorName, String roleName, Set<String> groups) throws SentryUserException { + client.grantRoleToGroups(requestorName, roleName, component, groups); + } + + public void revokePrivilegeFromRole(String requestorName, String roleName, String privilege) throws SentryUserException { + TSentryPrivilege sentryPrivilege = converter.fromString(privilege); + client.revokePrivilege(requestorName, roleName, component, sentryPrivilege); + } + + public void revokeRoleFromGroups(String requestorName, String roleName, Set<String> groups) throws SentryUserException { + client.revokeRoleFromGroups(requestorName, roleName, component, groups); + } + + public List<String> listRoles(String requestorName, String group) throws SentryUserException { + Set<TSentryRole> roles; + if (StringUtils.isEmpty(group)) { + roles = client.listAllRoles(requestorName, component); + } else { + roles = client.listRolesByGroupName(requestorName, group, component); + } + + List<String> result = new ArrayList<>(); + if (roles != null) { + for (TSentryRole role : roles) { + result.add(role.getRoleName()); + } + } + + return result; + } + + public List<String> listPrivileges(String requestorName, String roleName) throws SentryUserException { + Set<TSentryPrivilege> privileges = client + .listAllPrivilegesByRoleName(requestorName, roleName, component, serviceName); + + List<String> result = new ArrayList<>(); + if (privileges != null) { + for (TSentryPrivilege privilege : privileges) { + String privilegeStr = converter.toString(privilege); + result.add(privilegeStr); + } + } + + return result; + } + + public List<String> listGroupRoles(String requestorName) throws SentryUserException { + Set<TSentryRole> roles = client.listAllRoles(requestorName, component); + if (roles == null || roles.isEmpty()) { + return Collections.emptyList(); + } + + // Set of all group names + Set<String> groupNames = new HashSet<>(); + + // Map group to set of roles + Map<String, Set<String>> groupInfo = new HashMap<>(); + + // Get all group names + for (TSentryRole role: roles) { + for (String group : role.getGroups()) { + groupNames.add(group); + Set<String> groupRoles = groupInfo.get(group); + if (groupRoles != null) { + // Add a new or existing role + groupRoles.add(role.getRoleName()); + continue; + } + // Never seen this group before + groupRoles = new HashSet<>(); + groupRoles.add(role.getRoleName()); + groupInfo.put(group, groupRoles); + } + } + + List<String> groups = new ArrayList<>(groupNames); + + // Produce printable result as + // group1 = role1, role2, ... + // group2 = ... + List<String> result = new LinkedList<>(); + for (String groupName: groups) { + result.add(groupName + " = " + StringUtils.join(groupInfo.get(groupName), ", ")); + } + + return result; + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/CommandUtil.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/CommandUtil.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/CommandUtil.java new file mode 100644 index 0000000..b06732e --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/CommandUtil.java @@ -0,0 +1,63 @@ +/** + * 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.cli.tools.command.hive; + +import org.apache.commons.lang.StringUtils; +import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege; +import org.apache.sentry.service.thrift.ServiceConstants; + +public final class CommandUtil { + + public static final String SPLIT_CHAR = ","; + + private CommandUtil() { + // Make constructor private to avoid instantiation + } + + // check the privilege value for the specific privilege scope + // eg, for the table scope, server and database can't be empty + public static void validatePrivilegeHierarchy(TSentryPrivilege tSentryPrivilege) throws IllegalArgumentException { + String serverName = tSentryPrivilege.getServerName(); + String dbName = tSentryPrivilege.getDbName(); + String tableName = tSentryPrivilege.getTableName(); + String columnName = tSentryPrivilege.getColumnName(); + String uri = tSentryPrivilege.getURI(); + if (ServiceConstants.PrivilegeScope.SERVER.toString().equals(tSentryPrivilege.getPrivilegeScope())) { + if (StringUtils.isEmpty(serverName)) { + throw new IllegalArgumentException("The hierarchy of privilege is not correct."); + } + } else if (ServiceConstants.PrivilegeScope.URI.toString().equals(tSentryPrivilege.getPrivilegeScope())) { + if (StringUtils.isEmpty(serverName) || StringUtils.isEmpty(uri)) { + throw new IllegalArgumentException("The hierarchy of privilege is not correct."); + } + } else if (ServiceConstants.PrivilegeScope.DATABASE.toString().equals(tSentryPrivilege.getPrivilegeScope())) { + if (StringUtils.isEmpty(serverName) || StringUtils.isEmpty(dbName)) { + throw new IllegalArgumentException("The hierarchy of privilege is not correct."); + } + } else if (ServiceConstants.PrivilegeScope.TABLE.toString().equals(tSentryPrivilege.getPrivilegeScope())) { + if (StringUtils.isEmpty(serverName) || StringUtils.isEmpty(dbName) + || StringUtils.isEmpty(tableName)) { + throw new IllegalArgumentException("The hierarchy of privilege is not correct."); + } + } else if (ServiceConstants.PrivilegeScope.COLUMN.toString().equals(tSentryPrivilege.getPrivilegeScope()) + && (StringUtils.isEmpty(serverName) || StringUtils.isEmpty(dbName) + || StringUtils.isEmpty(tableName) || StringUtils.isEmpty(columnName))) { + throw new IllegalArgumentException("The hierarchy of privilege is not correct."); + } + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/HiveShellCommand.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/HiveShellCommand.java b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/HiveShellCommand.java new file mode 100644 index 0000000..1d11c3b --- /dev/null +++ b/sentry-tools/src/main/java/org/apache/sentry/cli/tools/command/hive/HiveShellCommand.java @@ -0,0 +1,152 @@ +/** + * 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.cli.tools.command.hive; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient; +import org.apache.sentry.provider.db.service.thrift.TSentryGroup; +import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege; +import org.apache.sentry.provider.db.service.thrift.TSentryRole; +import org.apache.sentry.cli.tools.ShellCommand; +import org.apache.sentry.service.thrift.SentryServiceUtil; + +/** + * The ShellCommand implementation for Hive. + */ +public class HiveShellCommand implements ShellCommand { + + private final SentryPolicyServiceClient client; + + public HiveShellCommand(SentryPolicyServiceClient client) { + this.client = client; + } + + public void createRole(String requestorName, String roleName) throws SentryUserException { + client.createRole(requestorName, roleName); + } + + public void dropRole(String requestorName, String roleName) throws SentryUserException { + client.dropRole(requestorName, roleName); + } + + public void grantPrivilegeToRole(String requestorName, String roleName, String privilege) throws SentryUserException { + TSentryPrivilege tSentryPrivilege = SentryServiceUtil.convertToTSentryPrivilege(privilege); + CommandUtil.validatePrivilegeHierarchy(tSentryPrivilege); + client.grantPrivilege(requestorName, roleName, tSentryPrivilege); + } + + public void grantRoleToGroups(String requestorName, String roleName, Set<String> groups) throws SentryUserException { + client.grantRoleToGroups(requestorName, roleName, groups); + } + + public void revokePrivilegeFromRole(String requestorName, String roleName, String privilege) throws SentryUserException { + TSentryPrivilege tSentryPrivilege = SentryServiceUtil.convertToTSentryPrivilege(privilege); + CommandUtil.validatePrivilegeHierarchy(tSentryPrivilege); + client.revokePrivilege(requestorName, roleName, tSentryPrivilege); + } + + public void revokeRoleFromGroups(String requestorName, String roleName, Set<String> groups) throws SentryUserException { + client.revokeRoleFromGroups(requestorName, roleName, groups); + } + + public List<String> listRoles(String requestorName, String group) throws SentryUserException { + Set<TSentryRole> roles; + if (StringUtils.isEmpty(group)) { + roles = client.listAllRoles(requestorName); + } else { + roles = client.listRolesByGroupName(requestorName, group); + } + + List<String> result = new ArrayList<>(); + if (roles != null) { + for (TSentryRole role : roles) { + result.add(role.getRoleName()); + } + } + + return result; + } + + public List<String> listPrivileges(String requestorName, String roleName) throws SentryUserException { + Set<TSentryPrivilege> privileges = client + .listAllPrivilegesByRoleName(requestorName, roleName); + + List<String> result = new ArrayList<>(); + if (privileges != null) { + for (TSentryPrivilege privilege : privileges) { + String privilegeStr = SentryServiceUtil.convertTSentryPrivilegeToStr(privilege); + result.add(privilegeStr); + } + } + return result; + } + + public List<String> listGroupRoles(String requestorName) throws SentryUserException { + Set<TSentryRole> roles = client.listAllRoles(requestorName); + if (roles == null || roles.isEmpty()) { + return Collections.emptyList(); + } + + // Set of all group names + Set<String> groupNames = new HashSet<>(); + + // Map group to set of roles + Map<String, Set<String>> groupInfo = new HashMap<>(); + + // Get all group names + for (TSentryRole role: roles) { + for (TSentryGroup group : role.getGroups()) { + String groupName = group.getGroupName(); + groupNames.add(groupName); + Set<String> groupRoles = groupInfo.get(groupName); + if (groupRoles != null) { + // Add a new or existing role + groupRoles.add(role.getRoleName()); + continue; + } + // Never seen this group before + groupRoles = new HashSet<>(); + groupRoles.add(role.getRoleName()); + groupInfo.put(groupName, groupRoles); + } + } + + List<String> groups = new ArrayList<>(groupNames); + + // Produce printable result as + // group1 = role1, role2, ... + // group2 = ... + List<String> result = new LinkedList<>(); + for (String groupName: groups) { + result.add(groupName + " = " + StringUtils.join(groupInfo.get(groupName), ", ")); + } + + return result; + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java index b7652a5..863f992 100644 --- a/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java +++ b/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java @@ -23,7 +23,7 @@ import com.budhash.cliche.Shell; import com.budhash.cliche.ShellDependent; import org.apache.sentry.core.common.exception.SentryUserException; -import org.apache.sentry.provider.db.tools.ShellCommand; +import org.apache.sentry.cli.tools.ShellCommand; import java.util.Arrays; import java.util.Collections; http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java index 8b8898f..3ff6737 100644 --- a/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java +++ b/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java @@ -24,7 +24,7 @@ import com.budhash.cliche.Shell; import com.budhash.cliche.ShellDependent; import org.apache.sentry.core.common.exception.SentryUserException; -import org.apache.sentry.provider.db.tools.ShellCommand; +import org.apache.sentry.cli.tools.ShellCommand; import java.util.Collections; import java.util.List; http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java index c014a30..42bce19 100644 --- a/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java +++ b/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java @@ -24,7 +24,7 @@ import com.budhash.cliche.Shell; import com.budhash.cliche.ShellDependent; import org.apache.sentry.core.common.exception.SentryUserException; -import org.apache.sentry.provider.db.tools.ShellCommand; +import org.apache.sentry.cli.tools.ShellCommand; import java.util.Collections; import java.util.List; http://git-wip-us.apache.org/repos/asf/sentry/blob/6752f14a/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java ---------------------------------------------------------------------- diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java index d9952a9..04d65bd 100644 --- a/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java +++ b/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java @@ -22,11 +22,11 @@ import org.apache.sentry.core.common.exception.SentryUserException; import org.apache.sentry.provider.common.AuthorizationComponent; import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceClient; import org.apache.sentry.provider.db.generic.tools.GenericPrivilegeConverter; -import org.apache.sentry.provider.db.generic.tools.command.GenericShellCommand; -import org.apache.sentry.provider.db.generic.tools.command.TSentryPrivilegeConverter; +import org.apache.sentry.cli.tools.command.GenericShellCommand; +import org.apache.sentry.provider.db.generic.tools.TSentryPrivilegeConverter; import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient; -import org.apache.sentry.provider.db.tools.ShellCommand; -import org.apache.sentry.provider.db.tools.command.hive.HiveShellCommand; +import org.apache.sentry.cli.tools.ShellCommand; +import org.apache.sentry.cli.tools.command.hive.HiveShellCommand; import com.budhash.cliche.Command; import com.budhash.cliche.Param;
