kudu-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From granthe...@apache.org
Subject [kudu] 02/04: [java] KUDU-2972: Add Kudu Ranger plugin
Date Thu, 05 Mar 2020 02:56:24 GMT
This is an automated email from the ASF dual-hosted git repository.

granthenke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git

commit 2ea8478d256b156a9257f5f51ad88a29a36872de
Author: Hao Hao <hao.hao@cloudera.com>
AuthorDate: Mon Jan 20 22:45:12 2020 -0800

    [java] KUDU-2972: Add Kudu Ranger plugin
    
    This commit adds the Kudu Ranger plugin for retrieving authorization
    decision from the Ranger service. It also introduces the Ranger subprocess
    which utilizes the plugin and communicates the authz decision with
    Kudu master via protobuf message.
    
    Change-Id: I0c995ac1a48ebf57667231cd3a82d3794f6ddf8d
    Reviewed-on: http://gerrit.cloudera.org:8080/15074
    Tested-by: Hao Hao <hao.hao@cloudera.com>
    Reviewed-by: Adar Dembo <adar@cloudera.com>
    Reviewed-by: Andrew Wong <awong@cloudera.com>
---
 CMakeLists.txt                                     |   1 +
 java/gradle/dependencies.gradle                    |   2 +
 java/kudu-subprocess/build.gradle                  |   8 +-
 .../subprocess/ranger/RangerProtocolHandler.java   |  71 ++++++++
 .../subprocess/ranger/RangerSubprocessMain.java    |  38 +++++
 .../ranger/authorization/RangerKuduAuthorizer.java | 185 +++++++++++++++++++++
 ...essageTestUtil.java => SubprocessTestUtil.java} |  90 +++++++++-
 .../org/apache/kudu/subprocess/TestMessageIO.java  |   8 +-
 .../kudu/subprocess/echo/TestEchoSubprocess.java   | 106 ++----------
 .../subprocess/ranger/TestRangerSubprocess.java    | 134 +++++++++++++++
 .../authorization/TestRangerKuduAuthorizer.java    |  81 +++++++++
 src/kudu/ranger/CMakeLists.txt                     |  33 ++++
 src/kudu/ranger/ranger.proto                       |  73 ++++++++
 13 files changed, 728 insertions(+), 102 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 21fb38f..b3207f8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1417,6 +1417,7 @@ add_subdirectory(src/kudu/integration-tests)
 add_subdirectory(src/kudu/kserver)
 add_subdirectory(src/kudu/master)
 add_subdirectory(src/kudu/mini-cluster)
+add_subdirectory(src/kudu/ranger)
 add_subdirectory(src/kudu/rebalance)
 add_subdirectory(src/kudu/rpc)
 add_subdirectory(src/kudu/security)
diff --git a/java/gradle/dependencies.gradle b/java/gradle/dependencies.gradle
index 4b3801b..022a52b 100755
--- a/java/gradle/dependencies.gradle
+++ b/java/gradle/dependencies.gradle
@@ -51,6 +51,7 @@ versions += [
     osdetector     : "1.6.2",
     parquet        : "1.11.0",
     protobuf       : "3.11.3",
+    ranger         : "2.0.0",
     scala          : "2.11.12",
     scalatest      : "3.0.8",
     scopt          : "3.7.1",
@@ -106,6 +107,7 @@ libs += [
     protobufJava         : "com.google.protobuf:protobuf-java:$versions.protobuf",
     protobufJavaUtil     : "com.google.protobuf:protobuf-java-util:$versions.protobuf",
     protoc               : "com.google.protobuf:protoc:$versions.protobuf",
+    rangerPlugin         : "org.apache.ranger:ranger-plugins-common:$versions.ranger",
     scalaLibrary         : "org.scala-lang:scala-library:$versions.scala",
     scalap               : "org.scala-lang:scalap:$versions.scala",
     scalatest            : "org.scalatest:scalatest_$versions.scalaBase:$versions.scalatest",
diff --git a/java/kudu-subprocess/build.gradle b/java/kudu-subprocess/build.gradle
index 6188ccf..1e04918 100644
--- a/java/kudu-subprocess/build.gradle
+++ b/java/kudu-subprocess/build.gradle
@@ -19,17 +19,23 @@ apply from: "$rootDir/gradle/protobuf.gradle"
 apply from: "$rootDir/gradle/shadow.gradle"
 
 dependencies {
+  compile libs.hadoopCommon
   compile libs.protobufJava
   compile libs.protobufJavaUtil
-  compile libs.hadoopCommon
+  compile libs.rangerPlugin
   compile libs.slf4jApi
 
+  // Workaround for RANGER-2749. Remove once resolved.
+  compile "commons-lang:commons-lang:2.6"
+
+  optional libs.jsr305
   optional libs.yetusAnnotations
 
   testCompile project(path: ":kudu-test-utils", configuration: "shadow")
   testCompile libs.junit
   testCompile libs.log4j
   testCompile libs.log4jSlf4jImpl
+  testCompile libs.mockitoCore
 }
 
 // Add protobuf files to the proto source set.
diff --git a/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/RangerProtocolHandler.java
b/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/RangerProtocolHandler.java
new file mode 100644
index 0000000..66a6a47
--- /dev/null
+++ b/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/RangerProtocolHandler.java
@@ -0,0 +1,71 @@
+// 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.kudu.subprocess.ranger;
+
+import org.apache.kudu.ranger.Ranger.RangerRequestListPB;
+import org.apache.kudu.ranger.Ranger.RangerResponseListPB;
+import org.apache.kudu.ranger.Ranger.RangerResponsePB;
+import org.apache.kudu.subprocess.ProtocolHandler;
+import org.apache.kudu.subprocess.ranger.authorization.RangerKuduAuthorizer;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class that sends requests to Ranger and gets authorization decision
+ * (e.g. allow or deny) as a response.
+ */
+@InterfaceAudience.Private
+class RangerProtocolHandler extends ProtocolHandler<RangerRequestListPB,
+                                                    RangerResponseListPB> {
+  private static final Logger LOG = LoggerFactory.getLogger(RangerProtocolHandler.class);
+
+  // The Ranger Kudu authorizer plugin. This field is not final
+  // as it is used in the mock test.
+  @InterfaceAudience.LimitedPrivate("Test")
+  static RangerKuduAuthorizer authz = new RangerKuduAuthorizer();
+
+  RangerProtocolHandler() {
+    authz.init();
+  }
+
+  @Override
+  protected RangerResponseListPB executeRequest(RangerRequestListPB requests) {
+    RangerResponseListPB.Builder responses = RangerResponseListPB.newBuilder();
+    for (RangerAccessResult result : authz.authorize(requests)) {
+      // The result can be null when Ranger plugin fails to load the policies
+      // from the Ranger admin server.
+      // TODO(Hao): add a test for the above case.
+      boolean isAllowed = (result != null && result.getIsAllowed());
+      RangerResponsePB.Builder response = RangerResponsePB.newBuilder();
+      response.setAllowed(isAllowed);
+      responses.addResponses(response);
+      if (LOG.isDebugEnabled()) {
+        LOG.debug(String.format("RangerAccessRequest [%s] receives result [%s]",
+                                result.getAccessRequest().toString(), result.toString()));
+      }
+    }
+    return responses.build();
+  }
+
+  @Override
+  protected Class<RangerRequestListPB> getRequestClass() {
+    return RangerRequestListPB.class;
+  }
+}
diff --git a/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/RangerSubprocessMain.java
b/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/RangerSubprocessMain.java
new file mode 100644
index 0000000..167cd08
--- /dev/null
+++ b/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/RangerSubprocessMain.java
@@ -0,0 +1,38 @@
+// 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.kudu.subprocess.ranger;
+
+import org.apache.kudu.subprocess.SubprocessExecutor;
+import org.apache.yetus.audience.InterfaceAudience;
+
+// The Ranger subprocess that wraps the Kudu Ranger plugin. For the
+// plugin to successfully connect to the Ranger service, configurations
+// such as ranger-kudu-security.xml (and ranger-kudu-policymgr-ssl.xml
+// for SSL connection) are required. To enable auditing in Ranger,
+// ranger-kudu-security.xml is needed. The plugin also requires
+// core-site.xml to use Hadoop UserGroupInformation for user group
+// resolution.
+@InterfaceAudience.Private
+class RangerSubprocessMain {
+
+  public static void main(String[] args) throws Exception {
+    SubprocessExecutor subprocessExecutor = new SubprocessExecutor();
+    RangerProtocolHandler protocolProcessor = new RangerProtocolHandler();
+    subprocessExecutor.run(args, protocolProcessor, /* timeoutMs= */-1);
+  }
+}
diff --git a/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/authorization/RangerKuduAuthorizer.java
b/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/authorization/RangerKuduAuthorizer.java
new file mode 100644
index 0000000..01ebe27
--- /dev/null
+++ b/java/kudu-subprocess/src/main/java/org/apache/kudu/subprocess/ranger/authorization/RangerKuduAuthorizer.java
@@ -0,0 +1,185 @@
+// 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.kudu.subprocess.ranger.authorization;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.kudu.ranger.Ranger.RangerRequestListPB;
+import org.apache.kudu.ranger.Ranger.RangerRequestPB;
+import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class RangerKuduAuthorizer {
+  private static final Logger LOG = LoggerFactory.getLogger(RangerKuduAuthorizer.class);
+  // The following properties need to match the Kudu service def in Ranger
+  // (https://github.com/apache/ranger/blob/master/agents-common/src/main/resources/service-defs/ranger-servicedef-kudu.json).
+  private static final String APP_ID = "kudu";
+  private static final String RANGER_DB_RESOURCE_NAME = "database";
+  private static final String RANGER_TABLE_RESOURCE_NAME = "table";
+  private static final String RANGER_COLUMN_RESOURCE_NAME = "column";
+  private static final String SERVICE_TYPE = "kudu";
+
+  // The Ranger Kudu plugin. This field is not final as it is used in the
+  // mock test.
+  @InterfaceAudience.LimitedPrivate("Test")
+  RangerBasePlugin plugin;
+
+  public RangerKuduAuthorizer() {
+    plugin = new RangerBasePlugin(SERVICE_TYPE, APP_ID);
+    plugin.setResultProcessor(new RangerDefaultAuditHandler());
+  }
+
+  /**
+   * Initializes the Ranger Kudu plugin, which has to be called explicitly
+   * before doing any authorizations.
+   */
+  public void init() {
+    LOG.info("Initializing Ranger Kudu plugin");
+    plugin.init();
+  }
+
+  /**
+   *  Authorizes a given <code>RangerRequestListPB</code> in Ranger and returns
+   *  a list of <code>RangerAccessResult</code> which contains the authorization
+   *  decisions. Note that the order of results is determined by the order of
+   *  requests.
+   *
+   * @param requests a RangerRequestListPB
+   * @return a list of RangerAccessResult
+   */
+  @VisibleForTesting
+  public Collection<RangerAccessResult> authorize(RangerRequestListPB requests) {
+    Collection<RangerAccessRequest> rangerRequests = createRequests(requests);
+    // Reject requests if user field is empty.
+    if (!requests.hasUser() || requests.getUser().isEmpty()) {
+      Collection<RangerAccessResult> results = new ArrayList<>();
+      for (RangerAccessRequest request : rangerRequests) {
+        // Create a 'dummy' RangerAccessResult that denies the request (to have
+        // a short cut), instead of sending the request to Ranger.
+        RangerAccessResult result = new RangerAccessResult(
+            /* policyType= */1, APP_ID,
+            new RangerServiceDef(), request);
+        result.setIsAllowed(false);
+        results.add(result);
+      }
+      return results;
+    }
+    return plugin.isAccessAllowed(rangerRequests);
+  }
+
+  /**
+   * Gets a list of authorization decision from Ranger with the specified list
+   * of ranger access request.
+   *
+   * @param requests a list of RangerAccessRequest
+   * @return a list of RangerAccessResult
+   */
+  @VisibleForTesting
+  Collection<RangerAccessResult> authorize(Collection<RangerAccessRequest> requests)
{
+    return plugin.isAccessAllowed(requests);
+  }
+
+  /**
+   * Creates a Ranger access request for the specified user who performs
+   * the given action on the resource.
+   *
+   * @param action action to be authorized on the resource. Note that when it
+   *               is null, Ranger will match to any valid actions
+   * @param user user who is performing the action
+   * @param groups the set of groups the user belongs to
+   * @param db the database name the action is to be performed on
+   * @param table the table name the action is to be performed on
+   * @param col the column name the action is to be performed on
+   * @return the ranger access request
+   */
+  private static RangerAccessRequestImpl createRequest(
+      @Nullable String action, String user,
+      @Nullable Set<String> groups, @Nullable String db,
+      @Nullable String table, @Nullable String col) {
+    final RangerAccessResourceImpl resource = new RangerAccessResourceImpl();
+    resource.setValue(RANGER_DB_RESOURCE_NAME, db);
+    resource.setValue(RANGER_TABLE_RESOURCE_NAME, table);
+    resource.setValue(RANGER_COLUMN_RESOURCE_NAME, col);
+
+    final RangerAccessRequestImpl request = new RangerAccessRequestImpl();
+    request.setResource(resource);
+    request.setAccessType(action);
+    // Add action as it is used for auditing in Ranger.
+    request.setAction(action);
+    request.setUser(user);
+    request.setUserGroups(groups);
+    return request;
+  }
+
+  /**
+   * Creates a list of <code>RangerAccessRequest</code> for the given
+   * <code>RangerRequestListPB</code>.
+   *
+   * @param requests the given RangerRequestListPB
+   * @return a list of RangerAccessRequest
+   */
+  private static List<RangerAccessRequest> createRequests(RangerRequestListPB requests)
{
+    List<RangerAccessRequest> rangerRequests = new ArrayList<>();
+    Preconditions.checkArgument(requests.hasUser());
+    Preconditions.checkArgument(!requests.getUser().isEmpty());
+    final String user = requests.getUser();
+    Set<String> groups = getUserGroups(user);
+    for (RangerRequestPB request : requests.getRequestsList()) {
+      // Action should be lower case to match the Kudu service def in Ranger.
+      String action = request.getAction().name().toLowerCase();
+      String db = request.hasDatabase() ? request.getDatabase() : null;
+      String table = request.hasTable() ? request.getTable() : null;
+      String column = request.hasColumn() ? request.getColumn() : null;
+      rangerRequests.add(createRequest(action, user, groups,
+                                       db, table, column));
+    }
+    return rangerRequests;
+  }
+
+  /**
+   * Gets the user group mapping from Hadoop. The groups of a user is determined by a
+   * group mapping service provider. See more detail at
+   * https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/GroupsMapping.html.
+   *
+   * @param user the user name
+   * @return the set of groups the user belongs to
+   */
+  private static Set<String> getUserGroups(String user) {
+    Preconditions.checkNotNull(user);
+    Preconditions.checkArgument(!user.isEmpty());
+    UserGroupInformation ugi;
+    ugi = UserGroupInformation.createRemoteUser(user);
+    return new HashSet<>(ugi.getGroups());
+  }
+}
diff --git a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/MessageTestUtil.java
b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/SubprocessTestUtil.java
similarity index 53%
rename from java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/MessageTestUtil.java
rename to java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/SubprocessTestUtil.java
index bca288f..b7d44c9 100644
--- a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/MessageTestUtil.java
+++ b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/SubprocessTestUtil.java
@@ -17,11 +17,20 @@
 
 package org.apache.kudu.subprocess;
 
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.function.Function;
 
 import com.google.protobuf.Any;
 import com.google.protobuf.Message;
@@ -29,12 +38,85 @@ import com.google.protobuf.Parser;
 
 import org.apache.kudu.subprocess.Subprocess.EchoRequestPB;
 import org.apache.kudu.subprocess.Subprocess.SubprocessRequestPB;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Utility class of common functions used for testing subprocess
- * message processing.
+ * Utility class of common functions used for testing subprocess.
  */
-public class MessageTestUtil {
+public class SubprocessTestUtil {
+  private static final Logger LOG = LoggerFactory.getLogger(SubprocessTestUtil.class);
+  protected static final String[] NO_ARGS = {""};
+  protected static final int TIMEOUT_MS = 1000;
+
+  // Helper functors that can be passed around to ensure we either see an error
+  // or not.
+  protected static final Function<Throwable, Void> NO_ERR = e -> {
+    LOG.error(String.format("Unexpected error: %s", e.getMessage()));
+    fail();
+    return null;
+  };
+  protected final Function<Throwable, Void> HAS_ERR = e -> {
+    assertTrue(e instanceof KuduSubprocessException);
+    return null;
+  };
+
+  // Pipe that we can write to that will feed requests to the subprocess's
+  // input pipe.
+  protected PipedOutputStream requestSenderPipe;
+
+  // Pipe that we can read from that will receive responses from the
+  // subprocess's output pipe.
+  protected final PipedInputStream responseReceiverPipe = new PipedInputStream();
+
+  public static class PrintStreamWithIOException extends PrintStream {
+    public PrintStreamWithIOException(OutputStream out, boolean autoFlush, String encoding)
+            throws UnsupportedEncodingException {
+      super(out, autoFlush, encoding);
+    }
+
+    @Override
+    public boolean checkError() {
+      // Always say that we've got an error.
+      return true;
+    }
+  }
+
+  // Sends a SubprocessRequestPB to the sender pipe, serializing it as
+  // appropriate.
+  public void sendRequestToPipe(Subprocess.SubprocessRequestPB req) throws IOException {
+    requestSenderPipe.write(SubprocessTestUtil.serializeMessage(req));
+  }
+
+  // Receives a response from the receiver pipe and deserializes it into a
+  // SubprocessResponsePB.
+  public Subprocess.SubprocessResponsePB receiveResponse() throws IOException {
+    BufferedInputStream bufferedInput = new BufferedInputStream(responseReceiverPipe);
+    return SubprocessTestUtil.deserializeMessage(bufferedInput, Subprocess.SubprocessResponsePB.parser());
+  }
+
+  // Sets up and returns a SubprocessExecutor with the given error handler and
+  // IO error injection behavior. The SubprocessExecutor will do IO to and from
+  // 'requestSenderPipe' and 'responseReceiverPipe'.
+  public SubprocessExecutor setUpExecutorIO(Function<Throwable, Void> errorHandler,
+                                            boolean injectIOError) throws IOException {
+    // Initialize the pipe that we'll push requests to; feed it into the
+    // executor's input pipe.
+    PipedInputStream inputPipe = new PipedInputStream();
+    requestSenderPipe = new PipedOutputStream(inputPipe);
+    System.setIn(inputPipe);
+
+    // Initialize the pipe that the executor will write to; feed it into the
+    // response pipe that we can read from.
+    PipedOutputStream outputPipe = new PipedOutputStream(responseReceiverPipe);
+    if (injectIOError) {
+      System.setOut(new PrintStreamWithIOException(outputPipe, /*autoFlush*/false, "UTF-8"));
+    } else {
+      System.setOut(new PrintStream(outputPipe));
+    }
+    SubprocessExecutor subprocessExecutor = new SubprocessExecutor(errorHandler);
+    return subprocessExecutor;
+  }
 
   /**
    * Constructs a SubprocessRequestPB message of echo request with the
@@ -91,7 +173,7 @@ public class MessageTestUtil {
    * @return a message
    * @throws IOException if an I/O error occurs
    */
-  static <T extends Message> T deserializeMessage(byte[] bytes, Parser<T> parser)
+  public static <T extends Message> T deserializeMessage(byte[] bytes, Parser<T>
parser)
       throws IOException {
     ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
     return deserializeMessage(new BufferedInputStream(inputStream), parser);
diff --git a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/TestMessageIO.java
b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/TestMessageIO.java
index 011f4e9..98fc0b0 100644
--- a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/TestMessageIO.java
+++ b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/TestMessageIO.java
@@ -64,9 +64,9 @@ public class TestMessageIO {
   @Test
   public void testBasicEchoMessage() throws Exception {
     final String data = "data";
-    final SubprocessRequestPB request = MessageTestUtil.createEchoSubprocessRequest(data);
-    final byte[] message = MessageTestUtil.serializeMessage(request);
-    final SubprocessRequestPB actualRequest = MessageTestUtil.deserializeMessage(
+    final SubprocessRequestPB request = SubprocessTestUtil.createEchoSubprocessRequest(data);
+    final byte[] message = SubprocessTestUtil.serializeMessage(request);
+    final SubprocessRequestPB actualRequest = SubprocessTestUtil.deserializeMessage(
         message, SubprocessRequestPB.parser());
     Assert.assertEquals(request, actualRequest);
   }
@@ -79,7 +79,7 @@ public class TestMessageIO {
   @Test
   public void testSubprocessOutputStream() {
     final String data = "data";
-    final SubprocessRequestPB request = MessageTestUtil.createEchoSubprocessRequest(data);
+    final SubprocessRequestPB request = SubprocessTestUtil.createEchoSubprocessRequest(data);
     final PrintStreamOverload printStreamOverload =
         new PrintStreamOverload(new ByteArrayOutputStream());
     final BufferedOutputStream out = new BufferedOutputStream(
diff --git a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/echo/TestEchoSubprocess.java
b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/echo/TestEchoSubprocess.java
index c0e06c4..66f7ddc 100644
--- a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/echo/TestEchoSubprocess.java
+++ b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/echo/TestEchoSubprocess.java
@@ -17,18 +17,11 @@
 
 package org.apache.kudu.subprocess.echo;
 
-import java.io.BufferedInputStream;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
-import java.util.function.Function;
 
 import org.junit.Assert;
 import org.junit.Rule;
@@ -37,62 +30,25 @@ import org.junit.function.ThrowingRunnable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.kudu.subprocess.KuduSubprocessException;
 import org.apache.kudu.subprocess.MessageIO;
-import org.apache.kudu.subprocess.MessageTestUtil;
 import org.apache.kudu.subprocess.OutboundResponse;
 import org.apache.kudu.subprocess.Subprocess.EchoResponsePB;
 import org.apache.kudu.subprocess.Subprocess.SubprocessMetricsPB;
-import org.apache.kudu.subprocess.Subprocess.SubprocessRequestPB;
 import org.apache.kudu.subprocess.Subprocess.SubprocessResponsePB;
 import org.apache.kudu.subprocess.SubprocessExecutor;
+import org.apache.kudu.subprocess.SubprocessTestUtil;
 import org.apache.kudu.test.junit.RetryRule;
 
 /**
  * Tests for subprocess that handles EchoRequest messages in various conditions.
  */
-public class TestEchoSubprocess {
+public class TestEchoSubprocess extends SubprocessTestUtil {
   private static final Logger LOG = LoggerFactory.getLogger(TestEchoSubprocess.class);
-  private static final String[] NO_ARGS = {""};
-  private static final int TIMEOUT_MS = 1000;
   private static final String MESSAGE = "We are one. We are many.";
 
-  // Helper functors that can be passed around to ensure we either see an error
-  // or not.
-  private static final Function<Throwable, Void> NO_ERR = e -> {
-    LOG.error(String.format("Unexpected error: %s", e.getMessage()));
-    Assert.fail();
-    return null;
-  };
-  private final Function<Throwable, Void> HAS_ERR = e -> {
-    Assert.assertTrue(e instanceof KuduSubprocessException);
-    return null;
-  };
-
-  // Pipe that we can write to that will feed requests to the subprocess's
-  // input pipe.
-  private PipedOutputStream requestSenderPipe;
-
-  // Pipe that we can read from that will receive responses from the
-  // subprocess's output pipe.
-  private final PipedInputStream responseReceiverPipe = new PipedInputStream();
-
   @Rule
   public RetryRule retryRule = new RetryRule();
 
-  public static class PrintStreamWithIOException extends PrintStream {
-    public PrintStreamWithIOException(OutputStream out, boolean autoFlush, String encoding)
-        throws UnsupportedEncodingException {
-      super(out, autoFlush, encoding);
-    }
-
-    @Override
-    public boolean checkError() {
-      // Always say that we've got an error.
-      return true;
-    }
-  }
-
   // Given that executors run multiple threads, the exceptions that we expect
   // may not necessarily be the first thrown. This checks for the expected
   // error on all thrown exceptions, including suppressed ones.
@@ -119,42 +75,6 @@ public class TestEchoSubprocess {
     throw new AssertionError("Didn't throw an exception");
   }
 
-  // Sends a SubprocessRequestPB to the sender pipe, serializing it as
-  // appropriate.
-  void sendRequestToPipe(SubprocessRequestPB req) throws IOException {
-    requestSenderPipe.write(MessageTestUtil.serializeMessage(req));
-  }
-
-  // Receives a response from the receiver pipe and deserializes it into a
-  // SubprocessResponsePB.
-  SubprocessResponsePB receiveResponse() throws IOException {
-    BufferedInputStream bufferedInput = new BufferedInputStream(responseReceiverPipe);
-    return MessageTestUtil.deserializeMessage(bufferedInput, SubprocessResponsePB.parser());
-  }
-
-  // Sets up and returns a SubprocessExecutor with the given error handler and
-  // IO error injection behavior. The SubprocessExecutor will do IO to and from
-  // 'requestSenderPipe' and 'responseReceiverPipe'.
-  SubprocessExecutor setUpExecutorIO(Function<Throwable, Void> errorHandler,
-                                     boolean injectIOError) throws IOException {
-    // Initialize the pipe that we'll push requests to; feed it into the
-    // executor's input pipe.
-    PipedInputStream inputPipe = new PipedInputStream();
-    requestSenderPipe = new PipedOutputStream(inputPipe);
-    System.setIn(inputPipe);
-
-    // Initialize the pipe that the executor will write to; feed it into the
-    // response pipe that we can read from.
-    PipedOutputStream outputPipe = new PipedOutputStream(responseReceiverPipe);
-    if (injectIOError) {
-      System.setOut(new PrintStreamWithIOException(outputPipe, /*autoFlush*/false, "UTF-8"));
-    } else {
-      System.setOut(new PrintStream(outputPipe));
-    }
-    SubprocessExecutor subprocessExecutor = new SubprocessExecutor(errorHandler);
-    return subprocessExecutor;
-  }
-
   /**
    * Test a regular old message. There should be no exceptions of any kind.
    * We should also see some metrics that make sense.
@@ -163,7 +83,7 @@ public class TestEchoSubprocess {
   public void testBasicMsg() throws Exception {
     SubprocessExecutor executor =
         setUpExecutorIO(NO_ERR, /*injectIOError*/false);
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE));
 
     executor.run(NO_ARGS, new EchoProtocolHandler(), TIMEOUT_MS);
     SubprocessResponsePB spResp = receiveResponse();
@@ -196,9 +116,9 @@ public class TestEchoSubprocess {
     SubprocessExecutor executor =
       setUpExecutorIO(NO_ERR, /*injectIOError*/false);
     final int SLEEP_MS = 200;
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE, SLEEP_MS));
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE, SLEEP_MS));
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE, SLEEP_MS));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE, SLEEP_MS));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE, SLEEP_MS));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE, SLEEP_MS));
 
     // Run the executor with a single parser thread so we can make stronger
     // assumptions about timing.
@@ -253,9 +173,9 @@ public class TestEchoSubprocess {
       setUpExecutorIO(NO_ERR, /*injectIOError*/false);
     final int BLOCK_MS = 200;
     executor.blockWriteMs(BLOCK_MS);
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE));
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE));
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE));
     executor.run(NO_ARGS, new EchoProtocolHandler(), TIMEOUT_MS);
 
     // In writing the first request, the other two requests should've been
@@ -312,7 +232,7 @@ public class TestEchoSubprocess {
   public void testInjectIOException() throws Exception {
     SubprocessExecutor executor =
         setUpExecutorIO(HAS_ERR, /*injectIOError*/true);
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE));
     // NOTE: we don't expect the ExecutionException from the MessageWriter's
     // CompletableFuture because, in waiting for completion, the MessageReader
     // times out before CompletableFuture.get() is called on the writer.
@@ -330,7 +250,7 @@ public class TestEchoSubprocess {
     SubprocessExecutor executor =
         setUpExecutorIO(HAS_ERR, /*injectIOError*/false);
     executor.interrupt();
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest(MESSAGE));
+    sendRequestToPipe(createEchoSubprocessRequest(MESSAGE));
     assertIncludingSuppressedThrows(ExecutionException.class,
         "Unable to put the message to the queue",
         () -> executor.run(NO_ARGS, new EchoProtocolHandler(), TIMEOUT_MS));
@@ -344,8 +264,8 @@ public class TestEchoSubprocess {
   public void testMessageParser() throws Exception  {
     SubprocessExecutor executor =
         setUpExecutorIO(NO_ERR, /*injectIOError*/false);
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest("a"));
-    sendRequestToPipe(MessageTestUtil.createEchoSubprocessRequest("b"));
+    sendRequestToPipe(createEchoSubprocessRequest("a"));
+    sendRequestToPipe(createEchoSubprocessRequest("b"));
     executor.blockWriteMs(1000);
     Assert.assertThrows(TimeoutException.class,
         () -> executor.run(NO_ARGS, new EchoProtocolHandler(), /*timeoutMs*/500));
diff --git a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/ranger/TestRangerSubprocess.java
b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/ranger/TestRangerSubprocess.java
new file mode 100644
index 0000000..667fa02
--- /dev/null
+++ b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/ranger/TestRangerSubprocess.java
@@ -0,0 +1,134 @@
+// 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.kudu.subprocess.ranger;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+import com.google.protobuf.Any;
+import org.apache.kudu.ranger.Ranger.ActionPB;
+import org.apache.kudu.ranger.Ranger.RangerRequestListPB;
+import org.apache.kudu.ranger.Ranger.RangerRequestPB;
+import org.apache.kudu.ranger.Ranger.RangerResponseListPB;
+import org.apache.kudu.subprocess.Subprocess.SubprocessRequestPB;
+import org.apache.kudu.subprocess.SubprocessExecutor;
+import org.apache.kudu.subprocess.SubprocessTestUtil;
+import org.apache.kudu.subprocess.ranger.authorization.RangerKuduAuthorizer;
+import org.apache.kudu.test.junit.RetryRule;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Tests for the ranger subprocess.
+ */
+public class TestRangerSubprocess extends SubprocessTestUtil {
+
+  @Rule
+  public RetryRule retryRule = new RetryRule();
+
+  private static RangerRequestPB createRangerRequest(ActionPB action, String db,
+                                                     String table, String col) {
+    RangerRequestPB.Builder builder = RangerRequestPB.newBuilder();
+    builder.setAction(action);
+    builder.setDatabase(db);
+    builder.setTable(table);
+    builder.setColumn(col);
+    return builder.build();
+  }
+
+  private static RangerRequestListPB createRangerRequestList(
+      List<RangerRequestPB> requests, String user) {
+    RangerRequestListPB.Builder builder = RangerRequestListPB.newBuilder();
+    builder.addAllRequests(requests);
+    builder.setUser(user);
+    return builder.build();
+  }
+
+  private static SubprocessRequestPB createRangerSubprocessRequest(
+      RangerRequestListPB request) {
+    SubprocessRequestPB.Builder builder = SubprocessRequestPB.newBuilder();
+    builder.setRequest(Any.pack(request));
+    return builder.build();
+  }
+
+  @Before
+  public void mockAuthorizer() {
+    RangerProtocolHandler.authz = Mockito.mock(RangerKuduAuthorizer.class);
+  }
+
+  /**
+   * Sends a list of Ranger request and verifies the response by mocking the authorization
+   * decisions.
+   */
+  @Test
+  public void testBasicRangerMessage() throws Exception {
+    final String user = "Alice";
+    final String db = "db";
+    final String table = "table";
+    final String col = "col";
+    final RangerRequestPB updateRequest = createRangerRequest(ActionPB.UPDATE, db, table,
col);
+    final RangerRequestPB selectRequest = createRangerRequest(ActionPB.SELECT, db, table,
col);
+    final RangerRequestPB createRequest = createRangerRequest(ActionPB.CREATE, db, table,
col);
+    final List<RangerRequestPB> requests = new ArrayList<>();
+    // Send multiple ranger requests in one message.
+    requests.add(updateRequest);
+    requests.add(selectRequest);
+    requests.add(createRequest);
+    final RangerRequestListPB requestList = createRangerRequestList(requests, user);
+    final SubprocessRequestPB subprocessRequest = createRangerSubprocessRequest(requestList);
+
+    // Mock the authorization results.
+    List<RangerAccessResult> rangerResults = new ArrayList<>();
+    final RangerAccessResult positiveResult = new RangerAccessResult(
+        /* policyType= */1, "kudu",
+        new RangerServiceDef(), new RangerAccessRequestImpl());
+    positiveResult.setIsAllowed(true);
+    final RangerAccessResult negativeResult = new RangerAccessResult(
+        /* policyType= */1, "kudu",
+        new RangerServiceDef(), new RangerAccessRequestImpl());
+    negativeResult.setIsAllowed(false);
+    rangerResults.add(positiveResult);
+    rangerResults.add(negativeResult);
+    rangerResults.add(positiveResult);
+    Mockito.when(RangerProtocolHandler.authz.authorize(requestList))
+           .thenReturn(rangerResults);
+
+    SubprocessExecutor executor =
+        setUpExecutorIO(NO_ERR, /*injectIOError*/false);
+    sendRequestToPipe(subprocessRequest);
+    // We expect the executor to time out since it is non cancelable
+    // if no exception encountered.
+    assertThrows(TimeoutException.class,
+        () -> executor.run(NO_ARGS, new RangerProtocolHandler(), TIMEOUT_MS));
+
+    RangerResponseListPB resp = receiveResponse().getResponse().unpack(RangerResponseListPB.class);
+    assertTrue(resp.getResponses(/* index= */0).getAllowed());
+    assertFalse(resp.getResponses(/* index= */1).getAllowed());
+    assertTrue(resp.getResponses(/* index= */2).getAllowed());
+  }
+}
\ No newline at end of file
diff --git a/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/ranger/authorization/TestRangerKuduAuthorizer.java
b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/ranger/authorization/TestRangerKuduAuthorizer.java
new file mode 100644
index 0000000..e90c8ad
--- /dev/null
+++ b/java/kudu-subprocess/src/test/java/org/apache/kudu/subprocess/ranger/authorization/TestRangerKuduAuthorizer.java
@@ -0,0 +1,81 @@
+// 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.kudu.subprocess.ranger.authorization;
+
+import org.apache.kudu.test.junit.RetryRule;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the Ranger authorizer.
+ */
+public class TestRangerKuduAuthorizer {
+
+  @Rule
+  public RetryRule retryRule = new RetryRule();
+
+  /**
+   * Generates a few ranger authorization results and verifies the
+   * Ranger authorizer work as expected.
+   */
+  @Test
+  public void testBasicRangerAuthorizer() {
+    RangerKuduAuthorizer authz = new RangerKuduAuthorizer();
+    authz.plugin = Mockito.mock(RangerBasePlugin.class);
+    // We have to mock RangerAccessRequestImpl as it does not implement equals().
+    // Mock with a positive authz result.
+    RangerAccessRequestImpl mockUpdateRequest = Mockito.mock(RangerAccessRequestImpl.class);
+    final RangerAccessResult updateResult = new RangerAccessResult(
+        /* policyType= */1, "kudu",
+        new RangerServiceDef(), mockUpdateRequest);
+    updateResult.setIsAllowed(true);
+
+    // Mock with a negative authz result.
+    RangerAccessRequestImpl mockCreateRequest = Mockito.mock(RangerAccessRequestImpl.class);
+    final RangerAccessResult createResult = new RangerAccessResult(
+        /* policyType= */1, "kudu",
+        new RangerServiceDef(), mockCreateRequest);
+    createResult.setIsAllowed(false);
+
+    Collection<RangerAccessRequest> requests = new ArrayList<>();
+    requests.add(mockUpdateRequest);
+    requests.add(mockCreateRequest);
+    Collection<RangerAccessResult> results = new ArrayList<>();
+    results.add(updateResult);
+    results.add(createResult);
+
+    Mockito.when(authz.plugin.isAccessAllowed(requests))
+           .thenReturn(results);
+    Iterator<RangerAccessResult> actualResultsIter = authz.authorize(requests).iterator();
+    assertTrue(actualResultsIter.next().getIsAllowed());
+    assertFalse(actualResultsIter.next().getIsAllowed());
+  }
+}
\ No newline at end of file
diff --git a/src/kudu/ranger/CMakeLists.txt b/src/kudu/ranger/CMakeLists.txt
new file mode 100644
index 0000000..6a21976
--- /dev/null
+++ b/src/kudu/ranger/CMakeLists.txt
@@ -0,0 +1,33 @@
+# 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.
+
+#######################################
+# ranger_proto
+#######################################
+
+PROTOBUF_GENERATE_CPP(
+  RANGER_PROTO_SRCS RANGER_PROTO_HDRS RANGER_PROTO_TGTS
+  SOURCE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..
+  BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/../..
+  PROTO_FILES ranger.proto)
+
+add_library(ranger_proto
+  ${RANGER_PROTO_SRCS}
+  ${RANGER_PROTO_HDRS})
+target_link_libraries(ranger_proto
+  protobuf
+)
diff --git a/src/kudu/ranger/ranger.proto b/src/kudu/ranger/ranger.proto
new file mode 100644
index 0000000..4848ea3
--- /dev/null
+++ b/src/kudu/ranger/ranger.proto
@@ -0,0 +1,73 @@
+// 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.
+syntax = "proto2";
+package kudu.ranger;
+option java_package = "org.apache.kudu.ranger";
+
+// Describes the type of action that can be performed in Ranger.
+enum ActionPB {
+  SELECT = 0;
+  INSERT = 1;
+  UPDATE = 2;
+  DELETE = 3;
+  ALTER = 4;
+  CREATE = 5;
+  DROP = 6;
+  ALL = 7;
+  METADATA = 8;
+}
+
+// The following protobuf definitions are used as interface between
+// Kudu master and the Ranger authorization plugin operating in a
+// Java subprocess that is managed by the Kudu master.
+
+// Describes a single Ranger authorization request per user.
+message RangerRequestPB {
+  // Database name the action is to be performed on.
+  optional string database = 1;
+
+  // Table name the action is to be performed on.
+  optional string table = 2;
+
+  // Column name the action is to be performed on.
+  optional string column = 3;
+
+  // Action to be authorized on the resource.
+  optional ActionPB action = 4;
+}
+
+// Describes a single Ranger authorization response for a single user.
+message RangerResponsePB {
+  // Whether or not a request is allowed.
+  optional bool allowed = 1;
+}
+
+// Describes a list of authorization requests sent to the Ranger service
+// for a single user.
+message RangerRequestListPB {
+  repeated RangerRequestPB requests = 1;
+
+  // User performing the action.
+  optional string user = 2;
+}
+
+// Describes a list of authorization descison responded from the Ranger
+// service for a request for a single user. Note that the order of
+// responses is determined by the order of requests in RangerRequestListPB.
+message RangerResponseListPB {
+  repeated RangerResponsePB responses = 1;
+}


Mime
View raw message