http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaTool.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaTool.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaTool.java new file mode 100644 index 0000000..d75e24b --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/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.provider.db.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.provider.db.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/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java new file mode 100644 index 0000000..b668b95 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java @@ -0,0 +1,110 @@ +/** + * 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.service.thrift; + +import java.util.Arrays; +import java.util.List; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.SaslRpcServer; +import org.apache.sentry.core.common.exception.ConnectionDeniedException; +import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig; + +public class GSSCallback extends SaslRpcServer.SaslGssCallbackHandler { + + private final Configuration conf; + public GSSCallback(Configuration conf) { + super(); + this.conf = conf; + } + + boolean comparePrincipals(String principal1, String principal2) { + String[] principalParts1 = SaslRpcServer.splitKerberosName(principal1); + String[] principalParts2 = SaslRpcServer.splitKerberosName(principal2); + if (principalParts1.length == 0 || principalParts2.length == 0) { + return false; + } + if (principalParts1.length == principalParts2.length) { + for (int i=0; i < principalParts1.length; i++) { + if (!principalParts1[i].equals(principalParts2[i])) { + return false; + } + } + return true; + } else { + return false; + } + } + + boolean allowConnect(String principal) { + String allowedPrincipals = conf.get(ServerConfig.ALLOW_CONNECT); + if (allowedPrincipals == null) { + return false; + } + String principalShortName = getShortName(principal); + List<String> items = Arrays.asList(allowedPrincipals.split("\\s*,\\s*")); + for (String item : items) { + if (comparePrincipals(item, principalShortName)) { + return true; + } + } + return false; + } + + private String getShortName(String principal) { + String parts[] = SaslRpcServer.splitKerberosName(principal); + return parts[0]; + } + + @Override + public void handle(Callback[] callbacks) + throws UnsupportedCallbackException, ConnectionDeniedException { + AuthorizeCallback ac = null; + for (Callback callback : callbacks) { + if (callback instanceof AuthorizeCallback) { + ac = (AuthorizeCallback) callback; + } else { + throw new UnsupportedCallbackException(callback, + "Unrecognized SASL GSSAPI Callback"); + } + } + if (ac != null) { + String authid = ac.getAuthenticationID(); + String authzid = ac.getAuthorizationID(); + + if (allowConnect(authid)) { + if (authid.equals(authzid)) { + ac.setAuthorized(true); + } else { + ac.setAuthorized(false); + } + if (ac.isAuthorized()) { + ac.setAuthorizedID(authzid); + } + } else { + throw new ConnectionDeniedException(ac, + "Connection to sentry service denied due to lack of client credentials", + authid); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java new file mode 100644 index 0000000..41e4fe4 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java @@ -0,0 +1,107 @@ +/** + * 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.service.thrift; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.login.AppConfigurationEntry; + +public class KerberosConfiguration extends javax.security.auth.login.Configuration { + private String principal; + private String keytab; + private boolean isInitiator; + private static final boolean IBM_JAVA = System.getProperty("java.vendor").contains("IBM"); + + private KerberosConfiguration(String principal, File keytab, + boolean client) { + this.principal = principal; + this.keytab = keytab.getAbsolutePath(); + this.isInitiator = client; + } + + public static javax.security.auth.login.Configuration createClientConfig(String principal, + File keytab) { + return new KerberosConfiguration(principal, keytab, true); + } + + public static javax.security.auth.login.Configuration createServerConfig(String principal, + File keytab) { + return new KerberosConfiguration(principal, keytab, false); + } + + private static String getKrb5LoginModuleName() { + return (IBM_JAVA ? "com.ibm.security.auth.module.Krb5LoginModule" + : "com.sun.security.auth.module.Krb5LoginModule"); + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + Map<String, String> options = new HashMap<String, String>(); + + if (IBM_JAVA) { + // IBM JAVA's UseKeytab covers both keyTab and useKeyTab options + options.put("useKeytab",keytab.startsWith("file://") ? keytab : "file://" + keytab); + + options.put("principal", principal); + options.put("refreshKrb5Config", "true"); + + // Both "initiator" and "acceptor" + options.put("credsType", "both"); + } else { + options.put("keyTab", keytab); + options.put("principal", principal); + options.put("useKeyTab", "true"); + options.put("storeKey", "true"); + options.put("doNotPrompt", "true"); + options.put("useTicketCache", "true"); + options.put("renewTGT", "true"); + options.put("refreshKrb5Config", "true"); + options.put("isInitiator", Boolean.toString(isInitiator)); + } + + String ticketCache = System.getenv("KRB5CCNAME"); + if (IBM_JAVA) { + // If cache is specified via env variable, it takes priority + if (ticketCache != null) { + // IBM JAVA only respects system property so copy ticket cache to system property + // The first value searched when "useDefaultCcache" is true. + System.setProperty("KRB5CCNAME", ticketCache); + } else { + ticketCache = System.getProperty("KRB5CCNAME"); + } + + if (ticketCache != null) { + options.put("useDefaultCcache", "true"); + options.put("renewTGT", "true"); + } + } else { + if (ticketCache != null) { + options.put("ticketCache", ticketCache); + } + } + options.put("debug", "true"); + + return new AppConfigurationEntry[]{ + new AppConfigurationEntry(getKrb5LoginModuleName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options)}; + } +} + http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java new file mode 100644 index 0000000..a3bb6ab --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java @@ -0,0 +1,31 @@ +/** + * 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.service.thrift; + +import org.apache.hadoop.conf.Configuration; +import org.apache.thrift.TMultiplexedProcessor; + +public abstract class ProcessorFactory { + protected final Configuration conf; + + public ProcessorFactory(Configuration conf) { + this.conf = conf; + } + + public abstract boolean register(TMultiplexedProcessor processor) throws Exception; +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryKerberosContext.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryKerberosContext.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryKerberosContext.java new file mode 100644 index 0000000..f54f161 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryKerberosContext.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.service.thrift; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Sets; + +public class SentryKerberosContext implements Runnable { + private static final float TICKET_RENEW_WINDOW = 0.80f; + private static final Logger LOGGER = LoggerFactory + .getLogger(SentryKerberosContext.class); + private LoginContext loginContext; + private Subject subject; + private final javax.security.auth.login.Configuration kerberosConfig; + @Deprecated + private Thread renewerThread; + @Deprecated + private boolean shutDownRenewer = false; + + public SentryKerberosContext(String principal, String keyTab, boolean autoRenewTicket) + throws LoginException { + subject = new Subject(false, Sets.newHashSet(new KerberosPrincipal(principal)), + new HashSet<Object>(), new HashSet<Object>()); + kerberosConfig = KerberosConfiguration.createClientConfig(principal, new File(keyTab)); + loginWithNewContext(); + if (autoRenewTicket) { + startRenewerThread(); + } + } + + private void loginWithNewContext() throws LoginException { + LOGGER.info("Logging in with new Context"); + logoutSubject(); + loginContext = new LoginContext("", subject, null, kerberosConfig); + loginContext.login(); + subject = loginContext.getSubject(); + } + + private void logoutSubject() { + if (loginContext != null) { + try { + loginContext.logout(); + } catch (LoginException e) { + LOGGER.warn("Error logging out the subject", e); + } + } + loginContext = null; + } + + public Subject getSubject() { + return subject; + } + + /** + * Get the Kerberos TGT + * @return the user's TGT or null if none was found + */ + @Deprecated + private KerberosTicket getTGT() { + Set<KerberosTicket> tickets = subject.getPrivateCredentials(KerberosTicket.class); + for(KerberosTicket ticket: tickets) { + KerberosPrincipal server = ticket.getServer(); + if (server.getName().equals("krbtgt/" + server.getRealm() + + "@" + server.getRealm())) { + return ticket; + } + } + return null; + } + + @Deprecated + private long getRefreshTime(KerberosTicket tgt) { + long start = tgt.getStartTime().getTime(); + long end = tgt.getEndTime().getTime(); + LOGGER.debug("Ticket start time: " + start); + LOGGER.debug("Ticket End time: " + end); + return start + (long) ((end - start) * TICKET_RENEW_WINDOW); + } + + /*** + * Ticket renewer thread + * wait till 80% time interval left on the ticket and then renew it + */ + @Deprecated + @Override + public void run() { + try { + LOGGER.info("Sentry Ticket renewer thread started"); + while (!shutDownRenewer) { + KerberosTicket tgt = getTGT(); + if (tgt == null) { + LOGGER.warn("No ticket found in the cache"); + return; + } + long nextRefresh = getRefreshTime(tgt); + while (System.currentTimeMillis() < nextRefresh) { + Thread.sleep(1000); + if (shutDownRenewer) { + return; + } + } + loginWithNewContext(); + LOGGER.debug("Renewed ticket"); + } + } catch (InterruptedException e1) { + LOGGER.warn("Sentry Ticket renewer thread interrupted", e1); + return; + } catch (LoginException e) { + LOGGER.warn("Failed to renew ticket", e); + } finally { + logoutSubject(); + LOGGER.info("Sentry Ticket renewer thread finished"); + } + } + + @Deprecated + public void startRenewerThread() { + renewerThread = new Thread(this); + renewerThread.start(); + } + + public void shutDown() throws LoginException { + if (renewerThread != null) { + shutDownRenewer = true; + } else { + logoutSubject(); + } + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryService.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryService.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryService.java new file mode 100644 index 0000000..5783649 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryService.java @@ -0,0 +1,426 @@ +/** + * 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.service.thrift; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.EventListener; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; + +import javax.security.auth.Subject; + +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.Options; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.net.NetUtils; +import org.apache.hadoop.security.SaslRpcServer; +import org.apache.hadoop.security.SaslRpcServer.AuthMethod; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.sentry.Command; +import org.apache.sentry.provider.db.service.thrift.SentryHealthCheckServletContextListener; +import org.apache.sentry.provider.db.service.thrift.SentryMetricsServletContextListener; +import org.apache.sentry.provider.db.service.thrift.SentryWebServer; +import org.apache.sentry.service.thrift.ServiceConstants.ConfUtilties; +import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig; +import org.apache.thrift.TMultiplexedProcessor; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.server.TServer; +import org.apache.thrift.server.TServerEventHandler; +import org.apache.thrift.server.TThreadPoolServer; +import org.apache.thrift.transport.TSaslServerTransport; +import org.apache.thrift.transport.TServerSocket; +import org.apache.thrift.transport.TServerTransport; +import org.apache.thrift.transport.TTransportFactory; +import org.eclipse.jetty.util.MultiException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +public class SentryService implements Callable { + + private static final Logger LOGGER = LoggerFactory + .getLogger(SentryService.class); + + private static enum Status { + NOT_STARTED(), STARTED(); + } + + private final Configuration conf; + private final InetSocketAddress address; + private final int maxThreads; + private final int minThreads; + private boolean kerberos; + private final String principal; + private final String[] principalParts; + private final String keytab; + private final ExecutorService serviceExecutor; + private Future serviceStatus; + private TServer thriftServer; + private Status status; + private int webServerPort; + private SentryWebServer sentryWebServer; + private long maxMessageSize; + + public SentryService(Configuration conf) { + this.conf = conf; + int port = conf + .getInt(ServerConfig.RPC_PORT, ServerConfig.RPC_PORT_DEFAULT); + if (port == 0) { + port = findFreePort(); + conf.setInt(ServerConfig.RPC_PORT, port); + } + this.address = NetUtils.createSocketAddr( + conf.get(ServerConfig.RPC_ADDRESS, ServerConfig.RPC_ADDRESS_DEFAULT), + port); + LOGGER.info("Configured on address " + address); + kerberos = ServerConfig.SECURITY_MODE_KERBEROS.equalsIgnoreCase( + conf.get(ServerConfig.SECURITY_MODE, ServerConfig.SECURITY_MODE_KERBEROS).trim()); + maxThreads = conf.getInt(ServerConfig.RPC_MAX_THREADS, + ServerConfig.RPC_MAX_THREADS_DEFAULT); + minThreads = conf.getInt(ServerConfig.RPC_MIN_THREADS, + ServerConfig.RPC_MIN_THREADS_DEFAULT); + maxMessageSize = conf.getLong(ServerConfig.SENTRY_POLICY_SERVER_THRIFT_MAX_MESSAGE_SIZE, + ServerConfig.SENTRY_POLICY_SERVER_THRIFT_MAX_MESSAGE_SIZE_DEFAULT); + if (kerberos) { + // Use Hadoop libraries to translate the _HOST placeholder with actual hostname + try { + String rawPrincipal = Preconditions.checkNotNull(conf.get(ServerConfig.PRINCIPAL), ServerConfig.PRINCIPAL + " is required"); + principal = SecurityUtil.getServerPrincipal(rawPrincipal, address.getAddress()); + } catch(IOException io) { + throw new RuntimeException("Can't translate kerberos principal'", io); + } + LOGGER.info("Using kerberos principal: " + principal); + + principalParts = SaslRpcServer.splitKerberosName(principal); + Preconditions.checkArgument(principalParts.length == 3, + "Kerberos principal should have 3 parts: " + principal); + keytab = Preconditions.checkNotNull(conf.get(ServerConfig.KEY_TAB), + ServerConfig.KEY_TAB + " is required"); + File keytabFile = new File(keytab); + Preconditions.checkState(keytabFile.isFile() && keytabFile.canRead(), + "Keytab " + keytab + " does not exist or is not readable."); + } else { + principal = null; + principalParts = null; + keytab = null; + } + serviceExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { + private int count = 0; + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, SentryService.class.getSimpleName() + "-" + + (count++)); + } + }); + webServerPort = conf.getInt(ServerConfig.SENTRY_WEB_PORT, ServerConfig.SENTRY_WEB_PORT_DEFAULT); + status = Status.NOT_STARTED; + } + + @Override + public String call() throws Exception { + SentryKerberosContext kerberosContext = null; + try { + status = Status.STARTED; + if (kerberos) { + Boolean autoRenewTicket = conf.getBoolean(ServerConfig.SENTRY_KERBEROS_TGT_AUTORENEW, ServerConfig.SENTRY_KERBEROS_TGT_AUTORENEW_DEFAULT); + kerberosContext = new SentryKerberosContext(principal, keytab, autoRenewTicket); + Subject.doAs(kerberosContext.getSubject(), new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + runServer(); + return null; + } + }); + } else { + runServer(); + } + } catch (Exception t) { + LOGGER.error("Error starting server", t); + throw new Exception("Error starting server", t); + } finally { + if (kerberosContext != null) { + kerberosContext.shutDown(); + } + status = Status.NOT_STARTED; + } + return null; + } + + private void runServer() throws Exception { + Iterable<String> processorFactories = ConfUtilties.CLASS_SPLITTER + .split(conf.get(ServerConfig.PROCESSOR_FACTORIES, + ServerConfig.PROCESSOR_FACTORIES_DEFAULT).trim()); + TMultiplexedProcessor processor = new TMultiplexedProcessor(); + boolean registeredProcessor = false; + for (String processorFactory : processorFactories) { + Class<?> clazz = conf.getClassByName(processorFactory); + if (!ProcessorFactory.class.isAssignableFrom(clazz)) { + throw new IllegalArgumentException("Processor Factory " + + processorFactory + " is not a " + + ProcessorFactory.class.getName()); + } + try { + Constructor<?> constructor = clazz + .getConstructor(Configuration.class); + LOGGER.info("ProcessorFactory being used: " + clazz.getCanonicalName()); + ProcessorFactory factory = (ProcessorFactory) constructor + .newInstance(conf); + boolean registerStatus = factory.register(processor); + if (!registerStatus) { + LOGGER.error("Failed to register " + clazz.getCanonicalName()); + } + registeredProcessor = registerStatus || registeredProcessor; + } catch (Exception e) { + throw new IllegalStateException("Could not create " + + processorFactory, e); + } + } + if (!registeredProcessor) { + throw new IllegalStateException( + "Failed to register any processors from " + processorFactories); + } + TServerTransport serverTransport = new TServerSocket(address); + TTransportFactory transportFactory = null; + if (kerberos) { + TSaslServerTransport.Factory saslTransportFactory = new TSaslServerTransport.Factory(); + saslTransportFactory.addServerDefinition(AuthMethod.KERBEROS + .getMechanismName(), principalParts[0], principalParts[1], + ServerConfig.SASL_PROPERTIES, new GSSCallback(conf)); + transportFactory = saslTransportFactory; + } else { + transportFactory = new TTransportFactory(); + } + TThreadPoolServer.Args args = new TThreadPoolServer.Args( + serverTransport).processor(processor) + .transportFactory(transportFactory) + .protocolFactory(new TBinaryProtocol.Factory(true, true, maxMessageSize, maxMessageSize)) + .minWorkerThreads(minThreads).maxWorkerThreads(maxThreads); + thriftServer = new TThreadPoolServer(args); + LOGGER.info("Serving on " + address); + startSentryWebServer(); + thriftServer.serve(); + } + + private void startSentryWebServer() throws Exception{ + Boolean sentryReportingEnable = conf.getBoolean(ServerConfig.SENTRY_WEB_ENABLE, + ServerConfig.SENTRY_WEB_ENABLE_DEFAULT); + if(sentryReportingEnable) { + List<EventListener> listenerList = new ArrayList<EventListener>(); + listenerList.add(new SentryHealthCheckServletContextListener()); + listenerList.add(new SentryMetricsServletContextListener()); + sentryWebServer = new SentryWebServer(listenerList, webServerPort, conf); + sentryWebServer.start(); + } + + } + + private void stopSentryWebServer() throws Exception{ + if( sentryWebServer != null) { + sentryWebServer.stop(); + sentryWebServer = null; + } + } + + public InetSocketAddress getAddress() { + return address; + } + + public synchronized boolean isRunning() { + return status == Status.STARTED && thriftServer != null + && thriftServer.isServing(); + } + + public synchronized void start() throws Exception{ + if (status != Status.NOT_STARTED) { + throw new IllegalStateException("Cannot start when " + status); + } + LOGGER.info("Attempting to start..."); + serviceStatus = serviceExecutor.submit(this); + } + + public synchronized void stop() throws Exception{ + MultiException exception = null; + LOGGER.info("Attempting to stop..."); + if (isRunning()) { + LOGGER.info("Attempting to stop sentry thrift service..."); + try { + thriftServer.stop(); + thriftServer = null; + status = Status.NOT_STARTED; + } catch (Exception e) { + LOGGER.error("Error while stopping sentry thrift service", e); + exception = addMultiException(exception,e); + } + } else { + thriftServer = null; + status = Status.NOT_STARTED; + LOGGER.info("Sentry thrift service is already stopped..."); + } + if (isWebServerRunning()) { + try { + LOGGER.info("Attempting to stop sentry web service..."); + stopSentryWebServer(); + } catch (Exception e) { + LOGGER.error("Error while stopping sentry web service", e); + exception = addMultiException(exception,e); + } + } else { + LOGGER.info("Sentry web service is already stopped..."); + } + if (exception != null) { + exception.ifExceptionThrow(); + } + LOGGER.info("Stopped..."); + } + + // wait for the service thread to finish execution + public synchronized void waitOnFuture() throws ExecutionException, InterruptedException { + LOGGER.info("Waiting on future.get()"); + serviceStatus.get(); + } + + private MultiException addMultiException(MultiException exception, Exception e) { + MultiException newException = exception; + if (newException == null) { + newException = new MultiException(); + } + newException.add(e); + return newException; + } + + private boolean isWebServerRunning() { + return sentryWebServer != null + && sentryWebServer.isAlive(); + } + + private static int findFreePort() { + int attempts = 0; + while (attempts++ <= 1000) { + try { + ServerSocket s = new ServerSocket(0); + int port = s.getLocalPort(); + s.close(); + return port; + } catch (IOException e) { + // ignore and retry + } + } + throw new IllegalStateException("Unable to find a port after 1000 attempts"); + } + + public static Configuration loadConfig(String configFileName) + throws MalformedURLException { + File configFile = null; + if (configFileName == null) { + throw new IllegalArgumentException("Usage: " + + ServiceConstants.ServiceArgs.CONFIG_FILE_LONG + + " path/to/sentry-service.xml"); + } else if (!((configFile = new File(configFileName)).isFile() && configFile + .canRead())) { + throw new IllegalArgumentException("Cannot read configuration file " + + configFile); + } + Configuration conf = new Configuration(false); + conf.addResource(configFile.toURI().toURL()); + return conf; + } + + public static class CommandImpl implements Command { + @Override + public void run(String[] args) throws Exception { + CommandLineParser parser = new GnuParser(); + Options options = new Options(); + options.addOption(ServiceConstants.ServiceArgs.CONFIG_FILE_SHORT, + ServiceConstants.ServiceArgs.CONFIG_FILE_LONG, + true, "Sentry Service configuration file"); + CommandLine commandLine = parser.parse(options, args); + String configFileName = commandLine.getOptionValue(ServiceConstants. + ServiceArgs.CONFIG_FILE_LONG); + File configFile = null; + if (configFileName == null || commandLine.hasOption("h") || commandLine.hasOption("help")) { + // print usage + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("sentry --command service", options); + System.exit(-1); + } else if(!((configFile = new File(configFileName)).isFile() && configFile.canRead())) { + throw new IllegalArgumentException("Cannot read configuration file " + configFile); + } + Configuration serverConf = loadConfig(configFileName); + final SentryService server = new SentryService(serverConf); + server.start(); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + LOGGER.info("ShutdownHook shutting down server"); + try { + server.stop(); + } catch (Throwable t) { + LOGGER.error("Error stopping SentryService", t); + } + } + }); + + // Let's wait on the service to stop + try { + server.waitOnFuture(); + } finally { + server.serviceExecutor.shutdown(); + } + } + } + + public Configuration getConf() { + return conf; + } + + /** + * Add Thrift event handler to underlying thrift threadpool server + * @param eventHandler + */ + public void setThriftEventHandler(TServerEventHandler eventHandler) throws IllegalStateException { + if (thriftServer == null) { + throw new IllegalStateException("Server is not initialized or stopped"); + } + thriftServer.setServerEventHandler(eventHandler); + } + + public TServerEventHandler getThriftEventHandler() throws IllegalStateException { + if (thriftServer == null) { + throw new IllegalStateException("Server is not initialized or stopped"); + } + return thriftServer.getEventHandler(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java new file mode 100644 index 0000000..1685702 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java @@ -0,0 +1,28 @@ +/** + * 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.service.thrift; +import org.apache.hadoop.conf.Configuration; + +public class SentryServiceFactory { + + public SentryService create(Configuration conf) throws Exception { + return new SentryService(conf); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.derby.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.derby.sql b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.derby.sql new file mode 100644 index 0000000..04353d1 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.derby.sql @@ -0,0 +1,2 @@ +-- SENTRY-327 +ALTER TABLE SENTRY_DB_PRIVILEGE ADD COLUMN WITH_GRANT_OPTION CHAR(1) NOT NULL DEFAULT 'N'; http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.mysql.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.mysql.sql b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.mysql.sql new file mode 100644 index 0000000..7d96bc0 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.mysql.sql @@ -0,0 +1,2 @@ +-- SENTRY-327 +ALTER TABLE `SENTRY_DB_PRIVILEGE` ADD `WITH_GRANT_OPTION` CHAR(1) NOT NULL DEFAULT 'N'; http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.oracle.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.oracle.sql b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.oracle.sql new file mode 100644 index 0000000..f42ccdf --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.oracle.sql @@ -0,0 +1,2 @@ +-- SENTRY-327 +ALTER TABLE SENTRY_DB_PRIVILEGE ADD WITH_GRANT_OPTION CHAR(1) DEFAULT 'N' NOT NULL; http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.postgres.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.postgres.sql b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.postgres.sql new file mode 100644 index 0000000..1b670ec --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/001-SENTRY-327.postgres.sql @@ -0,0 +1,2 @@ +-- SENTRY-327 +ALTER TABLE "SENTRY_DB_PRIVILEGE" ADD COLUMN "WITH_GRANT_OPTION" CHAR(1) NOT NULL DEFAULT 'N'; http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.derby.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.derby.sql b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.derby.sql new file mode 100644 index 0000000..647e9e2 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.derby.sql @@ -0,0 +1,13 @@ +-- SENTRY-339 +DROP INDEX SENTRYPRIVILEGENAME; +CREATE UNIQUE INDEX SENTRYPRIVILEGENAME ON SENTRY_DB_PRIVILEGE ("SERVER_NAME",DB_NAME,"TABLE_NAME",URI,"ACTION",WITH_GRANT_OPTION); + +ALTER TABLE SENTRY_DB_PRIVILEGE DROP COLUMN PRIVILEGE_NAME; + +ALTER TABLE SENTRY_DB_PRIVILEGE ALTER COLUMN DB_NAME SET DEFAULT '__NULL__'; +ALTER TABLE SENTRY_DB_PRIVILEGE ALTER COLUMN TABLE_NAME SET DEFAULT '__NULL__'; +ALTER TABLE SENTRY_DB_PRIVILEGE ALTER COLUMN URI SET DEFAULT '__NULL__'; + +UPDATE SENTRY_DB_PRIVILEGE SET DB_NAME = DEFAULT WHERE DB_NAME is null; +UPDATE SENTRY_DB_PRIVILEGE SET TABLE_NAME = DEFAULT WHERE TABLE_NAME is null; +UPDATE SENTRY_DB_PRIVILEGE SET URI = DEFAULT WHERE URI is null; http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.mysql.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.mysql.sql b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.mysql.sql new file mode 100644 index 0000000..cd4ec7c --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.mysql.sql @@ -0,0 +1,13 @@ +-- SENTRY-339 +ALTER TABLE `SENTRY_DB_PRIVILEGE` DROP INDEX `SENTRY_DB_PRIV_PRIV_NAME_UNIQ`; +ALTER TABLE `SENTRY_DB_PRIVILEGE` ADD UNIQUE `SENTRY_DB_PRIV_PRIV_NAME_UNIQ` (`SERVER_NAME`,`DB_NAME`,`TABLE_NAME`,`URI`(250),`ACTION`,`WITH_GRANT_OPTION`); +ALTER TABLE `SENTRY_DB_PRIVILEGE` DROP `PRIVILEGE_NAME`; + +ALTER TABLE SENTRY_DB_PRIVILEGE ALTER COLUMN DB_NAME SET DEFAULT '__NULL__'; +ALTER TABLE SENTRY_DB_PRIVILEGE ALTER COLUMN TABLE_NAME SET DEFAULT '__NULL__'; +ALTER TABLE SENTRY_DB_PRIVILEGE ALTER COLUMN URI SET DEFAULT '__NULL__'; + +UPDATE SENTRY_DB_PRIVILEGE SET DB_NAME = DEFAULT WHERE DB_NAME is null; +UPDATE SENTRY_DB_PRIVILEGE SET TABLE_NAME = DEFAULT WHERE TABLE_NAME is null; +UPDATE SENTRY_DB_PRIVILEGE SET URI = DEFAULT WHERE URI is null; + http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.oracle.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.oracle.sql b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.oracle.sql new file mode 100644 index 0000000..f5f596d --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.oracle.sql @@ -0,0 +1,13 @@ +-- SENTRY-339 +ALTER TABLE SENTRY_DB_PRIVILEGE DROP CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" DROP INDEX; +ALTER TABLE SENTRY_DB_PRIVILEGE ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","URI","ACTION","WITH_GRANT_OPTION"); +ALTER TABLE SENTRY_DB_PRIVILEGE DROP COLUMN PRIVILEGE_NAME; + +ALTER TABLE SENTRY_DB_PRIVILEGE MODIFY DB_NAME DEFAULT '__NULL__'; +ALTER TABLE SENTRY_DB_PRIVILEGE MODIFY TABLE_NAME DEFAULT '__NULL__'; +ALTER TABLE SENTRY_DB_PRIVILEGE MODIFY URI DEFAULT '__NULL__'; + +UPDATE SENTRY_DB_PRIVILEGE SET DB_NAME = DEFAULT WHERE DB_NAME is null; +UPDATE SENTRY_DB_PRIVILEGE SET TABLE_NAME = DEFAULT WHERE TABLE_NAME is null; +UPDATE SENTRY_DB_PRIVILEGE SET URI = DEFAULT WHERE URI is null; + http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.postgres.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.postgres.sql b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.postgres.sql new file mode 100644 index 0000000..458e447 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/002-SENTRY-339.postgres.sql @@ -0,0 +1,13 @@ +-- SENTRY-339 +ALTER TABLE "SENTRY_DB_PRIVILEGE" DROP CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ"; +ALTER TABLE "SENTRY_DB_PRIVILEGE" ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","URI", "ACTION","WITH_GRANT_OPTION"); +ALTER TABLE "SENTRY_DB_PRIVILEGE" DROP COLUMN "PRIVILEGE_NAME"; + +ALTER TABLE "SENTRY_DB_PRIVILEGE" ALTER COLUMN "DB_NAME" SET DEFAULT '__NULL__'; +AlTER TABLE "SENTRY_DB_PRIVILEGE" ALTER COLUMN "TABLE_NAME" SET DEFAULT '__NULL__'; +ALTER TABLE "SENTRY_DB_PRIVILEGE" ALTER COLUMN "URI" SET DEFAULT '__NULL__'; + +UPDATE "SENTRY_DB_PRIVILEGE" SET "DB_NAME" = DEFAULT where "DB_NAME" is null; +UPDATE "SENTRY_DB_PRIVILEGE" SET "TABLE_NAME" = DEFAULT where "TABLE_NAME" is null; +UPDATE "SENTRY_DB_PRIVILEGE" SET "URI" = DEFAULT where "URI" is null; + http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.derby.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.derby.sql b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.derby.sql new file mode 100644 index 0000000..f27b358 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.derby.sql @@ -0,0 +1,7 @@ +-- SENTRY-380 +ALTER TABLE SENTRY_DB_PRIVILEGE DROP GRANTOR_PRINCIPAL; +ALTER TABLE SENTRY_ROLE DROP GRANTOR_PRINCIPAL; +ALTER TABLE SENTRY_GROUP DROP GRANTOR_PRINCIPAL; + +ALTER TABLE SENTRY_ROLE_DB_PRIVILEGE_MAP ADD GRANTOR_PRINCIPAL VARCHAR(128); +ALTER TABLE SENTRY_ROLE_GROUP_MAP ADD GRANTOR_PRINCIPAL VARCHAR(128); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.mysql.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.mysql.sql b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.mysql.sql new file mode 100644 index 0000000..8e0a633 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.mysql.sql @@ -0,0 +1,7 @@ +-- SENTRY-380 +ALTER TABLE `SENTRY_DB_PRIVILEGE` DROP `GRANTOR_PRINCIPAL`; +ALTER TABLE `SENTRY_ROLE` DROP `GRANTOR_PRINCIPAL`; +ALTER TABLE `SENTRY_GROUP` DROP `GRANTOR_PRINCIPAL`; + +ALTER TABLE `SENTRY_ROLE_DB_PRIVILEGE_MAP` ADD `GRANTOR_PRINCIPAL` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin; +ALTER TABLE `SENTRY_ROLE_GROUP_MAP` ADD `GRANTOR_PRINCIPAL` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.oracle.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.oracle.sql b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.oracle.sql new file mode 100644 index 0000000..d07d20e --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.oracle.sql @@ -0,0 +1,7 @@ +-- SENTRY-380 +ALTER TABLE "SENTRY_DB_PRIVILEGE" DROP COLUMN "GRANTOR_PRINCIPAL"; +ALTER TABLE "SENTRY_ROLE" DROP COLUMN "GRANTOR_PRINCIPAL"; +ALTER TABLE "SENTRY_GROUP" DROP COLUMN "GRANTOR_PRINCIPAL"; + +ALTER TABLE "SENTRY_ROLE_DB_PRIVILEGE_MAP" ADD "GRANTOR_PRINCIPAL" VARCHAR2(128); +ALTER TABLE "SENTRY_ROLE_GROUP_MAP" ADD "GRANTOR_PRINCIPAL" VARCHAR2(128); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.postgres.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.postgres.sql b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.postgres.sql new file mode 100644 index 0000000..95a2ef1 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/003-SENTRY-380.postgres.sql @@ -0,0 +1,7 @@ +-- SENTRY-380 +ALTER TABLE "SENTRY_DB_PRIVILEGE" DROP "GRANTOR_PRINCIPAL"; +ALTER TABLE "SENTRY_ROLE" DROP "GRANTOR_PRINCIPAL"; +ALTER TABLE "SENTRY_GROUP" DROP "GRANTOR_PRINCIPAL"; + +ALTER TABLE "SENTRY_ROLE_DB_PRIVILEGE_MAP" ADD "GRANTOR_PRINCIPAL" character varying(128); +ALTER TABLE "SENTRY_ROLE_GROUP_MAP" ADD "GRANTOR_PRINCIPAL" character varying(128); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.derby.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.derby.sql b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.derby.sql new file mode 100644 index 0000000..da1f4d6 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.derby.sql @@ -0,0 +1,4 @@ +-- SENTRY-74 +ALTER TABLE SENTRY_DB_PRIVILEGE ADD COLUMN COLUMN_NAME VARCHAR(4000) DEFAULT '__NULL__'; +DROP INDEX SENTRYPRIVILEGENAME; +CREATE UNIQUE INDEX SENTRYPRIVILEGENAME ON SENTRY_DB_PRIVILEGE ("SERVER_NAME",DB_NAME,"TABLE_NAME","COLUMN_NAME",URI,"ACTION",WITH_GRANT_OPTION); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.mysql.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.mysql.sql b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.mysql.sql new file mode 100644 index 0000000..1419ca3 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.mysql.sql @@ -0,0 +1,4 @@ +-- SENTRY-74 +ALTER TABLE `SENTRY_DB_PRIVILEGE` ADD `COLUMN_NAME` VARCHAR(128) DEFAULT '__NULL__'; +ALTER TABLE `SENTRY_DB_PRIVILEGE` DROP INDEX `SENTRY_DB_PRIV_PRIV_NAME_UNIQ`; +ALTER TABLE `SENTRY_DB_PRIVILEGE` ADD UNIQUE `SENTRY_DB_PRIV_PRIV_NAME_UNIQ` (`SERVER_NAME`,`DB_NAME`,`TABLE_NAME`,`COLUMN_NAME`,`URI`(250),`ACTION`,`WITH_GRANT_OPTION`); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.oracle.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.oracle.sql b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.oracle.sql new file mode 100644 index 0000000..a70ae0a --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.oracle.sql @@ -0,0 +1,4 @@ +-- SENTRY-74 +ALTER TABLE SENTRY_DB_PRIVILEGE ADD COLUMN_NAME VARCHAR2(128) DEFAULT '__NULL__'; +ALTER TABLE SENTRY_DB_PRIVILEGE DROP CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" DROP INDEX; +ALTER TABLE SENTRY_DB_PRIVILEGE ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","COLUMN_NAME","URI","ACTION","WITH_GRANT_OPTION"); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.postgres.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.postgres.sql b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.postgres.sql new file mode 100644 index 0000000..81bdfa3 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/004-SENTRY-74.postgres.sql @@ -0,0 +1,4 @@ +-- SENTRY-74 +ALTER TABLE "SENTRY_DB_PRIVILEGE" ADD COLUMN "COLUMN_NAME" character varying(128) DEFAULT '__NULL__'; +ALTER TABLE "SENTRY_DB_PRIVILEGE" DROP CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ"; +ALTER TABLE "SENTRY_DB_PRIVILEGE" ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","COLUMN_NAME","URI", "ACTION","WITH_GRANT_OPTION"); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.derby.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.derby.sql b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.derby.sql new file mode 100644 index 0000000..c038b81 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.derby.sql @@ -0,0 +1,43 @@ +-- Table SENTRY_GM_PRIVILEGE for classes [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +CREATE TABLE SENTRY_GM_PRIVILEGE +( + GM_PRIVILEGE_ID BIGINT NOT NULL, + "ACTION" VARCHAR(40), + COMPONENT_NAME VARCHAR(400), + CREATE_TIME BIGINT NOT NULL, + WITH_GRANT_OPTION CHAR(1) NOT NULL DEFAULT 'N', + RESOURCE_NAME_0 VARCHAR(400) DEFAULT '__NULL__', + RESOURCE_NAME_1 VARCHAR(400) DEFAULT '__NULL__', + RESOURCE_NAME_2 VARCHAR(400) DEFAULT '__NULL__', + RESOURCE_NAME_3 VARCHAR(400) DEFAULT '__NULL__', + RESOURCE_TYPE_0 VARCHAR(400) DEFAULT '__NULL__', + RESOURCE_TYPE_1 VARCHAR(400) DEFAULT '__NULL__', + RESOURCE_TYPE_2 VARCHAR(400) DEFAULT '__NULL__', + RESOURCE_TYPE_3 VARCHAR(400) DEFAULT '__NULL__', + "SCOPE" VARCHAR(40), + SERVICE_NAME VARCHAR(400) +); +-- Primary key(GM_PRIVILEGE_ID) +ALTER TABLE SENTRY_GM_PRIVILEGE ADD CONSTRAINT SENTRY_GM_PRIVILEGE_PK PRIMARY KEY (GM_PRIVILEGE_ID); + +-- Constraints for table SENTRY_GM_PRIVILEGE for class(es) [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +CREATE UNIQUE INDEX GM_PRIVILEGE_INDEX ON SENTRY_GM_PRIVILEGE (COMPONENT_NAME,SERVICE_NAME,RESOURCE_NAME_0,RESOURCE_TYPE_0,RESOURCE_NAME_1,RESOURCE_TYPE_1,RESOURCE_NAME_2,RESOURCE_TYPE_2,RESOURCE_NAME_3,RESOURCE_TYPE_3,"ACTION",WITH_GRANT_OPTION); + +-- Table SENTRY_ROLE_GM_PRIVILEGE_MAP for join relationship +CREATE TABLE SENTRY_ROLE_GM_PRIVILEGE_MAP +( + ROLE_ID BIGINT NOT NULL, + GM_PRIVILEGE_ID BIGINT NOT NULL +); +ALTER TABLE SENTRY_ROLE_GM_PRIVILEGE_MAP ADD CONSTRAINT SENTRY_ROLE_GM_PRIVILEGE_MAP_PK PRIMARY KEY (ROLE_ID,GM_PRIVILEGE_ID); + +-- Constraints for table SENTRY_ROLE_GM_PRIVILEGE_MAP +CREATE INDEX SENTRY_ROLE_GM_PRIVILEGE_MAP_N50 ON SENTRY_ROLE_GM_PRIVILEGE_MAP (ROLE_ID); + +CREATE INDEX SENTRY_ROLE_GM_PRIVILEGE_MAP_N49 ON SENTRY_ROLE_GM_PRIVILEGE_MAP (GM_PRIVILEGE_ID); + +ALTER TABLE SENTRY_ROLE_GM_PRIVILEGE_MAP ADD CONSTRAINT SENTRY_ROLE_GM_PRIVILEGE_MAP_FK2 FOREIGN KEY (GM_PRIVILEGE_ID) REFERENCES SENTRY_GM_PRIVILEGE (GM_PRIVILEGE_ID); + +ALTER TABLE SENTRY_ROLE_GM_PRIVILEGE_MAP ADD CONSTRAINT SENTRY_ROLE_GM_PRIVILEGE_MAP_FK1 FOREIGN KEY (ROLE_ID) REFERENCES SENTRY_ROLE (ROLE_ID); + + http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.mysql.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.mysql.sql b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.mysql.sql new file mode 100644 index 0000000..920737f --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.mysql.sql @@ -0,0 +1,62 @@ +-- Table SENTRY_GM_PRIVILEGE for classes [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +CREATE TABLE `SENTRY_GM_PRIVILEGE` +( + `GM_PRIVILEGE_ID` BIGINT NOT NULL, + `ACTION` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `COMPONENT_NAME` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `CREATE_TIME` BIGINT NOT NULL, + `WITH_GRANT_OPTION` CHAR(1) NOT NULL DEFAULT 'N', + `RESOURCE_NAME_0` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `RESOURCE_NAME_1` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `RESOURCE_NAME_2` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `RESOURCE_NAME_3` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `RESOURCE_TYPE_0` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `RESOURCE_TYPE_1` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `RESOURCE_TYPE_2` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `RESOURCE_TYPE_3` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT '__NULL__', + `SCOPE` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `SERVICE_NAME` VARCHAR(64) BINARY CHARACTER SET utf8 COLLATE utf8_bin NOT NULL +) ENGINE=INNODB DEFAULT CHARSET=utf8; + +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD CONSTRAINT `SENTRY_GM_PRIVILEGE_PK` PRIMARY KEY (`GM_PRIVILEGE_ID`); +-- Constraints for table SENTRY_GM_PRIVILEGE for class(es) [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD UNIQUE `GM_PRIVILEGE_UNIQUE` (`COMPONENT_NAME`,`SERVICE_NAME`,`RESOURCE_NAME_0`,`RESOURCE_TYPE_0`,`RESOURCE_NAME_1`,`RESOURCE_TYPE_1`,`RESOURCE_NAME_2`,`RESOURCE_TYPE_2`,`RESOURCE_NAME_3`,`RESOURCE_TYPE_3`,`ACTION`,`WITH_GRANT_OPTION`); + +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD INDEX `SENTRY_GM_PRIV_COMP_IDX` (`COMPONENT_NAME`); + +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD INDEX `SENTRY_GM_PRIV_SERV_IDX` (`SERVICE_NAME`); + +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD INDEX `SENTRY_GM_PRIV_RES0_IDX` (`RESOURCE_NAME_0`,`RESOURCE_TYPE_0`); + +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD INDEX `SENTRY_GM_PRIV_RES1_IDX` (`RESOURCE_NAME_1`,`RESOURCE_TYPE_1`); + +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD INDEX `SENTRY_GM_PRIV_RES2_IDX` (`RESOURCE_NAME_2`,`RESOURCE_TYPE_2`); + +ALTER TABLE `SENTRY_GM_PRIVILEGE` + ADD INDEX `SENTRY_GM_PRIV_RES3_IDX` (`RESOURCE_NAME_3`,`RESOURCE_TYPE_3`); + +-- Table SENTRY_ROLE_GM_PRIVILEGE_MAP for join relationship +CREATE TABLE `SENTRY_ROLE_GM_PRIVILEGE_MAP` +( + `ROLE_ID` BIGINT NOT NULL, + `GM_PRIVILEGE_ID` BIGINT NOT NULL +) ENGINE=INNODB DEFAULT CHARSET=utf8; + +ALTER TABLE `SENTRY_ROLE_GM_PRIVILEGE_MAP` + ADD CONSTRAINT `SENTRY_ROLE_GM_PRIVILEGE_MAP_PK` PRIMARY KEY (`ROLE_ID`,`GM_PRIVILEGE_ID`); + +-- Constraints for table SENTRY_ROLE_GM_PRIVILEGE_MAP +ALTER TABLE `SENTRY_ROLE_GM_PRIVILEGE_MAP` + ADD CONSTRAINT `SEN_RLE_GM_PRV_MAP_SN_RLE_FK` + FOREIGN KEY (`ROLE_ID`) REFERENCES `SENTRY_ROLE`(`ROLE_ID`); + +ALTER TABLE `SENTRY_ROLE_GM_PRIVILEGE_MAP` + ADD CONSTRAINT `SEN_RL_GM_PRV_MAP_SN_DB_PRV_FK` + FOREIGN KEY (`GM_PRIVILEGE_ID`) REFERENCES `SENTRY_GM_PRIVILEGE`(`GM_PRIVILEGE_ID`); http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.oracle.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.oracle.sql b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.oracle.sql new file mode 100644 index 0000000..412bc45 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.oracle.sql @@ -0,0 +1,55 @@ +-- Table SENTRY_GM_PRIVILEGE for classes [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +CREATE TABLE "SENTRY_GM_PRIVILEGE" ( + "GM_PRIVILEGE_ID" NUMBER NOT NULL, + "COMPONENT_NAME" VARCHAR2(32) NOT NULL, + "CREATE_TIME" NUMBER NOT NULL, + "WITH_GRANT_OPTION" CHAR(1) DEFAULT 'N' NOT NULL, + "RESOURCE_NAME_0" VARCHAR2(64) DEFAULT '__NULL__', + "RESOURCE_NAME_1" VARCHAR2(64) DEFAULT '__NULL__', + "RESOURCE_NAME_2" VARCHAR2(64) DEFAULT '__NULL__', + "RESOURCE_NAME_3" VARCHAR2(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_0" VARCHAR2(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_1" VARCHAR2(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_2" VARCHAR2(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_3" VARCHAR2(64) DEFAULT '__NULL__', + "ACTION" VARCHAR2(32) NOT NULL, + "SCOPE" VARCHAR2(128) NOT NULL, + "SERVICE_NAME" VARCHAR2(64) NOT NULL +); + +ALTER TABLE "SENTRY_GM_PRIVILEGE" + ADD CONSTRAINT "SENTRY_GM_PRIV_PK" PRIMARY KEY ("GM_PRIVILEGE_ID"); +-- Constraints for table SENTRY_GM_PRIVILEGE for class(es) [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +ALTER TABLE "SENTRY_GM_PRIVILEGE" + ADD CONSTRAINT "SENTRY_GM_PRIV_PRIV_NAME_UNIQ" UNIQUE ("COMPONENT_NAME","SERVICE_NAME","RESOURCE_NAME_0","RESOURCE_NAME_1","RESOURCE_NAME_2", + "RESOURCE_NAME_3","RESOURCE_TYPE_0","RESOURCE_TYPE_1","RESOURCE_TYPE_2","RESOURCE_TYPE_3","ACTION","WITH_GRANT_OPTION"); + +CREATE INDEX "SENTRY_GM_PRIV_COMP_IDX" ON "SENTRY_GM_PRIVILEGE" ("COMPONENT_NAME"); + +CREATE INDEX "SENTRY_GM_PRIV_SERV_IDX" ON "SENTRY_GM_PRIVILEGE" ("SERVICE_NAME"); + +CREATE INDEX "SENTRY_GM_PRIV_RES0_IDX" ON "SENTRY_GM_PRIVILEGE" ("RESOURCE_NAME_0","RESOURCE_TYPE_0"); + +CREATE INDEX "SENTRY_GM_PRIV_RES1_IDX" ON "SENTRY_GM_PRIVILEGE" ("RESOURCE_NAME_1","RESOURCE_TYPE_1"); + +CREATE INDEX "SENTRY_GM_PRIV_RES2_IDX" ON "SENTRY_GM_PRIVILEGE" ("RESOURCE_NAME_2","RESOURCE_TYPE_2"); + +CREATE INDEX "SENTRY_GM_PRIV_RES3_IDX" ON "SENTRY_GM_PRIVILEGE" ("RESOURCE_NAME_3","RESOURCE_TYPE_3"); + +-- Table SENTRY_ROLE_GM_PRIVILEGE_MAP for join relationship +CREATE TABLE "SENTRY_ROLE_GM_PRIVILEGE_MAP" ( + "ROLE_ID" NUMBER NOT NULL, + "GM_PRIVILEGE_ID" NUMBER NOT NULL +); + +ALTER TABLE "SENTRY_ROLE_GM_PRIVILEGE_MAP" + ADD CONSTRAINT "SEN_RLE_GM_PRIV_MAP_PK" PRIMARY KEY ("ROLE_ID","GM_PRIVILEGE_ID"); + +-- Constraints for table SENTRY_ROLE_GM_PRIVILEGE_MAP +ALTER TABLE "SENTRY_ROLE_GM_PRIVILEGE_MAP" + ADD CONSTRAINT "SEN_RLE_GM_PRV_MAP_SN_RLE_FK" + FOREIGN KEY ("ROLE_ID") REFERENCES "SENTRY_ROLE"("ROLE_ID") INITIALLY DEFERRED; + +ALTER TABLE "SENTRY_ROLE_GM_PRIVILEGE_MAP" + ADD CONSTRAINT "SEN_RL_GM_PRV_MAP_SN_DB_PRV_FK" + FOREIGN KEY ("GM_PRIVILEGE_ID") REFERENCES "SENTRY_GM_PRIVILEGE"("GM_PRIVILEGE_ID") INITIALLY DEFERRED; http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.postgres.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.postgres.sql b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.postgres.sql new file mode 100644 index 0000000..e9e1655 --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/005-SENTRY-398.postgres.sql @@ -0,0 +1,54 @@ +-- Table SENTRY_GM_PRIVILEGE for classes [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +CREATE TABLE "SENTRY_GM_PRIVILEGE" ( + "GM_PRIVILEGE_ID" BIGINT NOT NULL, + "COMPONENT_NAME" character varying(32) NOT NULL, + "CREATE_TIME" BIGINT NOT NULL, + "WITH_GRANT_OPTION" CHAR(1) NOT NULL DEFAULT 'N', + "RESOURCE_NAME_0" character varying(64) DEFAULT '__NULL__', + "RESOURCE_NAME_1" character varying(64) DEFAULT '__NULL__', + "RESOURCE_NAME_2" character varying(64) DEFAULT '__NULL__', + "RESOURCE_NAME_3" character varying(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_0" character varying(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_1" character varying(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_2" character varying(64) DEFAULT '__NULL__', + "RESOURCE_TYPE_3" character varying(64) DEFAULT '__NULL__', + "ACTION" character varying(32) NOT NULL, + "SCOPE" character varying(128) NOT NULL, + "SERVICE_NAME" character varying(64) NOT NULL +); +ALTER TABLE ONLY "SENTRY_GM_PRIVILEGE" + ADD CONSTRAINT "SENTRY_GM_PRIV_PK" PRIMARY KEY ("GM_PRIVILEGE_ID"); +-- Constraints for table SENTRY_GM_PRIVILEGE for class(es) [org.apache.sentry.provider.db.service.model.MSentryGMPrivilege] +ALTER TABLE ONLY "SENTRY_GM_PRIVILEGE" + ADD CONSTRAINT "SENTRY_GM_PRIV_PRIV_NAME_UNIQ" UNIQUE ("COMPONENT_NAME","SERVICE_NAME","RESOURCE_NAME_0","RESOURCE_NAME_1","RESOURCE_NAME_2", + "RESOURCE_NAME_3","RESOURCE_TYPE_0","RESOURCE_TYPE_1","RESOURCE_TYPE_2","RESOURCE_TYPE_3","ACTION","WITH_GRANT_OPTION"); + +CREATE INDEX "SENTRY_GM_PRIV_COMP_IDX" ON "SENTRY_GM_PRIVILEGE" USING btree ("COMPONENT_NAME"); + +CREATE INDEX "SENTRY_GM_PRIV_SERV_IDX" ON "SENTRY_GM_PRIVILEGE" USING btree ("SERVICE_NAME"); + +CREATE INDEX "SENTRY_GM_PRIV_RES0_IDX" ON "SENTRY_GM_PRIVILEGE" USING btree ("RESOURCE_NAME_0","RESOURCE_TYPE_0"); + +CREATE INDEX "SENTRY_GM_PRIV_RES1_IDX" ON "SENTRY_GM_PRIVILEGE" USING btree ("RESOURCE_NAME_1","RESOURCE_TYPE_1"); + +CREATE INDEX "SENTRY_GM_PRIV_RES2_IDX" ON "SENTRY_GM_PRIVILEGE" USING btree ("RESOURCE_NAME_2","RESOURCE_TYPE_2"); + +CREATE INDEX "SENTRY_GM_PRIV_RES3_IDX" ON "SENTRY_GM_PRIVILEGE" USING btree ("RESOURCE_NAME_3","RESOURCE_TYPE_3"); + +-- Table SENTRY_ROLE_GM_PRIVILEGE_MAP for join relationship +CREATE TABLE "SENTRY_ROLE_GM_PRIVILEGE_MAP" ( + "ROLE_ID" BIGINT NOT NULL, + "GM_PRIVILEGE_ID" BIGINT NOT NULL +); + +ALTER TABLE "SENTRY_ROLE_GM_PRIVILEGE_MAP" + ADD CONSTRAINT "SENTRY_ROLE_GM_PRIVILEGE_MAP_PK" PRIMARY KEY ("ROLE_ID","GM_PRIVILEGE_ID"); + +-- Constraints for table SENTRY_ROLE_GM_PRIVILEGE_MAP +ALTER TABLE ONLY "SENTRY_ROLE_GM_PRIVILEGE_MAP" + ADD CONSTRAINT "SEN_RLE_GM_PRV_MAP_SN_RLE_FK" + FOREIGN KEY ("ROLE_ID") REFERENCES "SENTRY_ROLE"("ROLE_ID") DEFERRABLE; + +ALTER TABLE ONLY "SENTRY_ROLE_GM_PRIVILEGE_MAP" + ADD CONSTRAINT "SEN_RL_GM_PRV_MAP_SN_DB_PRV_FK" + FOREIGN KEY ("GM_PRIVILEGE_ID") REFERENCES "SENTRY_GM_PRIVILEGE"("GM_PRIVILEGE_ID") DEFERRABLE; http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/resources/006-SENTRY-711.derby.sql ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/resources/006-SENTRY-711.derby.sql b/sentry-service/sentry-service-server/src/main/resources/006-SENTRY-711.derby.sql new file mode 100644 index 0000000..807a62b --- /dev/null +++ b/sentry-service/sentry-service-server/src/main/resources/006-SENTRY-711.derby.sql @@ -0,0 +1,27 @@ +CREATE TABLE SENTRY_USER +( + USER_ID BIGINT NOT NULL generated always as identity (start with 1), + CREATE_TIME BIGINT NOT NULL, + USER_NAME VARCHAR(128) +); + +ALTER TABLE SENTRY_USER ADD CONSTRAINT SENTRY_USER_PK PRIMARY KEY (USER_ID); + +CREATE UNIQUE INDEX SENTRYUSERNAME ON SENTRY_USER (USER_NAME); + +CREATE TABLE SENTRY_ROLE_USER_MAP +( + USER_ID BIGINT NOT NULL, + ROLE_ID BIGINT NOT NULL, + GRANTOR_PRINCIPAL VARCHAR(128) +); + +ALTER TABLE SENTRY_ROLE_USER_MAP ADD CONSTRAINT SENTRY_ROLE_USER_MAP_PK PRIMARY KEY (USER_ID,ROLE_ID); + +CREATE INDEX SENTRY_ROLE_USER_MAP_N49 ON SENTRY_ROLE_USER_MAP (USER_ID); + +CREATE INDEX SENTRY_ROLE_USER_MAP_N50 ON SENTRY_ROLE_USER_MAP (ROLE_ID); + +ALTER TABLE SENTRY_ROLE_USER_MAP ADD CONSTRAINT SENTRY_ROLE_USER_MAP_FK2 FOREIGN KEY (ROLE_ID) REFERENCES SENTRY_ROLE (ROLE_ID) ; + +ALTER TABLE SENTRY_ROLE_USER_MAP ADD CONSTRAINT SENTRY_ROLE_USER_MAP_FK1 FOREIGN KEY (USER_ID) REFERENCES SENTRY_USER (USER_ID) ; \ No newline at end of file
