Initial version of interactive client shell
Project: http://git-wip-us.apache.org/repos/asf/sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/b7903d01 Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/b7903d01 Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/b7903d01 Branch: refs/heads/akolb-ha-cli Commit: b7903d01af0922f3468e37b85f5a3f941b7bd18f Parents: 1151441 Author: Alexander Kolbasov <[email protected]> Authored: Mon Dec 12 12:57:32 2016 -0800 Committer: Alexander Kolbasov <[email protected]> Committed: Wed May 10 23:28:29 2017 -0700 ---------------------------------------------------------------------- .../sentry-binding-hive-follower/pom.xml | 5 + .../sentry/provider/db/cli/GroupShell.java | 65 ++++++ .../sentry/provider/db/cli/RolesShell.java | 72 +++++++ .../sentry/provider/db/cli/SentryCli.java | 172 ++++++++++++++++ .../sentry/provider/db/cli/ShellUtil.java | 205 +++++++++++++++++++ .../sentry/provider/db/cli/TopLevelShell.java | 93 +++++++++ 6 files changed, 612 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/sentry/blob/b7903d01/sentry-binding/sentry-binding-hive-follower/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-hive-follower/pom.xml b/sentry-binding/sentry-binding-hive-follower/pom.xml index bcfb417..51bd482 100644 --- a/sentry-binding/sentry-binding-hive-follower/pom.xml +++ b/sentry-binding/sentry-binding-hive-follower/pom.xml @@ -66,6 +66,11 @@ limitations under the License. <version>${hive.version}</version> <scope>compile</scope> </dependency> + <dependency> + <groupId>com.budhash.cliche</groupId> + <artifactId>cliche-shell</artifactId> + <version>0.9.3</version> + </dependency> </dependencies> </project> http://git-wip-us.apache.org/repos/asf/sentry/blob/b7903d01/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/GroupShell.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/GroupShell.java b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/GroupShell.java new file mode 100644 index 0000000..fe4e52c --- /dev/null +++ b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/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.provider.db.cli; + +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/b7903d01/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/RolesShell.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/RolesShell.java b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/RolesShell.java new file mode 100644 index 0000000..fbd7ac0 --- /dev/null +++ b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/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.provider.db.cli; + +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/b7903d01/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/SentryCli.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/SentryCli.java b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/SentryCli.java new file mode 100644 index 0000000..001397b --- /dev/null +++ b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/SentryCli.java @@ -0,0 +1,172 @@ +/** + * 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.cli; + +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; + +/** + * 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 configOpt = "config"; + private static final String userOpt = "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("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 pathConf = cmd.getOptionValue(configOpt); + if (pathConf == null) { + pathConf = env.get("SENTRY_CONFIG"); + } + if (pathConf == null) { + System.out.println("Missing config file"); + System.exit(1); + } + + 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); + } + Configuration conf = new Configuration(); + conf.addResource(new Path(pathConf)); + + requestorName = cmd.getOptionValue(userOpt, ""); + if (requestorName.isEmpty()) { + 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/b7903d01/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/ShellUtil.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/ShellUtil.java b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/ShellUtil.java new file mode 100644 index 0000000..95d8d4b --- /dev/null +++ b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/ShellUtil.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.provider.db.cli; + +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.SentryPolicyServiceClient; +import org.apache.sentry.provider.db.service.thrift.TSentryGroup; +import org.apache.sentry.provider.db.service.thrift.TSentryRole; + +import java.util.*; + +/** + * ShellUtil implements actual commands + */ +class ShellUtil { + + List<String> listRoles() { + Set<TSentryRole> roles = null; + try { + roles = sentryClient.listRoles(authUser); + } 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; + } + + 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()); + } + } + + + + 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/b7903d01/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/TopLevelShell.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/TopLevelShell.java b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/TopLevelShell.java new file mode 100644 index 0000000..f940e36 --- /dev/null +++ b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/provider/db/cli/TopLevelShell.java @@ -0,0 +1,93 @@ +/** + * 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.cli; + +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="listRoles, create and remove roles") + public void roles() throws IOException { + ShellFactory.createSubshell("roles", shell, "roles commands", + new RolesShell(sentryClient, authUser)).commandLoop(); + } + + @Command(description = "listRoles, create and remove groups") + public void groups() throws IOException { + ShellFactory.createSubshell("groups", shell, "groups commands", + new GroupShell(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(); + } + + @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()); + } + } +}
