You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by ak...@apache.org on 2017/09/27 18:29:32 UTC
sentry git commit: SENTRY-1812: Provide interactive Sentry CLI
Repository: sentry
Updated Branches:
refs/heads/akolb-cli [created] 44c5d9f4a
SENTRY-1812: Provide interactive Sentry CLI
Project: http://git-wip-us.apache.org/repos/asf/sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/44c5d9f4
Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/44c5d9f4
Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/44c5d9f4
Branch: refs/heads/akolb-cli
Commit: 44c5d9f4a744bd71047bab773cdb783ee3b89b44
Parents: da1863f
Author: Alexander Kolbasov <ak...@cloudera.com>
Authored: Wed Sep 27 13:28:46 2017 -0500
Committer: Alexander Kolbasov <ak...@cloudera.com>
Committed: Wed Sep 27 13:28:46 2017 -0500
----------------------------------------------------------------------
.../db/service/thrift/TestSentryMetrics.java | 96 ++++++
sentry-tools/pom.xml | 63 ++++
.../org/apache/sentry/shell/GroupShell.java | 65 ++++
.../org/apache/sentry/shell/PrivsShell.java | 73 ++++
.../org/apache/sentry/shell/RolesShell.java | 72 ++++
.../java/org/apache/sentry/shell/SentryCli.java | 205 ++++++++++++
.../java/org/apache/sentry/shell/ShellUtil.java | 335 +++++++++++++++++++
.../org/apache/sentry/shell/TopLevelShell.java | 161 +++++++++
8 files changed, 1070 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryMetrics.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryMetrics.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryMetrics.java
new file mode 100644
index 0000000..b9c63ff
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/thrift/TestSentryMetrics.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.codahale.metrics.Counter;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.service.thrift.ServiceConstants;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+
+import static java.lang.Thread.sleep;
+
+public class TestSentryMetrics {
+ private static SentryMetrics metrics = SentryMetrics.getInstance();
+ private final static Configuration conf = new Configuration();
+ private static File jsonReportFile;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ jsonReportFile = File.createTempFile("TestMetrics", ".json");
+ String jsonFile = jsonReportFile.getAbsolutePath();
+ conf.set(ServiceConstants.ServerConfig.SENTRY_JSON_REPORTER_FILE, jsonFile);
+ conf.setInt(ServiceConstants.ServerConfig.SENTRY_REPORTER_INTERVAL_SEC, 1);
+ conf.set(ServiceConstants.ServerConfig.SENTRY_REPORTER, "JSON");
+ metrics.initReporting(conf);
+ }
+
+ @AfterClass
+ public static void cleanup() {
+ System.out.println(jsonReportFile);
+ jsonReportFile.delete();
+ }
+
+
+ /**
+ * Test JSON reporter.
+ * <ul>
+ * <li>increment the counter value</li>
+ * <li>wait a bit for the new repor to be written</li>
+ * <li>read the value from JSON file</li>
+ * <li>verify that the value matches expectation</li>
+ * </ul>
+ * This check is repeated a few times to verify that the values are updated over time.
+ * @throws Exception if fails to read counter value
+ */
+ @Test
+ public void testJsonReporter() throws Exception {
+ int runs = 5;
+ String counterName = "cnt";
+ Counter counter = metrics.getCounter(counterName);
+ for (int i = 0; i < runs; i++) {
+ counter.inc();
+ sleep(1500);
+ Assert.assertEquals(i + 1, getCounterValue(counterName));
+ }
+
+ }
+
+ /**
+ * Read counter value from JSON metric report
+ * @param name counter name
+ * @return counter value
+ * @throws FileNotFoundException if file doesn't exist
+ */
+ private int getCounterValue(String name) throws FileNotFoundException {
+ JsonParser parser = new JsonParser();
+ JsonElement element = parser.parse(new FileReader(jsonReportFile.getAbsolutePath()));
+ JsonObject jobj = element.getAsJsonObject();
+ jobj = jobj.getAsJsonObject("counters").getAsJsonObject(name);
+ return jobj.get("count").getAsInt();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-tools/pom.xml
----------------------------------------------------------------------
diff --git a/sentry-tools/pom.xml b/sentry-tools/pom.xml
new file mode 100644
index 0000000..ed0fb92
--- /dev/null
+++ b/sentry-tools/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>sentry</artifactId>
+ <groupId>org.apache.sentry</groupId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>sentry-tools</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.budhash.cliche</groupId>
+ <artifactId>cliche-shell</artifactId>
+ <version>0.9.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sentry</groupId>
+ <artifactId>sentry-provider-db</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>${basedir}/src/main/java</sourceDirectory>
+ </build>
+
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java
----------------------------------------------------------------------
diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java
new file mode 100644
index 0000000..3fc7a31
--- /dev/null
+++ b/sentry-tools/src/main/java/org/apache/sentry/shell/GroupShell.java
@@ -0,0 +1,65 @@
+/*
+ * 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.shell;
+
+import com.budhash.cliche.Command;
+import com.budhash.cliche.Shell;
+import com.budhash.cliche.ShellDependent;
+import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient;
+
+import java.util.List;
+
+/**
+ * Sentry group manipulation for CLI
+ */
+public class GroupShell implements ShellDependent {
+ @Command
+ public List<String> list() {
+ return tools.listGroups();
+ }
+
+ @Command(abbrev = "lr", header = "[groups]",
+ description = "list groups and their roles")
+ public List<String> listRoles() {
+ return tools.listGroupRoles();
+ }
+
+ @Command(description = "Grant role to groups")
+ public void grant(String roleName, String ...groups) {
+ tools.grantGroupsToRole(roleName, groups);
+ }
+
+ @Command(description = "Revoke role from groups")
+ public void revoke(String roleName, String ...groups) {
+ tools.revokeGroupsFromRole(roleName, groups);
+ }
+
+ private final ShellUtil tools;
+ Shell shell;
+
+
+ public GroupShell(SentryPolicyServiceClient sentryClient, String authUser) {
+ this.tools = new ShellUtil(sentryClient, authUser);
+ }
+
+ @Override
+ public void cliSetShell(Shell theShell) {
+ this.shell = theShell;
+ }
+}
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java
----------------------------------------------------------------------
diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java
new file mode 100644
index 0000000..9d8b9d9
--- /dev/null
+++ b/sentry-tools/src/main/java/org/apache/sentry/shell/PrivsShell.java
@@ -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.
+ */
+
+package org.apache.sentry.shell;
+
+import com.budhash.cliche.Command;
+import com.budhash.cliche.Param;
+import com.budhash.cliche.Shell;
+import com.budhash.cliche.ShellDependent;
+import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient;
+
+import java.util.List;
+
+public class PrivsShell implements ShellDependent {
+ private final ShellUtil tools;
+ Shell shell;
+
+ @Command(description = "Grant privilege to role")
+ public void grant(
+ @Param(name = "roleName")
+ String roleName,
+ @Param(name = "privilege",
+ description = "privilege string, e.g. server=s1->db=foo")
+ String privilege) {
+ tools.grantPrivilegeToRole(roleName, privilege);
+ }
+
+ @Command
+ public String list() {
+ return tools.listPrivileges();
+ }
+
+ @Command
+ public List<String> list(
+ @Param(name = "roleName")
+ String roleName) {
+ return tools.listPrivileges(roleName);
+ }
+
+ @Command
+ public void revoke(
+ @Param(name = "roleName")
+ String roleName,
+ @Param(name = "privilege",
+ description = "privilege string, e.g. server=s1->db=foo")
+ String privilege) {
+ tools.revokePrivilegeFromRole(roleName, privilege);
+ }
+
+ public PrivsShell(SentryPolicyServiceClient sentryClient, String authUser) {
+ this.tools = new ShellUtil(sentryClient, authUser);
+ }
+
+ @Override
+ public void cliSetShell(Shell theShell) {
+ this.shell = theShell;
+ }
+}
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java
----------------------------------------------------------------------
diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java
new file mode 100644
index 0000000..9ac6637
--- /dev/null
+++ b/sentry-tools/src/main/java/org/apache/sentry/shell/RolesShell.java
@@ -0,0 +1,72 @@
+/*
+ * 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.shell;
+
+import com.budhash.cliche.Command;
+import com.budhash.cliche.Param;
+import com.budhash.cliche.Shell;
+import com.budhash.cliche.ShellDependent;
+import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient;
+
+import java.util.List;
+
+/**
+ * Sentry roles manipulation for CLI.
+ */
+public class RolesShell implements ShellDependent {
+ @Command(description = "List sentry roles. shows all available roles.")
+ public List<String> list() {
+ return tools.listRoles();
+ }
+
+ @Command(description = "List sentry roles by group")
+ public List<String> list(
+ @Param(name = "groupName", description = "group name for roles")
+ String group) {
+ return tools.listRoles(group);
+ }
+
+ @Command(description = "Create Sentry role(s).")
+ public void create(
+ @Param(name = "roleName", description = "name of role to create")
+ String ...roles) {
+ tools.createRoles(roles);
+ }
+
+ @Command(description = "remove Sentry role(s).")
+ public void remove(
+ @Param(name = "roleName ...", description = "role names to remove")
+ String ...roles) {
+ tools.removeRoles(roles);
+ }
+
+
+ @Override
+ public void cliSetShell(Shell theShell) {
+ this.shell = theShell;
+ }
+
+ private final ShellUtil tools;
+ Shell shell;
+
+ public RolesShell(SentryPolicyServiceClient sentryClient, String authUser) {
+ this.tools = new ShellUtil(sentryClient, authUser);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-tools/src/main/java/org/apache/sentry/shell/SentryCli.java
----------------------------------------------------------------------
diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/SentryCli.java b/sentry-tools/src/main/java/org/apache/sentry/shell/SentryCli.java
new file mode 100644
index 0000000..180d240
--- /dev/null
+++ b/sentry-tools/src/main/java/org/apache/sentry/shell/SentryCli.java
@@ -0,0 +1,205 @@
+/**
+ * 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.shell;
+
+import org.apache.commons.cli.*;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient;
+import org.apache.sentry.service.thrift.SentryServiceClientFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.apache.sentry.service.thrift.ServiceConstants.ClientConfig.SERVER_RPC_ADDRESS;
+import static org.apache.sentry.service.thrift.ServiceConstants.ServerConfig.SECURITY_MODE;
+import static org.apache.sentry.service.thrift.ServiceConstants.ServerConfig.SECURITY_MODE_NONE;
+
+/**
+ * Sentry interactive tool
+ */
+public class SentryCli {
+ private static final Logger log = LoggerFactory.getLogger(SentryCli.class.getName());
+ private static final String LOG4J_CONF = "log4jConf";
+ private final String[] args;
+ private Options options = new Options();
+ private CommandLine cmd;
+
+ private static final String localhost = "localhost";
+ private static final String defaultPort = "8038";
+
+
+ private static final String configOpt = "config";
+ private static final String userOpt = "user";
+ private static final String hostOpt = "host";
+
+ private static final String configEnv = "SENTRY_CONFIG";
+ private static final String hostEnv = "SENTRY_HOST";
+ private static final String userEnv = "SENTRY_USER";
+
+
+ private SentryPolicyServiceClient sentryClient;
+
+ public SentryPolicyServiceClient getSentryClient() {
+ return sentryClient;
+ }
+
+ public String getRequestorName() {
+ return requestorName;
+ }
+
+ private String requestorName;
+
+ public static void main(String[] args) {
+ SentryCli cli = new SentryCli(args);
+ // Create interactive shell and run it
+ TopLevelShell shell = new TopLevelShell(cli.getSentryClient(),
+ cli.getRequestorName());
+ shell.run();
+ }
+
+ /**
+ * Construct SentryCli from arguments
+ * @param args command-line arguments
+ */
+ public SentryCli(String[] args) {
+ this.args = args;
+ options.addOption("h", "help", false, "show help");
+ // file path of sentry-site
+ options.addOption("U", userOpt, true, "auth user");
+ options.addOption("H", hostOpt, true, "host address");
+ options.addOption("c", configOpt, true, "sentry configuration");
+ options.addOption("L", LOG4J_CONF, true, "Location of log4j properties file");
+ CommandLineParser parser = new GnuParser();
+ try {
+ this.cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ help();
+ }
+ if (cmd.hasOption("h")) {
+ help();
+ }
+ init();
+ }
+
+ /**
+ * Parse command-line arguments.
+ */
+ public void parse() {
+ CommandLineParser parser = new GnuParser();
+ try {
+ cmd = parser.parse(options, args);
+ if (cmd.hasOption("h")) {
+ help();
+ }
+ } catch (ParseException e) {
+ log.warn("error in parsing expression", e);
+ help();
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Initialize CLI
+ */
+ private void init() {
+ Map<String, String> env = System.getenv();
+ String log4jconf = cmd.getOptionValue(LOG4J_CONF);
+ if (log4jconf != null && log4jconf.length() > 0) {
+ Properties log4jProperties = new Properties();
+
+ // Firstly load log properties from properties file
+ FileInputStream istream = null;
+ try {
+ istream = new FileInputStream(log4jconf);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ try {
+ log4jProperties.load(istream);
+ istream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ PropertyConfigurator.configure(log4jProperties);
+ }
+
+ String host = cmd.getOptionValue(hostOpt);
+ if (host == null) {
+ host = env.get(hostEnv);
+ }
+
+ String pathConf = cmd.getOptionValue(configOpt);
+ if (pathConf == null) {
+ pathConf = env.get(configEnv);
+ }
+ if (host == null && pathConf == null) {
+ host = localhost + ":" + defaultPort;
+ }
+
+ Configuration conf = new Configuration();
+
+ if (pathConf != null) {
+ conf.addResource(new Path(pathConf));
+ } else {
+ conf.set(SECURITY_MODE, SECURITY_MODE_NONE);
+ }
+
+ if (host != null) {
+ conf.set(SERVER_RPC_ADDRESS, host);
+ }
+
+ requestorName = cmd.getOptionValue(userOpt);
+ if (requestorName == null) {
+ requestorName = env.get(userEnv);
+ }
+ if (requestorName == null) {
+
+ UserGroupInformation ugi = null;
+ try {
+ ugi = UserGroupInformation.getLoginUser();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ requestorName = ugi.getShortUserName();
+ }
+
+ try {
+ sentryClient = SentryServiceClientFactory.create(conf);
+ } catch (Exception e) {
+ System.out.println("Failed to connect to Sentry server: " + e.toString());
+ }
+ }
+
+ private void help() {
+ // This prints out some help
+ HelpFormatter formater = new HelpFormatter();
+ formater.printHelp("sentrycli", options);
+ System.exit(0);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-tools/src/main/java/org/apache/sentry/shell/ShellUtil.java
----------------------------------------------------------------------
diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/ShellUtil.java b/sentry-tools/src/main/java/org/apache/sentry/shell/ShellUtil.java
new file mode 100644
index 0000000..007975c
--- /dev/null
+++ b/sentry-tools/src/main/java/org/apache/sentry/shell/ShellUtil.java
@@ -0,0 +1,335 @@
+/*
+ * 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.shell;
+
+import com.google.common.collect.Sets;
+import org.apache.commons.lang.StringUtils;
+import org.apache.sentry.core.common.exception.SentryUserException;
+import org.apache.sentry.provider.db.service.thrift.*;
+import org.apache.sentry.service.thrift.ServiceConstants;
+
+import java.util.*;
+
+import static org.apache.sentry.service.thrift.SentryServiceUtil.convertTSentryPrivilegeToStr;
+import static org.apache.sentry.service.thrift.SentryServiceUtil.convertToTSentryPrivilege;
+
+/**
+ * ShellUtil implements actual commands
+ */
+class ShellUtil {
+
+ List<String> listRoles() {
+ List<String> roles = null;
+ try {
+ return getRoles();
+ } catch (SentryUserException e) {
+ System.out.println("Error listing roles: " + e.toString());
+ }
+ return new LinkedList<>();
+ }
+
+ List<String> listRoles(String group) {
+ Set<TSentryRole> roles = null;
+ try {
+ roles = sentryClient.listRolesByGroupName(authUser, group);
+ } catch (SentryUserException e) {
+ System.out.println("Error listing roles: " + e.toString());
+ }
+ List<String> result = new ArrayList<>();
+ if (roles == null || roles.isEmpty()) {
+ return result;
+ }
+
+ for(TSentryRole role: roles) {
+ result.add(role.getRoleName());
+ }
+
+ Collections.sort(result);
+ return result;
+ }
+
+ void createRoles(String ...roles) {
+ for (String role: roles) {
+ try {
+ sentryClient.createRole(authUser, role);
+ } catch (SentryUserException e) {
+ System.out.printf("failed to create role %s: %s\n",
+ role, e.toString());
+ }
+ }
+ }
+
+ void removeRoles(String ...roles) {
+ for (String role: roles) {
+ try {
+ sentryClient.dropRole(authUser, role);
+ } catch (SentryUserException e) {
+ System.out.printf("failed to remove role %s: %s\n",
+ role, e.toString());
+ }
+ }
+ }
+
+ List<String> listGroups() {
+ Set<TSentryRole> roles = null;
+
+ try {
+ roles = sentryClient.listRoles(authUser);
+ } catch (SentryUserException e) {
+ System.out.println("Error reading roles: " + e.toString());
+ }
+
+ if (roles == null || roles.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // Set of all group names
+ Set<String> groupNames = new HashSet<>();
+
+ // Get all group names
+ for (TSentryRole role: roles) {
+ for (TSentryGroup group: role.getGroups()) {
+ groupNames.add(group.getGroupName());
+ }
+ }
+
+ List<String> result = new ArrayList<>(groupNames);
+
+ Collections.sort(result);
+ return result;
+ }
+
+ List<String> listGroupRoles() {
+ Set<TSentryRole> roles = null;
+
+ try {
+ roles = sentryClient.listRoles(authUser);
+ } catch (SentryUserException e) {
+ System.out.println("Error reading roles: " + e.toString());
+ }
+
+ if (roles == null || roles.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // Set of all group names
+ Set<String> groupNames = new HashSet<>();
+
+ // Map group to set of roles
+ Map<String, Set<String>> groupInfo = new HashMap<>();
+
+ // Get all group names
+ for (TSentryRole role: roles) {
+ for (TSentryGroup group: role.getGroups()) {
+ String groupName = group.getGroupName();
+ groupNames.add(groupName);
+ Set<String> groupRoles = groupInfo.get(groupName);
+ if (groupRoles != null) {
+ // Add a new or existing role
+ groupRoles.add(role.getRoleName());
+ continue;
+ }
+ // Never seen this group before
+ groupRoles = new HashSet<>();
+ groupRoles.add(role.getRoleName());
+ groupInfo.put(groupName, groupRoles);
+ }
+ }
+
+ List<String> groups = new ArrayList<>(groupNames);
+ Collections.sort(groups);
+
+ // Produce printable result as
+ // group1 = role1, role2, ...
+ // group2 = ...
+ List<String> result = new LinkedList<>();
+ for(String groupName: groups) {
+ result.add(groupName + " = " +
+ StringUtils.join(groupInfo.get(groupName), ", "));
+ }
+ return result;
+ }
+
+ void grantGroupsToRole(String roleName, String ...groups) {
+ try {
+ sentryClient.grantRoleToGroups(authUser, roleName, Sets.newHashSet(groups));
+ } catch (SentryUserException e) {
+ System.out.printf("Failed to gran role %s to groups: %s\n",
+ roleName, e.toString());
+ }
+ }
+
+ void revokeGroupsFromRole(String roleName, String ...groups) {
+ try {
+ sentryClient.revokeRoleFromGroups(authUser, roleName, Sets.newHashSet(groups));
+ } catch (SentryUserException e) {
+ System.out.printf("Failed to revoke role %s to groups: %s\n",
+ roleName, e.toString());
+ }
+ }
+
+ void grantPrivilegeToRole(String roleName, String privilege) {
+ TSentryPrivilege tPriv = convertToTSentryPrivilege(privilege);
+ boolean grantOption = tPriv.getGrantOption().equals(TSentryGrantOption.TRUE);
+ try {
+ if (ServiceConstants.PrivilegeScope.SERVER.toString().equals(tPriv.getPrivilegeScope())) {
+ sentryClient.grantServerPrivilege(authUser, roleName, tPriv.getServerName(),
+ tPriv.getAction(), grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.DATABASE.toString().equals(tPriv.getPrivilegeScope())) {
+ sentryClient.grantDatabasePrivilege(authUser, roleName, tPriv.getServerName(),
+ tPriv.getDbName(), tPriv.getAction(), grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.TABLE.toString().equals(tPriv.getPrivilegeScope())) {
+ sentryClient.grantTablePrivilege(authUser, roleName, tPriv.getServerName(),
+ tPriv.getDbName(), tPriv.getTableName(),
+ tPriv.getAction(), grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.COLUMN.toString().equals(tPriv.getPrivilegeScope())) {
+ sentryClient.grantColumnPrivilege(authUser, roleName, tPriv.getServerName(),
+ tPriv.getDbName(), tPriv.getTableName(),
+ tPriv.getColumnName(), tPriv.getAction(), grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.URI.toString().equals(tPriv.getPrivilegeScope())) {
+ sentryClient.grantURIPrivilege(authUser, roleName, tPriv.getServerName(),
+ tPriv.getURI(), grantOption);
+ return;
+ }
+ } catch (SentryUserException e) {
+ System.out.println("Error granting privilege: " + e.toString());
+ }
+ }
+
+ List<String> listPrivileges(String roleName) {
+ Set<TSentryPrivilege> privileges = null;
+ try {
+ privileges = sentryClient
+ .listAllPrivilegesByRoleName(authUser, roleName);
+ } catch (SentryUserException e) {
+ System.out.println("Failed to list privileges: " + e.toString());
+ }
+
+ if (privileges == null || privileges.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ List<String> result = new LinkedList<>();
+ for (TSentryPrivilege privilege : privileges) {
+ String privilegeStr = convertTSentryPrivilegeToStr(privilege);
+ if (privilegeStr.isEmpty()) {
+ continue;
+ }
+ result.add(privilegeStr);
+ }
+ return result;
+ }
+
+ /**
+ * List all privileges
+ * @return string with privilege info for all roles
+ */
+ String listPrivileges() {
+ List<String> roles = null;
+ try {
+ roles = getRoles();
+ } catch (SentryUserException e) {
+ System.out.println("failed to get role names: " + e.toString());
+ }
+
+ if (roles == null || roles.isEmpty()) {
+ return "";
+ }
+
+ StringBuilder result = new StringBuilder();
+ for (String role: roles) {
+ List<String> privs = listPrivileges(role);
+ if (privs.isEmpty()) {
+ continue;
+ }
+ result.append(role).append(" = ");
+ result.append(StringUtils.join(listPrivileges(role), ",\n\t"));
+ result.append('\n');
+ }
+ return result.toString();
+ }
+
+ void revokePrivilegeFromRole(String roleName, String privilegeStr) {
+ TSentryPrivilege tSentryPrivilege = convertToTSentryPrivilege(privilegeStr);
+ boolean grantOption = tSentryPrivilege.getGrantOption().equals(TSentryGrantOption.TRUE) ? true : false;
+
+ try {
+ if (ServiceConstants.PrivilegeScope.SERVER.toString().equals(tSentryPrivilege.getPrivilegeScope())) {
+ sentryClient.revokeServerPrivilege(authUser, roleName, tSentryPrivilege.getServerName(),
+ grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.DATABASE.toString().equals(tSentryPrivilege.getPrivilegeScope())) {
+ sentryClient.revokeDatabasePrivilege(authUser, roleName, tSentryPrivilege.getServerName(),
+ tSentryPrivilege.getDbName(), tSentryPrivilege.getAction(), grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.TABLE.toString().equals(tSentryPrivilege.getPrivilegeScope())) {
+ sentryClient.revokeTablePrivilege(authUser, roleName, tSentryPrivilege.getServerName(),
+ tSentryPrivilege.getDbName(), tSentryPrivilege.getTableName(),
+ tSentryPrivilege.getAction(), grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.COLUMN.toString().equals(tSentryPrivilege.getPrivilegeScope())) {
+ sentryClient.revokeColumnPrivilege(authUser, roleName, tSentryPrivilege.getServerName(),
+ tSentryPrivilege.getDbName(), tSentryPrivilege.getTableName(),
+ tSentryPrivilege.getColumnName(), tSentryPrivilege.getAction(), grantOption);
+ return;
+ }
+ if (ServiceConstants.PrivilegeScope.URI.toString().equals(tSentryPrivilege.getPrivilegeScope())) {
+ sentryClient.revokeURIPrivilege(authUser, roleName, tSentryPrivilege.getServerName(),
+ tSentryPrivilege.getURI(), grantOption);
+ return;
+ }
+ } catch (SentryUserException e) {
+ System.out.println("failed to revoke privilege: " + e.toString());
+ }
+ }
+
+
+ private List<String>getRoles() throws SentryUserException {
+ // Collect role names
+ Set<TSentryRole> roles = null;
+ roles = sentryClient.listRoles(authUser);
+ List<String> roleNames = new ArrayList<>();
+ for(TSentryRole role: roles) {
+ roleNames.add(role.getRoleName());
+ }
+
+ Collections.sort(roleNames);
+ return roleNames;
+ }
+
+ ShellUtil(SentryPolicyServiceClient sentryClient, String authUser) {
+ this.sentryClient = sentryClient;
+ this.authUser = authUser;
+ }
+
+ private final SentryPolicyServiceClient sentryClient;
+ private final String authUser;
+
+}
http://git-wip-us.apache.org/repos/asf/sentry/blob/44c5d9f4/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java
----------------------------------------------------------------------
diff --git a/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java b/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java
new file mode 100644
index 0000000..ef5313a
--- /dev/null
+++ b/sentry-tools/src/main/java/org/apache/sentry/shell/TopLevelShell.java
@@ -0,0 +1,161 @@
+/**
+ * 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.shell;
+
+import com.budhash.cliche.*;
+import org.apache.sentry.provider.db.service.thrift.SentryPolicyServiceClient;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Top level commands
+ */
+public class TopLevelShell implements ShellDependent, Runnable {
+
+ private final Shell topShell;
+ private final ShellUtil tools;
+ private Shell shell; // top level shell object
+
+ private final String authUser;
+ private final SentryPolicyServiceClient sentryClient;
+
+ TopLevelShell(SentryPolicyServiceClient sentryClient,
+ String authUser) {
+ this.authUser = authUser;
+ this.sentryClient = sentryClient;
+ this.tools = new ShellUtil(sentryClient, authUser);
+ topShell = ShellFactory.createConsoleShell("sentry",
+ "sentry shell\n" +
+ "Enter ?l to list available commands.",
+ this);
+ }
+
+ @Command(description="list, create and remove roles")
+ public void roles() throws IOException {
+ ShellFactory.createSubshell("roles", shell, "roles commands",
+ new RolesShell(sentryClient, authUser)).commandLoop();
+ }
+
+ @Command(description = "list, create and remove groups")
+ public void groups() throws IOException {
+ ShellFactory.createSubshell("groups", shell, "groups commands",
+ new GroupShell(sentryClient, authUser)).commandLoop();
+ }
+
+ @Command(description = "list, create and remove privileges")
+ public void privileges() throws IOException {
+ ShellFactory.createSubshell("privileges", shell, "privileges commands",
+ new PrivsShell(sentryClient, authUser)).commandLoop();
+ }
+
+ @Command(description = "List sentry roles. shows all available roles.")
+ public List<String> listRoles() {
+ return tools.listRoles();
+ }
+
+ @Command(description = "List sentry roles by group")
+ public List<String> listRoles(
+ @Param(name = "groupName")
+ String group) {
+ return tools.listRoles(group);
+ }
+
+ @Command(abbrev = "lg", header = "[groups]",
+ description = "list groups and their roles")
+ public List<String> listGroups() {
+ return tools.listGroupRoles();
+ }
+
+ @Command(description = "Grant role to groups")
+ public void grantRole(
+ @Param(name = "roleName")
+ String roleName,
+ @Param(name = "group...") String ...groups) {
+ tools.grantGroupsToRole(roleName, groups);
+ }
+
+ @Command(abbrev = "grm",
+ description = "Revoke role from groups")
+ public void revokeRole(
+ @Param(name = "roleName")
+ String roleName,
+ @Param(name = "group...")
+ String ...groups) {
+ tools.revokeGroupsFromRole(roleName, groups);
+ }
+
+ @Command(description = "Create Sentry role(s).")
+ public void createRole(
+ @Param(name = "roleName", description = "name of role to create")
+ String ...roles) {
+ tools.createRoles(roles);
+ }
+
+ @Command(abbrev = "rm", description = "remove Sentry role(s).")
+ public void removeRole(
+ @Param(name = "roleName ...", description = "role names to remove")
+ String ...roles) {
+ tools.removeRoles(roles);
+ }
+
+ @Command(description = "list Sentry privileges")
+ public String listPrivileges() {
+ return tools.listPrivileges();
+ }
+
+ @Command(description = "list Sentry privileges")
+ public List<String> listPrivileges(
+ @Param(name = "roleName")
+ String roleName) {
+ return tools.listPrivileges(roleName);
+ }
+
+ @Command(description = "Grant privilege to role")
+ public void grantPrivilege(
+ @Param(name = "roleName")
+ String roleName,
+ @Param(name = "privilege", description = "privilege string, e.g. server=s1->db=foo")
+ String privilege) {
+ tools.grantPrivilegeToRole(roleName, privilege);
+ }
+
+ @Command
+ public void revokePrivilege(
+ @Param(name = "roleName")
+ String roleName,
+ @Param(name = "privilege", description = "privilege string, e.g. server=s1->db=foo")
+ String privilege) {
+ tools.revokePrivilegeFromRole(roleName, privilege);
+ }
+
+ @Override
+ public void cliSetShell(Shell theShell) {
+ this.shell = theShell;
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.topShell.commandLoop();
+ } catch (IOException e) {
+ System.out.println("error: " + e.toString());
+ }
+ }
+}