You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by sh...@apache.org on 2014/03/13 22:21:17 UTC

[02/13] SENTRY-143: Merge db_policy_store branch into master (Brock Noland via Shreepadma Venugopalan)

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ConnectionDeniedException.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ConnectionDeniedException.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ConnectionDeniedException.java
new file mode 100644
index 0000000..02c5eb3
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ConnectionDeniedException.java
@@ -0,0 +1,36 @@
+/**
+ * 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 javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class ConnectionDeniedException extends UnsupportedCallbackException {
+
+  private static final long serialVersionUID = 653174214903923178L;
+  private String connectionPrincipal;
+
+  public ConnectionDeniedException(Callback callback, String message, String connectionPrincipal) {
+    super(callback, message);
+    this.connectionPrincipal = connectionPrincipal;
+  }
+
+  public String getConnectionPrincipal() {
+    return connectionPrincipal;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java
new file mode 100644
index 0000000..c4a0fd4
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/GSSCallback.java
@@ -0,0 +1,102 @@
+/**
+ * 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;
+
+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("sentry.service.allow.connect");
+    if (allowedPrincipals == null) {
+      return false;
+    }
+    List<String> items = Arrays.asList(allowedPrincipals.split("\\s*,\\s*"));
+    for (String item:items) {
+      if(comparePrincipals(item, principal)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @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/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java
new file mode 100644
index 0000000..3022f67
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/KerberosConfiguration.java
@@ -0,0 +1,78 @@
+/**
+ * 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 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 System.getProperty("java.vendor").contains("IBM")
+        ? "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>();
+    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 (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/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java
new file mode 100644
index 0000000..07b3472
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ProcessorFactory.java
@@ -0,0 +1,30 @@
+/**
+ * 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/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
new file mode 100644
index 0000000..fbb0eef
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
@@ -0,0 +1,272 @@
+/**
+ * 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.ServerSocket;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+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.sentry.Command;
+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.TThreadPoolServer;
+import org.apache.thrift.transport.TSaslServerTransport;
+import org.apache.thrift.transport.TServerSocket;
+import org.apache.thrift.transport.TServerTransport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+
+public class SentryService implements Runnable {
+
+  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 final String principal;
+  private final String[] principalParts;
+  private final String keytab;
+  private final ExecutorService serviceExecutor;
+
+  private TServer thriftServer;
+  private Status status;
+
+  public SentryService(Configuration conf) {
+    this.conf = conf;
+    int port = conf
+        .getInt(ServerConfig.RPC_PORT, ServerConfig.RPC_PORT_DEFAULT);
+    if (port == 0) {
+      port = findFreePort();
+    }
+    this.address = NetUtils.createSocketAddr(
+        conf.get(ServerConfig.RPC_ADDRESS, ServerConfig.RPC_ADDRESS_DEFAULT),
+        port);
+    LOGGER.info("Configured on address " + address);
+    maxThreads = conf.getInt(ServerConfig.RPC_MAX_THREADS,
+        ServerConfig.RPC_MAX_THREADS_DEFAULT);
+    minThreads = conf.getInt(ServerConfig.RPC_MIN_THREADS,
+        ServerConfig.RPC_MIN_THREADS_DEFAULT);
+    principal = Preconditions.checkNotNull(conf.get(ServerConfig.PRINCIPAL),
+        ServerConfig.PRINCIPAL + " is required");
+    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.");
+    serviceExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+      private int count = 0;
+
+      @Override
+      public Thread newThread(Runnable r) {
+        return new Thread(r, SentryService.class.getSimpleName() + "-"
+            + (count++));
+      }
+    });
+    status = Status.NOT_STARTED;
+  }
+
+  @Override
+  public void run() {
+    LoginContext loginContext = null;
+    try {
+      Subject subject = new Subject(false,
+          Sets.newHashSet(new KerberosPrincipal(principal)),
+          new HashSet<Object>(), new HashSet<Object>());
+      loginContext = new LoginContext("", subject, null,
+          KerberosConfiguration.createClientConfig(principal, new File(keytab)));
+      loginContext.login();
+      subject = loginContext.getSubject();
+      Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
+        @Override
+        public Void run() 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);
+              ProcessorFactory factory = (ProcessorFactory) constructor
+                  .newInstance(conf);
+              registeredProcessor = registeredProcessor
+                  || factory.register(processor);
+            } 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);
+          TSaslServerTransport.Factory saslTransportFactory = new TSaslServerTransport.Factory();
+          saslTransportFactory.addServerDefinition(AuthMethod.KERBEROS
+              .getMechanismName(), principalParts[0], principalParts[1],
+              ServerConfig.SASL_PROPERTIES, new GSSCallback(conf));
+          TThreadPoolServer.Args args = new TThreadPoolServer.Args(
+              serverTransport).processor(processor)
+              .transportFactory(saslTransportFactory)
+              .protocolFactory(new TBinaryProtocol.Factory())
+              .minWorkerThreads(minThreads).maxWorkerThreads(maxThreads);
+          thriftServer = new TThreadPoolServer(args);
+          LOGGER.info("Serving on " + address);
+          thriftServer.serve();
+          return null;
+        }
+      });
+    } catch (Throwable t) {
+      LOGGER.error("Error starting server", t);
+    } finally {
+      status = Status.NOT_STARTED;
+      if (loginContext != null) {
+        try {
+          loginContext.logout();
+        } catch (LoginException e) {
+          LOGGER.error("Error logging out", e);
+        }
+      }
+    }
+  }
+
+  public InetSocketAddress getAddress() {
+    return address;
+  }
+
+  public synchronized boolean isRunning() {
+    return status == Status.STARTED && thriftServer != null
+        && thriftServer.isServing();
+  }
+
+  public synchronized void start() {
+    if (status != Status.NOT_STARTED) {
+      throw new IllegalStateException("Cannot start when " + status);
+    }
+    LOGGER.info("Attempting to start...");
+    status = Status.STARTED;
+    serviceExecutor.submit(this);
+  }
+
+  public synchronized void stop() {
+    if (status == Status.NOT_STARTED) {
+      return;
+    }
+    LOGGER.info("Attempting to stop...");
+
+    if (thriftServer.isServing()) {
+      thriftServer.stop();
+    }
+    thriftServer = null;
+    status = Status.NOT_STARTED;
+    LOGGER.info("Stopped...");
+  }
+
+  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 class CommandImpl implements Command {
+    @Override
+    @SuppressWarnings("deprecation")
+    public void run(String[] args) throws Exception {
+      CommandLineParser parser = new GnuParser();
+      Options options = new Options();
+      options.addOption(null, ServiceConstants.ServiceArgs.CONFIG_FILE,
+          true, "Sentry Service configuration file");
+      CommandLine commandLine = parser.parse(options, args);
+      String configFileName = commandLine.getOptionValue(ServiceConstants.
+          ServiceArgs.CONFIG_FILE);
+      File configFile = null;
+      if (configFileName == null) {
+        throw new IllegalArgumentException("Usage: " + ServiceConstants.ServiceArgs.CONFIG_FILE +
+            " 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.toURL());
+      final SentryService server = new SentryService(conf);
+      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);
+          }
+        }
+      });
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java
new file mode 100644
index 0000000..11545a5
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java
@@ -0,0 +1,30 @@
+/**
+ * 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.sentry.provider.db.service.thrift.SentryPolicyServiceClient;
+
+public class SentryServiceClientFactory {
+
+  public SentryPolicyServiceClient create(Configuration conf) throws Exception {
+    SentryPolicyServiceClient client = new SentryPolicyServiceClient(conf);
+    return client;
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java
new file mode 100644
index 0000000..bd7e447
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryServiceFactory.java
@@ -0,0 +1,29 @@
+/**
+ * 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 {
+    SentryService server = new SentryService(conf);
+    return server;
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
new file mode 100644
index 0000000..253f88e
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
@@ -0,0 +1,78 @@
+/**
+ * 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.HashMap;
+import java.util.Map;
+
+import javax.security.sasl.Sasl;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+
+public class ServiceConstants {
+
+  private static final ImmutableMap<String, String> SASL_PROPERTIES;
+
+  static {
+    Map<String, String> saslProps = new HashMap<String, String>();
+    saslProps.put(Sasl.SERVER_AUTH, "true");
+    saslProps.put(Sasl.QOP, "auth-conf");
+    SASL_PROPERTIES = ImmutableMap.copyOf(saslProps);
+  }
+
+  public static class ConfUtilties {
+    public static final Splitter CLASS_SPLITTER = Splitter.onPattern("[\\s,]")
+        .trimResults().omitEmptyStrings();
+  }
+  public static class ServiceArgs {
+    public static final String CONFIG_FILE = "--conf-file";
+  }
+  public static class ServerConfig {
+    public static final ImmutableMap<String, String> SASL_PROPERTIES = ServiceConstants.SASL_PROPERTIES;
+    public static final String PRINCIPAL = "sentry.service.server.principal";
+    public static final String KEY_TAB = "sentry.service.server.keytab";
+    public static final String RPC_PORT = "sentry.service.server.rpc-port";
+    public static final int RPC_PORT_DEFAULT = 8038;
+    public static final String RPC_ADDRESS = "sentry.service.server.rpc-address";
+    public static final String RPC_ADDRESS_DEFAULT = "0.0.0.0";
+    public static final String RPC_MAX_THREADS = "sentry.service.server-max-threads";
+    public static final int RPC_MAX_THREADS_DEFAULT = 500;
+    public static final String RPC_MIN_THREADS = "sentry.service.server-min-threads";
+    public static final int RPC_MIN_THREADS_DEFAULT = 10;
+    public static final String ALLOW_CONNECT = "sentry.service.allow.connect";
+    public static final String PROCESSOR_FACTORIES = "sentry.service.processor.factories";
+    public static final String PROCESSOR_FACTORIES_DEFAULT =
+        "org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessorFactory";
+  }
+  public static class ClientConfig {
+    public static final ImmutableMap<String, String> SASL_PROPERTIES = ServiceConstants.SASL_PROPERTIES;
+    public static final String SERVER_RPC_PORT = "sentry.service.client.server.rpc-port";
+    public static final int SERVER_RPC_PORT_DEFAULT = ServerConfig.RPC_PORT_DEFAULT;
+    public static final String SERVER_RPC_ADDRESS = "sentry.service.client.server.rpc-address";
+    public static final String SERVER_RPC_CONN_TIMEOUT = "sentry.service.client.server.rpc-connection-timeout";
+    public static final int SERVER_RPC_CONN_TIMEOUT_DEFAULT = 200000;
+  }
+
+  /**
+   * Thrift generates terrible constant class names
+   */
+  public static class ThriftConstants extends org.apache.sentry.service.thrift.sentry_common_serviceConstants {
+    public static final int TSENTRY_SERVICE_VERSION_CURRENT = TSENTRY_SERVICE_V1;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/Status.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/Status.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/Status.java
new file mode 100644
index 0000000..1686780
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/Status.java
@@ -0,0 +1,84 @@
+/**
+ * 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.PrintWriter;
+import java.io.StringWriter;
+
+import javax.annotation.Nullable;
+
+import org.apache.sentry.service.thrift.ServiceConstants.ThriftConstants;
+
+/**
+ * Simple factory to make returning TSentryStatus objects easy
+ */
+public enum Status {
+  OK(ThriftConstants.TSENTRY_STATUS_OK),
+  ALREADY_EXISTS(ThriftConstants.TSENTRY_STATUS_ALREADY_EXISTS),
+  NO_SUCH_OBJECT(ThriftConstants.TSENTRY_STATUS_NO_SUCH_OBJECT),
+  RUNTIME_ERROR(ThriftConstants.TSENTRY_STATUS_RUNTIME_ERROR),
+  INVALID_INPUT(ThriftConstants.TSENTRY_STATUS_INVALID_INPUT),
+  UNKNOWN(-1)
+  ;
+  private int code;
+  private Status(int code) {
+    this.code = code;
+  }
+  public int getCode() {
+    return code;
+  }
+  public static Status fromCode(int code) {
+    for (Status status : Status.values()) {
+      if (status.getCode() == code) {
+        return status;
+      }
+    }
+    return Status.UNKNOWN;
+  }
+  public static TSentryResponseStatus OK() {
+    return Create(Status.OK, "");
+  }
+  public static TSentryResponseStatus AlreadyExists(String message, Throwable t) {
+    return Create(Status.ALREADY_EXISTS, message, t);
+  }
+  public static TSentryResponseStatus NoSuchObject(String message, Throwable t) {
+    return Create(Status.NO_SUCH_OBJECT, message, t);
+  }
+  public static TSentryResponseStatus RuntimeError(String message, Throwable t) {
+    return Create(Status.RUNTIME_ERROR, message, t);
+  }
+  public static TSentryResponseStatus Create(Status value, String message) {
+    return Create(value, message, null);
+  }
+  public static TSentryResponseStatus InvalidInput(String message, Throwable t) {
+    return Create(Status.INVALID_INPUT, message, t);
+  }
+  public static TSentryResponseStatus Create(Status value, String message, @Nullable Throwable t) {
+    TSentryResponseStatus status = new TSentryResponseStatus();
+    status.setValue(value.getCode());
+    status.setMessage(message);
+    if (t != null) {
+      StringWriter stringWriter = new StringWriter();
+      PrintWriter printWriter = new PrintWriter(stringWriter);
+      t.printStackTrace(printWriter);
+      printWriter.close();
+      status.setStack(stringWriter.toString());
+    }
+    return status;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql
new file mode 100644
index 0000000..85d5085
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql
@@ -0,0 +1,113 @@
+--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.
+
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+CREATE TABLE `SENTRY_DB_PRIVILEGE` (
+  `DB_PRIVILEGE_ID` BIGINT NOT NULL,
+  `PRIVILEGE_NAME` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, -- Name of the privilege
+  `PRIVILEGE_SCOPE` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, -- Scope. Valid values are Server, Database, Table
+  `SERVER_NAME` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
+  `DATABASE_NAME` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
+  `TABLE_NAME` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
+  `URI` VARCHAR(4000) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
+  `PRIVILEGE` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, -- Allowed action. Valid values are ALL, INSERT, SELECT
+  `CREATE_TIME` BIGINT NOT NULL,
+  `GRANTOR_PRINCIPAL` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL -- principal of the creator
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `SENTRY_ROLE` (
+  `ROLE_ID` BIGINT  NOT NULL,
+  `ROLE_NAME` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
+  `CREATE_TIME` BIGINT NOT NULL,
+  `ROLE_OWNER` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `SENTRY_GROUP` (
+  `GROUP_ID` BIGINT  NOT NULL,
+  `GROUP_NAME` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
+  `CREATE_TIME` BIGINT NOT NULL,
+  `GRANTOR_PRINCIPAL` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `SENTRY_ROLE_DB_PRIVILEGE_MAP` (
+  `ROLE_PRIVILEGE_MAP_ID` BIGINT NOT NULL,
+  `ROLE_ID` BIGINT NOT NULL, -- FK to SENTRY_ROLE.ROLE_ID
+  `DB_PRIVILEGE_ID` BIGINT NOT NULL -- FK to SENTRY_DB_PRIVILEGE.DB_PRIVILEGE_ID
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `SENTRY_ROLE_GROUP_MAP` (
+  `ROLE_GROUP_MAP_ID` BIGINT NOT NULL,
+  `ROLE_ID` BIGINT NOT NULL, -- FK to SENTRY_ROLE.ROLE_ID
+  `GROUP_ID` BIGINT NOT NULL -- FK to SENTRY_GROUP.GROUP_ID
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `SENTRY_VERSION` (
+  `VER_ID` BIGINT NOT NULL,
+  `SCHEMA_VERSION` VARCHAR(127) NOT NULL,
+  `VERSION_COMMENT` VARCHAR(255) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE `SENTRY_DB_PRIVILEGE`
+  ADD CONSTRAINT `SENTRY_DB_PRIV_PK` PRIMARY KEY (`DB_PRIVILEGE_ID`);
+
+ALTER TABLE `SENTRY_ROLE`
+  ADD CONSTRAINT `SENTRY_ROLE_PK` PRIMARY KEY (`ROLE_ID`);
+
+ALTER TABLE `SENTRY_GROUP`
+  ADD CONSTRAINT `SENTRY_GROUP_PK` PRIMARY KEY (`GROUP_ID`);
+
+ALTER TABLE `SENTRY_ROLE_DB_PRIVILEGE_MAP`
+  ADD CONSTRAINT `SENTRY_ROLE_DB_PRIV_MAP_PK` PRIMARY KEY (`ROLE_PRIVILEGE_MAP_ID`);
+
+ALTER TABLE `SENTRY_ROLE_GROUP_MAP`
+  ADD CONSTRAINT `SENTRY_ROLE_GROUP_MAP_PK` PRIMARY KEY (`ROLE_GROUP_MAP_ID`);
+
+ALTER TABLE `SENTRY_VERSION`
+  ADD CONSTRAINT `SENTRY_VERSION` PRIMARY KEY (`VER_ID`);
+
+ALTER TABLE `SENTRY_DB_PRIVILEGE`
+  ADD CONSTRAINT `SENTRY_DB_PRIV_PRIV_NAME_UNIQ` UNIQUE (`PRIVILEGE_NAME`);
+
+ALTER TABLE `SENTRY_ROLE`
+  ADD CONSTRAINT `SENTRY_ROLE_ROLE_NAME_UNIQUE` UNIQUE (`ROLE_NAME`);
+
+ALTER TABLE `SENTRY_ROLE_DB_PRIVILEGE_MAP`
+  ADD CONSTRAINT `SEN_RLE_DB_PRV_MAP_SN_RLE_FK`
+  FOREIGN KEY (`ROLE_ID`) REFERENCES `SENTRY_ROLE`(`ROLE_ID`);
+
+ALTER TABLE `SENTRY_ROLE_DB_PRIVILEGE_MAP`
+  ADD CONSTRAINT `SEN_RL_DB_PRV_MAP_SN_DB_PRV_FK`
+  FOREIGN KEY (`DB_PRIVILEGE_ID`) REFERENCES `SENTRY_DB_PRIVILEGE`(`DB_PRIVILEGE_ID`);
+
+ALTER TABLE `SENTRY_ROLE_GROUP_MAP`
+  ADD CONSTRAINT `SEN_ROLE_GROUP_MAP_SEN_ROLE_FK`
+  FOREIGN KEY (`ROLE_ID`) REFERENCES `SENTRY_ROLE`(`ROLE_ID`);
+
+ALTER TABLE `SENTRY_ROLE_GROUP_MAP`
+  ADD CONSTRAINT `SEN_ROLE_GROUP_MAP_SEN_GRP_FK`
+  FOREIGN KEY (`GROUP_ID`) REFERENCES `SENTRY_GROUP`(`GROUP_ID`);
+
+INSERT INTO SENTRY_VERSION (VER_ID, SCHEMA_VERSION, VERSION_COMMENT) VALUES (1, '1.4.0', 'Sentry release version 1.4.0');

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql
new file mode 100644
index 0000000..0508d45
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql
@@ -0,0 +1,101 @@
+--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.
+
+CREATE TABLE "SENTRY_DB_PRIVILEGE" (
+  "DB_PRIVILEGE_ID" NUMBER NOT NULL,
+  "PRIVILEGE_NAME" VARCHAR2(128) NOT NULL, -- Name of the privilege
+  "PRIVILEGE_SCOPE" VARCHAR2(32) NOT NULL, -- Scope. Valid values are Server, Database, Table
+  "SERVER_NAME" VARCHAR2(128) NOT NULL,
+  "DATABASE_NAME" VARCHAR2(128) NULL,
+  "TABLE_NAME" VARCHAR2(128) NULL,
+  "URI" VARCHAR2(4000) NULL,
+  "PRIVILEGE" VARCHAR2(128) NOT NULL, -- Allowed action. Valid values are ALL, INSERT, SELECT
+  "CREATE_TIME" NUMBER NOT NULL,
+  "GRANTOR_PRINCIPAL" VARCHAR(128) NOT NULL -- principal of the creator
+);
+
+CREATE TABLE "SENTRY_ROLE" (
+  "ROLE_ID" NUMBER  NOT NULL,
+  "ROLE_NAME" VARCHAR2(128) NOT NULL,
+  "CREATE_TIME" NUMBER NOT NULL,
+  "ROLE_OWNER" VARCHAR2(128) NOT NULL
+);
+
+CREATE TABLE "SENTRY_GROUP" (
+  "GROUP_ID" NUMBER  NOT NULL,
+  "GROUP_NAME" VARCHAR2(128) NOT NULL,
+  "CREATE_TIME" NUMBER NOT NULL,
+  "GRANTOR_PRINCIPAL" VARCHAR2(128) NOT NULL
+);
+
+CREATE TABLE "SENTRY_ROLE_DB_PRIVILEGE_MAP" (
+  "ROLE_PRIVILEGE_MAP_ID" NUMBER NOT NULL,
+  "ROLE_ID" NUMBER NOT NULL, -- FK to SENTRY_ROLE.ROLE_ID
+  "DB_PRIVILEGE_ID" NUMBER NOT NULL -- FK to SENTRY_DB_PRIVILEGE.DB_PRIVILEGE_ID
+);
+
+CREATE TABLE "SENTRY_ROLE_GROUP_MAP" (
+  "ROLE_GROUP_MAP_ID" NUMBER NOT NULL,
+  "ROLE_ID" NUMBER NOT NULL, -- FK to SENTRY_ROLE.ROLE_ID
+  "GROUP_ID" NUMBER NOT NULL -- FK to SENTRY_GROUP.GROUP_ID
+);
+
+CREATE TABLE "SENTRY_VERSION" (
+  "VER_ID" NUMBER NOT NULL,
+  "SCHEMA_VERSION" VARCHAR(127) NOT NULL,
+  "VERSION_COMMENT" VARCHAR(255) NOT NULL
+);
+
+ALTER TABLE "SENTRY_DB_PRIVILEGE"
+  ADD CONSTRAINT "SENTRY_DB_PRIV_PK" PRIMARY KEY ("DB_PRIVILEGE_ID");
+
+ALTER TABLE "SENTRY_ROLE"
+  ADD CONSTRAINT "SENTRY_ROLE_PK" PRIMARY KEY ("ROLE_ID");
+
+ALTER TABLE "SENTRY_GROUP"
+  ADD CONSTRAINT "SENTRY_GROUP_PK" PRIMARY KEY ("GROUP_ID");
+
+ALTER TABLE "SENTRY_ROLE_DB_PRIVILEGE_MAP"
+  ADD CONSTRAINT "SENTRY_ROLE_DB_PRIV_MAP_PK" PRIMARY KEY ("ROLE_PRIVILEGE_MAP_ID");
+
+ALTER TABLE "SENTRY_ROLE_GROUP_MAP"
+  ADD CONSTRAINT "SENTRY_ROLE_GROUP_MAP_PK" PRIMARY KEY ("ROLE_GROUP_MAP_ID");
+
+ALTER TABLE "SENTRY_VERSION" ADD CONSTRAINT "SENTRY_VERSION_PK" PRIMARY KEY ("VER_ID");
+
+ALTER TABLE "SENTRY_DB_PRIVILEGE"
+  ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("PRIVILEGE_NAME");
+
+ALTER TABLE "SENTRY_ROLE"
+  ADD CONSTRAINT "SENTRY_ROLE_ROLE_NAME_UNIQUE" UNIQUE ("ROLE_NAME");
+
+ALTER TABLE "SENTRY_ROLE_DB_PRIVILEGE_MAP"
+  ADD CONSTRAINT "SEN_RLE_DB_PRV_MAP_SN_RLE_FK"
+  FOREIGN KEY ("ROLE_ID") REFERENCES "SENTRY_ROLE"("ROLE_ID") INITIALLY DEFERRED;
+
+ALTER TABLE "SENTRY_ROLE_DB_PRIVILEGE_MAP"
+  ADD CONSTRAINT "SEN_RL_DB_PRV_MAP_SN_DB_PRV_FK"
+  FOREIGN KEY ("DB_PRIVILEGE_ID") REFERENCES "SENTRY_DB_PRIVILEGE"("DB_PRIVILEGE_ID") INITIALLY DEFERRED;
+
+ALTER TABLE "SENTRY_ROLE_GROUP_MAP"
+  ADD CONSTRAINT "SEN_ROLE_GROUP_MAP_SEN_ROLE_FK"
+  FOREIGN KEY ("ROLE_ID") REFERENCES "SENTRY_ROLE"("ROLE_ID") INITIALLY DEFERRED;
+
+ALTER TABLE "SENTRY_ROLE_GROUP_MAP"
+  ADD CONSTRAINT "SEN_ROLE_GROUP_MAP_SEN_GRP_FK"
+  FOREIGN KEY ("GROUP_ID") REFERENCES "SENTRY_GROUP"("GROUP_ID") INITIALLY DEFERRED;
+
+INSERT INTO VERSION (VER_ID, SCHEMA_VERSION, VERSION_COMMENT) VALUES (1, '1.4.0', 'Sentry release version 1.4.0');
+

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql
new file mode 100644
index 0000000..7298923
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql
@@ -0,0 +1,115 @@
+--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.
+
+START TRANSACTION;
+
+SET statement_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = off;
+SET check_function_bodies = false;
+SET client_min_messages = warning;
+SET escape_string_warning = off;
+SET search_path = public, pg_catalog;
+SET default_tablespace = '';
+SET default_with_oids = false;
+
+CREATE TABLE "SENTRY_DB_PRIVILEGE" (
+  "DB_PRIVILEGE_ID" BIGINT NOT NULL,
+  "PRIVILEGE_NAME" character varying(128) NOT NULL, -- Name of the privilege
+  "PRIVILEGE_SCOPE" character varying(32) NOT NULL, -- Scope. Valid values are Server, Database, Table
+  "SERVER_NAME" character varying(128) NOT NULL,
+  "DATABASE_NAME" character varying(128) DEFAULT NULL::character varying,
+  "TABLE_NAME" character varying(128) DEFAULT NULL::character varying,
+  "URI" character varying(4000) DEFAULT NULL::character varying,
+  "PRIVILEGE" character varying(128) NOT NULL, -- Allowed action. Valid values are ALL, INSERT, SELECT
+  "CREATE_TIME" BIGINT NOT NULL,
+  "GRANTOR_PRINCIPAL" VARCHAR(128) NOT NULL -- principal of the creator
+);
+
+CREATE TABLE "SENTRY_ROLE" (
+  "ROLE_ID" BIGINT  NOT NULL,
+  "ROLE_NAME" character varying(128) NOT NULL,
+  "CREATE_TIME" BIGINT NOT NULL,
+  "ROLE_OWNER" character varying(128) NOT NULL
+);
+
+CREATE TABLE "SENTRY_GROUP" (
+  "GROUP_ID" BIGINT  NOT NULL,
+  "GROUP_NAME" character varying(128) NOT NULL,
+  "CREATE_TIME" BIGINT NOT NULL,
+  "GRANTOR_PRINCIPAL" character varying(128) NOT NULL
+);
+
+CREATE TABLE "SENTRY_ROLE_DB_PRIVILEGE_MAP" (
+  "ROLE_PRIVILEGE_MAP_ID" BIGINT NOT NULL,
+  "ROLE_ID" BIGINT NOT NULL, -- FK to SENTRY_ROLE.ROLE_ID
+  "DB_PRIVILEGE_ID" BIGINT NOT NULL -- FK to SENTRY_DB_PRIVILEGE.DB_PRIVILEGE_ID
+);
+
+CREATE TABLE "SENTRY_ROLE_GROUP_MAP" (
+  "ROLE_GROUP_MAP_ID" BIGINT NOT NULL,
+  "ROLE_ID" BIGINT NOT NULL, -- FK to SENTRY_ROLE.ROLE_ID
+  "GROUP_ID" BIGINT NOT NULL -- FK to SENTRY_GROUP.GROUP_ID
+);
+
+CREATE TABLE "SENTRY_VERSION" (
+  "VER_ID" bigint,
+  "SCHEMA_VERSION" character varying(127) NOT NULL,
+  "VERSION_COMMENT" character varying(255) NOT NULL
+);
+
+
+ALTER TABLE ONLY "SENTRY_DB_PRIVILEGE"
+  ADD CONSTRAINT "SENTRY_DB_PRIV_PK" PRIMARY KEY ("DB_PRIVILEGE_ID");
+
+ALTER TABLE ONLY "SENTRY_ROLE"
+  ADD CONSTRAINT "SENTRY_ROLE_PK" PRIMARY KEY ("ROLE_ID");
+
+ALTER TABLE ONLY "SENTRY_GROUP"
+  ADD CONSTRAINT "SENTRY_GROUP_PK" PRIMARY KEY ("GROUP_ID");
+
+ALTER TABLE ONLY "SENTRY_ROLE_DB_PRIVILEGE_MAP"
+  ADD CONSTRAINT "SENTRY_ROLE_DB_PRIV_MAP_PK" PRIMARY KEY ("ROLE_PRIVILEGE_MAP_ID");
+
+ALTER TABLE ONLY "SENTRY_ROLE_GROUP_MAP"
+  ADD CONSTRAINT "SENTRY_ROLE_GROUP_MAP_PK" PRIMARY KEY ("ROLE_GROUP_MAP_ID");
+
+ALTER TABLE ONLY "SENTRY_VERSION" ADD CONSTRAINT "SENTRY_VERSION_PK" PRIMARY KEY ("VER_ID");
+
+ALTER TABLE ONLY "SENTRY_DB_PRIVILEGE"
+  ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("PRIVILEGE_NAME");
+
+ALTER TABLE ONLY "SENTRY_ROLE"
+  ADD CONSTRAINT "SENTRY_ROLE_ROLE_NAME_UNIQUE" UNIQUE ("ROLE_NAME");
+
+ALTER TABLE ONLY "SENTRY_ROLE_DB_PRIVILEGE_MAP"
+  ADD CONSTRAINT "SEN_RLE_DB_PRV_MAP_SN_RLE_FK"
+  FOREIGN KEY ("ROLE_ID") REFERENCES "SENTRY_ROLE"("ROLE_ID") DEFERRABLE;
+
+ALTER TABLE ONLY "SENTRY_ROLE_DB_PRIVILEGE_MAP"
+  ADD CONSTRAINT "SEN_RL_DB_PRV_MAP_SN_DB_PRV_FK"
+  FOREIGN KEY ("DB_PRIVILEGE_ID") REFERENCES "SENTRY_DB_PRIVILEGE"("DB_PRIVILEGE_ID") DEFERRABLE;
+
+ALTER TABLE ONLY "SENTRY_ROLE_GROUP_MAP"
+  ADD CONSTRAINT "SEN_ROLE_GROUP_MAP_SEN_ROLE_FK"
+  FOREIGN KEY ("ROLE_ID") REFERENCES "SENTRY_ROLE"("ROLE_ID") DEFERRABLE;
+
+ALTER TABLE ONLY "SENTRY_ROLE_GROUP_MAP"
+  ADD CONSTRAINT "SEN_ROLE_GROUP_MAP_SEN_GRP_FK"
+  FOREIGN KEY ("GROUP_ID") REFERENCES "SENTRY_GROUP"("GROUP_ID") DEFERRABLE;
+
+INSERT INTO "SENTRY_VERSION" ("VER_ID", "SCHEMA_VERSION", "VERSION_COMMENT") VALUES (1, '1.4.0', 'Sentry release version 1.4.0');
+
+COMMIT;

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/resources/sentry_common_service.thrift
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry_common_service.thrift b/sentry-provider/sentry-provider-db/src/main/resources/sentry_common_service.thrift
new file mode 100644
index 0000000..7a545be
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry_common_service.thrift
@@ -0,0 +1,41 @@
+#!/usr/local/bin/thrift -java
+
+/**
+ * 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.
+ */
+
+include "share/fb303/if/fb303.thrift"
+
+namespace java org.apache.sentry.service.thrift
+namespace php sentry.service.thrift
+namespace cpp Apache.Sentry.Service.Thrift
+
+const i32 TSENTRY_SERVICE_V1 = 1;
+
+const i32 TSENTRY_STATUS_OK = 0;
+const i32 TSENTRY_STATUS_ALREADY_EXISTS = 1;
+const i32 TSENTRY_STATUS_NO_SUCH_OBJECT = 2;
+const i32 TSENTRY_STATUS_RUNTIME_ERROR = 3;
+const i32 TSENTRY_STATUS_INVALID_INPUT = 4;
+
+struct TSentryResponseStatus {
+1: required i32 value,
+// message will be set to empty string when status is OK
+2: required string message
+3: optional string stack
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
new file mode 100644
index 0000000..b3f7d6e
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
@@ -0,0 +1,150 @@
+#!/usr/local/bin/thrift -java
+
+/**
+ * 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.
+ */
+
+#
+# Thrift Service that the MetaStore is built on
+#
+
+include "share/fb303/if/fb303.thrift"
+include "sentry_common_service.thrift"
+
+namespace java org.apache.sentry.provider.db.service.thrift
+namespace php sentry.provider.db.service.thrift
+namespace cpp Apache.Sentry.Provider.Db.Service.Thrift
+
+struct TSentryPrivilege {
+1: required string privilegeScope, # Valid values are SERVER, DATABASE, TABLE
+2: optional string privilegeName, # Generated on server side
+3: required string serverName,
+4: optional string dbName,
+5: optional string tableName,
+6: optional string URI,
+7: required string action,
+8: optional i64 createTime, # Set on server side
+9: optional string grantorPrincipal # Set on server side
+}
+
+struct TSentryRole {
+1: required string roleName,
+# TODO privs should not be part of Sentry role as
+# they are created when a grant is executed
+# They need to be returned as part of the list role API, else
+# there would be another round trip
+2: required set<TSentryPrivilege> privileges,
+3: required i64 createTime,
+4: required string grantorPrincipal
+}
+
+// TODO fill out
+struct TSentryGroup {
+1: required string groupName
+}
+
+struct TCreateSentryRoleRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName,
+3: required TSentryRole role,
+4: required set<string> requestorGroupName
+}
+struct TCreateSentryRoleResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
+struct TListSentryRolesRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName, # user on whose behalf the request is issued
+3: optional string rolerequestorGroupName, # list roles for this group
+4: required string roleName,
+5: required set<string> requestorGroupName # groups the requesting user belongs to
+}
+struct TListSentryRolesResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+2: required set<TSentryRole> roles
+}
+
+struct TDropSentryRoleRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName,
+3: required string roleName,
+4: required set<string> requestorGroupName
+}
+struct TDropSentryRoleResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
+struct TAlterSentryRoleAddGroupsRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName,
+3: required string roleName,
+4: required set<string> requestorGroupName,
+5: required set<TSentryGroup> groups
+}
+
+struct TAlterSentryRoleAddGroupsResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
+struct TAlterSentryRoleDeleteGroupsRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName,
+3: required set<string> requestorGroupName
+}
+struct TAlterSentryRoleDeleteGroupsResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
+struct TAlterSentryRoleGrantPrivilegeRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName,
+3: required string roleName,
+4: required set<string> requestorGroupName,
+5: required TSentryPrivilege privilege
+}
+
+struct TAlterSentryRoleGrantPrivilegeResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
+struct TAlterSentryRoleRevokePrivilegeRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName,
+3: required string roleName,
+4: required set<string> requestorGroupName,
+5: required TSentryPrivilege privilege
+}
+
+struct TAlterSentryRoleRevokePrivilegeResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
+service SentryPolicyService
+{
+  TCreateSentryRoleResponse create_sentry_role(1:TCreateSentryRoleRequest request)
+  TDropSentryRoleResponse drop_sentry_role(1:TDropSentryRoleRequest request)
+  
+  TAlterSentryRoleGrantPrivilegeResponse alter_sentry_role_grant_privilege(1:TAlterSentryRoleGrantPrivilegeRequest request)
+  TAlterSentryRoleRevokePrivilegeResponse alter_sentry_role_revoke_privilege(1:TAlterSentryRoleRevokePrivilegeRequest request)
+  
+  TAlterSentryRoleAddGroupsResponse alter_sentry_role_add_groups(1:TAlterSentryRoleAddGroupsRequest request)
+  TAlterSentryRoleDeleteGroupsResponse alter_sentry_role_delete_groups(1:TAlterSentryRoleDeleteGroupsRequest request)
+
+  TListSentryRolesResponse list_sentry_roles_by_group(1:TListSentryRolesRequest request)
+  TListSentryRolesResponse list_sentry_roles_by_role_name(1:TListSentryRolesRequest request) 
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
new file mode 100644
index 0000000..be3d078
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
@@ -0,0 +1,145 @@
+/**
+ * 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.service.persistent;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.sentry.provider.db.service.model.MSentryPrivilege;
+import org.apache.sentry.provider.db.service.model.MSentryRole;
+import org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessor;
+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.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+public class TestSentryStore {
+
+  private static File dataDir;
+  private static SentryStore sentryStore;
+
+  @BeforeClass
+  public static void setup() throws Exception {
+    dataDir = new File(Files.createTempDir(), SentryStore.DEFAULT_DATA_DIR);
+    sentryStore = new SentryStore(dataDir.getPath());
+  }
+
+  @AfterClass
+  public static void teardown() {
+    if (sentryStore != null) {
+      sentryStore.stop();
+    }
+    if (dataDir != null) {
+      FileUtils.deleteQuietly(dataDir);
+    }
+  }
+
+  private static CommitContext createRole(String r, String g) throws Exception {
+    TSentryRole role = new TSentryRole();
+    role.setGrantorPrincipal(g);
+    role.setRoleName(r);
+    return sentryStore.createSentryRole(role);
+  }
+
+
+  @Test
+  public void testCreateDuplicateRole() throws Exception {
+    String roleName = "test-dup-role";
+    String grantor = "g1";
+    createRole(roleName, grantor);
+    try {
+      createRole(roleName, grantor);
+      fail("Expected SentryAlreadyExistsException");
+    } catch(SentryAlreadyExistsException e) {
+      // expected
+    }
+  }
+
+  @Test
+  public void testCreateDropRole() throws Exception {
+    String roleName = "test-drop-role";
+    String grantor = "g1";
+    long seqId = createRole(roleName, grantor).getSequenceId();
+    assertEquals(seqId + 1, sentryStore.dropSentryRole(roleName).getSequenceId());
+  }
+
+  @Test(expected = SentryNoSuchObjectException.class)
+  public void testAddDeleteGroupsNonExistantRole()
+      throws Exception {
+    String roleName = "non-existant-role";
+    String grantor = "g1";
+    Set<TSentryGroup> groups = Sets.newHashSet();
+    sentryStore.alterSentryRoleAddGroups(grantor, roleName, groups);
+  }
+
+  @Test
+  public void testAddDeleteGroups() throws Exception {
+    String roleName = "test-groups";
+    String grantor = "g1";
+    long seqId = createRole(roleName, grantor).getSequenceId();
+    Set<TSentryGroup> groups = Sets.newHashSet();
+    TSentryGroup group = new TSentryGroup();
+    group.setGroupName("test-groups-g1");
+    groups.add(group);
+    group = new TSentryGroup();
+    group.setGroupName("test-groups-g2");
+    groups.add(group);
+    assertEquals(seqId + 1, sentryStore.alterSentryRoleAddGroups(grantor,
+        roleName, groups).getSequenceId());
+    assertEquals(seqId + 2, sentryStore.alterSentryRoleDeleteGroups(roleName, groups)
+        .getSequenceId());
+    MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
+    assertEquals(Collections.emptySet(), role.getGroups());
+  }
+
+  @Test
+  public void testGrantRevokePrivilege() throws Exception {
+    String roleName = "test-privilege";
+    String grantor = "g1";
+    long seqId = createRole(roleName, grantor).getSequenceId();
+    TSentryPrivilege privilege = new TSentryPrivilege();
+    privilege.setPrivilegeScope("TABLE");
+    privilege.setServerName("server1");
+    privilege.setDbName("db1");
+    privilege.setTableName("tbl1");
+    privilege.setAction("SELECT");
+    privilege.setGrantorPrincipal(grantor);
+    privilege.setCreateTime(System.currentTimeMillis());
+    privilege.setPrivilegeName(SentryPolicyStoreProcessor.constructPrivilegeName(privilege));
+    assertEquals(seqId + 1, sentryStore.alterSentryRoleGrantPrivilege(roleName, privilege)
+        .getSequenceId());
+    MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
+    Set<MSentryPrivilege> privileges = role.getPrivileges();
+    assertEquals(privileges.toString(), 1, privileges.size());
+    assertEquals(privilege.getPrivilegeName(), Iterables.get(privileges, 0).getPrivilegeName());
+    assertEquals(seqId + 2, sentryStore.alterSentryRoleRevokePrivilege(roleName, privilege.getPrivilegeName())
+        .getSequenceId());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestNotificationHandlerInvoker.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestNotificationHandlerInvoker.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestNotificationHandlerInvoker.java
new file mode 100644
index 0000000..6a2f48f
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestNotificationHandlerInvoker.java
@@ -0,0 +1,112 @@
+/**
+ * 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.service.thrift;
+
+import java.util.UUID;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.provider.db.service.persistent.CommitContext;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.google.common.collect.Lists;
+
+public class TestNotificationHandlerInvoker {
+
+  private Configuration conf;
+  private CommitContext commitContext;
+  private NotificationHandler handler;
+  private NotificationHandlerInvoker invoker;
+
+  @Before
+  public void setup() throws Exception {
+    conf = new Configuration(false);
+    commitContext = new CommitContext(UUID.randomUUID(), 1L);
+    handler = Mockito.spy(new NotificationHandler(conf) {});
+    invoker = new NotificationHandlerInvoker(conf,
+        Lists.newArrayList(new ThrowingNotificationHandler(conf), handler));
+  }
+
+  @Test
+  public void testCreateSentryRole() throws Exception {
+    TCreateSentryRoleRequest request = new TCreateSentryRoleRequest();
+    TCreateSentryRoleResponse response = new TCreateSentryRoleResponse();
+    invoker.create_sentry_role(commitContext, request, response);
+    Mockito.verify(handler).create_sentry_role(commitContext,
+        request, response);
+  }
+
+  @Test
+  public void testDropSentryRole() throws Exception {
+    TDropSentryRoleRequest request = new TDropSentryRoleRequest();
+    TDropSentryRoleResponse response = new TDropSentryRoleResponse();
+    invoker.drop_sentry_role(commitContext, request, response);
+    Mockito.verify(handler).drop_sentry_role(commitContext,
+        request, response);
+  }
+
+
+
+  @Test
+  public void testAlterSentryRoleAddGroups() throws Exception {
+    TAlterSentryRoleAddGroupsRequest request = new TAlterSentryRoleAddGroupsRequest();
+    TAlterSentryRoleAddGroupsResponse response = new TAlterSentryRoleAddGroupsResponse();
+    invoker.alter_sentry_role_add_groups(commitContext, request, response);
+    Mockito.verify(handler).alter_sentry_role_add_groups(commitContext,
+        request, response);
+  }
+
+  @Test
+  public void testAlterSentryRoleDeleteGroups() throws Exception {
+    TAlterSentryRoleDeleteGroupsRequest request = new TAlterSentryRoleDeleteGroupsRequest();
+    TAlterSentryRoleDeleteGroupsResponse response = new TAlterSentryRoleDeleteGroupsResponse();
+    invoker.alter_sentry_role_delete_groups(commitContext, request, response);
+    Mockito.verify(handler).alter_sentry_role_delete_groups(commitContext,
+        request, response);
+  }
+
+  public static class ThrowingNotificationHandler extends NotificationHandler {
+    public ThrowingNotificationHandler(Configuration config) throws Exception {
+      super(config);
+    }
+    @Override
+    public void create_sentry_role(CommitContext args,
+                                   TCreateSentryRoleRequest request, TCreateSentryRoleResponse response) {
+      throw new RuntimeException();
+    }
+    public void drop_sentry_role(CommitContext context,
+                                 TDropSentryRoleRequest request,
+                                 TDropSentryRoleResponse response) {
+      throw new RuntimeException();
+    }
+    @Override
+    public void alter_sentry_role_add_groups(CommitContext args,
+        TAlterSentryRoleAddGroupsRequest request,
+        TAlterSentryRoleAddGroupsResponse response) {
+      throw new RuntimeException();
+    }
+    @Override
+    public void alter_sentry_role_delete_groups(
+      CommitContext args, TAlterSentryRoleDeleteGroupsRequest request,
+      TAlterSentryRoleDeleteGroupsResponse response) {
+      throw new RuntimeException();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryPolicyStoreProcessor.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryPolicyStoreProcessor.java
new file mode 100644
index 0000000..46f8fb8
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryPolicyStoreProcessor.java
@@ -0,0 +1,70 @@
+/**
+ * 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.service.thrift;
+
+import junit.framework.Assert;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.provider.db.service.thrift.PolicyStoreConstants.PolicyStoreServerConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestSentryPolicyStoreProcessor {
+
+  private Configuration conf;
+
+  @Before
+  public void setup() {
+    conf = new Configuration(false);
+  }
+  @Test(expected=SentryConfigurationException.class)
+  public void testConfigNotNotificationHandler() throws Exception {
+    conf.set(PolicyStoreServerConfig.NOTIFICATION_HANDLERS, Object.class.getName());
+    SentryPolicyStoreProcessor.createHandlers(conf);
+  }
+  @Test(expected=SentryConfigurationException.class)
+  public void testConfigCannotCreateNotificationHandler() throws Exception {
+    conf.set(PolicyStoreServerConfig.NOTIFICATION_HANDLERS,
+        ExceptionInConstructorNotificationHandler.class.getName());
+    SentryPolicyStoreProcessor.createHandlers(conf);
+  }
+  @Test(expected=SentryConfigurationException.class)
+  public void testConfigNotAClassNotificationHandler() throws Exception {
+    conf.set(PolicyStoreServerConfig.NOTIFICATION_HANDLERS, "junk");
+    SentryPolicyStoreProcessor.createHandlers(conf);
+  }
+  @Test
+  public void testConfigMultipleNotificationHandlers() throws Exception {
+    conf.set(PolicyStoreServerConfig.NOTIFICATION_HANDLERS,
+        NoopNotificationHandler.class.getName() + "," +
+            NoopNotificationHandler.class.getName() + " " +
+            NoopNotificationHandler.class.getName());
+    Assert.assertEquals(3, SentryPolicyStoreProcessor.createHandlers(conf).size());
+  }
+  public static class ExceptionInConstructorNotificationHandler extends NotificationHandler {
+    public ExceptionInConstructorNotificationHandler(Configuration config) throws Exception {
+      super(config);
+      throw new Exception();
+    }
+  }
+  public static class NoopNotificationHandler extends NotificationHandler {
+    public NoopNotificationHandler(Configuration config) throws Exception {
+      super(config);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceFailureCase.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceFailureCase.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceFailureCase.java
new file mode 100644
index 0000000..a4643bf
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceFailureCase.java
@@ -0,0 +1,45 @@
+/**
+ * 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.service.thrift;
+
+import java.security.PrivilegedActionException;
+
+import org.apache.sentry.service.thrift.SentryServiceIntegrationBase;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestSentryServiceFailureCase extends SentryServiceIntegrationBase {
+
+  @Before @Override
+  public void setup() throws Exception {
+    beforeSetup();
+    setupConf();
+    conf.set(ServerConfig.ALLOW_CONNECT, "");
+    startSentryService();
+    afterSetup();
+  }
+
+  @Test(expected = PrivilegedActionException.class)
+  public void testClientServerConnectionFailure()  throws Exception {
+    connectToSentryService();
+    Assert.fail("Failed to receive Exception");
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/644e8be3/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceIntegration.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceIntegration.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceIntegration.java
new file mode 100644
index 0000000..d073d8b
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryServiceIntegration.java
@@ -0,0 +1,170 @@
+/**
+ * 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 createRequired 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.service.thrift;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.sentry.service.thrift.SentryServiceIntegrationBase;
+import org.apache.sentry.service.thrift.ServiceConstants.ThriftConstants;
+import org.apache.sentry.service.thrift.Status;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+public class TestSentryServiceIntegration extends SentryServiceIntegrationBase {
+  private static final Logger LOGGER = LoggerFactory.getLogger(TestSentryServiceIntegration.class);
+
+  @Test
+  public void testCreateRole() throws Exception {
+    Set<String> groupSet = new HashSet<String>();
+    TDropSentryRoleRequest dropReq = new TDropSentryRoleRequest();
+    dropReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    dropReq.setRoleName("admin_r");
+    dropReq.setRequestorUserName("user_1");
+    groupSet.add("admin");
+    dropReq.setRequestorGroupName(groupSet);
+    TDropSentryRoleResponse dropResp = client.dropRole(dropReq);
+    assertStatus(Status.NO_SUCH_OBJECT, dropResp.getStatus());
+    LOGGER.info("Successfully dropped role: admin_r");
+    groupSet.clear();
+
+    TCreateSentryRoleRequest createReq = new TCreateSentryRoleRequest();
+    createReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    createReq.setRequestorUserName("user_1");
+    groupSet.add("admin");
+    createReq.setRequestorGroupName(groupSet);
+    TSentryRole role = new TSentryRole();
+    role.setRoleName("admin_r");
+    role.setCreateTime(System.currentTimeMillis());
+    role.setGrantorPrincipal("test");
+    role.setPrivileges(new HashSet<TSentryPrivilege>());
+    createReq.setRole(role);
+    TCreateSentryRoleResponse createResp = client.createRole(createReq);
+    assertOK(createResp.getStatus());
+    LOGGER.info("Successfully create role: admin_r");
+    groupSet.clear();
+
+    TListSentryRolesRequest listReq = new TListSentryRolesRequest();
+    listReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    listReq.setRoleName("admin_r");
+    listReq.setRequestorUserName("user_1");
+    groupSet.add("admin");
+    listReq.setRequestorGroupName(groupSet);
+    TListSentryRolesResponse listResp = client.listRoleByName(listReq);
+    Set<TSentryRole> roles = listResp.getRoles();
+    Preconditions.checkArgument(roles.size() == 1, "Incorrect number of roles");
+    groupSet.clear();
+
+    dropReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    dropReq.setRoleName("admin_r");
+    dropReq.setRequestorUserName("user_1");
+    groupSet.add("admin");
+    dropReq.setRequestorGroupName(groupSet);
+    dropResp = client.dropRole(dropReq);
+    assertOK(dropResp.getStatus());
+    LOGGER.info("Successfully dropped role: admin_r");
+    groupSet.clear();
+  }
+
+  @Test
+  public void testGrantRevokePrivilege() throws Exception {
+    Set<String> groupSet = new HashSet<String>();
+    TDropSentryRoleRequest dropReq = new TDropSentryRoleRequest();
+    dropReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    dropReq.setRoleName("admin_testdb");
+    dropReq.setRequestorUserName("server_admin");
+    groupSet.add("admin");
+    dropReq.setRequestorGroupName(groupSet);
+    TDropSentryRoleResponse dropResp = client.dropRole(dropReq);
+    assertStatus(Status.NO_SUCH_OBJECT, dropResp.getStatus());
+    LOGGER.info("Successfully dropped role: admin_testdb");
+    groupSet.clear();
+
+    TCreateSentryRoleRequest createReq = new TCreateSentryRoleRequest();
+    createReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    createReq.setRequestorUserName("server_admin");
+    groupSet.add("admin");
+    createReq.setRequestorGroupName(groupSet);
+    TSentryRole role = new TSentryRole();
+    role.setRoleName("admin_testdb");
+    role.setCreateTime(System.currentTimeMillis());
+    role.setGrantorPrincipal("server_admin");
+    role.setPrivileges(new HashSet<TSentryPrivilege>());
+    createReq.setRole(role);
+    TCreateSentryRoleResponse createResp = client.createRole(createReq);
+    assertOK(createResp.getStatus());
+    LOGGER.info("Successfully create role: admin_testdb");
+    groupSet.clear();
+
+    TListSentryRolesRequest listReq = new TListSentryRolesRequest();
+    listReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    listReq.setRoleName("admin_testdb");
+    listReq.setRequestorUserName("server_admin");
+    groupSet.add("admin");
+    listReq.setRequestorGroupName(groupSet);
+    TListSentryRolesResponse listResp = client.listRoleByName(listReq);
+    Set<TSentryRole> roles = listResp.getRoles();
+    Preconditions.checkArgument(roles.size() == 1, "Incorrect number of roles");
+    groupSet.clear();
+
+    TAlterSentryRoleGrantPrivilegeRequest grantReq = new TAlterSentryRoleGrantPrivilegeRequest();
+    grantReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    grantReq.setRoleName("admin_testdb");
+    grantReq.setRequestorUserName("server_admin");
+    groupSet.add("admin");
+    grantReq.setRequestorGroupName(groupSet);
+    TSentryPrivilege privilege = new TSentryPrivilege();
+    privilege.setPrivilegeScope("DB");
+    privilege.setServerName("server1");
+    privilege.setDbName("testDB");
+    privilege.setAction("ALL");
+    privilege.setGrantorPrincipal("server_admin");
+    privilege.setCreateTime(System.currentTimeMillis());
+    grantReq.setPrivilege(privilege);
+    TAlterSentryRoleGrantPrivilegeResponse grantResp = client.grantPrivilege(grantReq);
+    assertOK(grantResp.getStatus());
+    LOGGER.info("Successfully granted privilege: " + privilege.toString());
+    groupSet.clear();
+
+    TAlterSentryRoleRevokePrivilegeRequest revokeReq = new TAlterSentryRoleRevokePrivilegeRequest();
+    revokeReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    revokeReq.setRoleName("admin_testdb");
+    revokeReq.setRequestorUserName("server_admin");
+    groupSet.add("admin");
+    revokeReq.setRequestorGroupName(groupSet);
+    revokeReq.setPrivilege(privilege);
+    TAlterSentryRoleRevokePrivilegeResponse revokeResp = client.revokePrivilege(revokeReq);
+    assertOK(revokeResp.getStatus());
+    LOGGER.info("Successfully revoked privilege: " + privilege.toString());
+    groupSet.clear();
+
+    dropReq.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    dropReq.setRoleName("admin_testdb");
+    dropReq.setRequestorUserName("server_admin");
+    groupSet.add("admin");
+    dropReq.setRequestorGroupName(groupSet);
+    dropResp = client.dropRole(dropReq);
+    assertOK(dropResp.getStatus());
+    LOGGER.info("Successfully dropped role: admin_testdb");
+    groupSet.clear();
+  }
+
+}