sentry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kal...@apache.org
Subject sentry git commit: SENTRY-1480: A upgrade tool to migrate Solr/Sentry permissions. (Hrishikesh Gadre, reviewed by Kalyan Kumar Kalvagadda)
Date Wed, 22 Nov 2017 13:14:02 GMT
Repository: sentry
Updated Branches:
  refs/heads/master b85b323fb -> 9fd29f9df


SENTRY-1480: A upgrade tool to migrate Solr/Sentry permissions. (Hrishikesh Gadre,  reviewed by Kalyan Kumar Kalvagadda)


Project: http://git-wip-us.apache.org/repos/asf/sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/9fd29f9d
Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/9fd29f9d
Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/9fd29f9d

Branch: refs/heads/master
Commit: 9fd29f9dfbe621c498461227472824a087845ac6
Parents: b85b323
Author: Kalyan Kumar Kalvagadda <kkalyan@cloudera.com>
Authored: Wed Nov 22 07:12:32 2017 -0600
Committer: Kalyan Kumar Kalvagadda <kkalyan@cloudera.com>
Committed: Wed Nov 22 07:12:32 2017 -0600

----------------------------------------------------------------------
 .../core/common/utils/PolicyFileConstants.java  |   8 +
 .../sentry/core/common/utils/PolicyFiles.java   |  48 +++
 .../common/utils/StrictStringTokenizer.java     |  59 +++
 .../sentry/core/common/utils/Version.java       | 239 ++++++++++++
 .../sentry/policy/common/PrivilegeUtils.java    |  11 +-
 .../tools/PermissionsMigrationToolCommon.java   | 343 ++++++++++++++++++
 .../tools/PermissionsMigrationToolSolr.java     | 109 ++++++
 .../tools/TestPermissionsMigrationToolSolr.java | 362 +++++++++++++++++++
 8 files changed, 1178 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFileConstants.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFileConstants.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFileConstants.java
index 6b625ff..216d861 100644
--- a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFileConstants.java
+++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFileConstants.java
@@ -16,6 +16,9 @@
  */
 package org.apache.sentry.core.common.utils;
 
+import java.util.Arrays;
+import java.util.List;
+
 public class PolicyFileConstants {
   public static final String DATABASES = "databases";
   public static final String GROUPS = "groups";
@@ -30,6 +33,11 @@ public class PolicyFileConstants {
   public static final String PRIVILEGE_ACTION_NAME = "action";
   public static final String PRIVILEGE_GRANT_OPTION_NAME = "grantoption";
 
+  /**
+   * This constant defines all possible section names in sentry ini file in the expected order
+   */
+  public static final List<String> SECTION_NAMES = Arrays.asList(DATABASES, USERS, GROUPS, ROLES);
+
   private PolicyFileConstants() {
     // Make constructor private to avoid instantiation
   }

http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFiles.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFiles.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFiles.java
index ca2de7a..bba95e4 100644
--- a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFiles.java
+++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PolicyFiles.java
@@ -16,20 +16,27 @@
  */
 package org.apache.sentry.core.common.utils;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.io.IOUtils;
 import org.apache.shiro.config.Ini;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Resources;
 
@@ -37,6 +44,8 @@ public final class PolicyFiles {
 
   private static final Logger LOGGER = LoggerFactory
       .getLogger(PolicyFiles.class);
+  private static final String NL = System.getProperty("line.separator", "\n");
+
 
   public static void copyToDir(File dest, String... resources)
       throws FileNotFoundException, IOException {
@@ -91,6 +100,45 @@ public final class PolicyFiles {
     }
   }
 
+  /**
+   * Save the specified Sentry configuration file to the desired location
+   *
+   * @param iniFile The Sentry configuration ini file to be saved
+   * @param fileSystem The {@linkplain FileSystem} instance to be used
+   * @param path The path on the {@linkplain FileSystem} where the configuration file should be stored.
+   * @throws IOException in case of I/O errors
+   */
+  public static void writeToPath (Ini iniFile, FileSystem fileSystem, Path path) throws IOException {
+    if (fileSystem.exists(path)) {
+      throw new IllegalArgumentException("The specified path " + path + " already exist!");
+    }
+
+    List<String> sectionStrs = new ArrayList<>();
+    for (String sectionName : PolicyFileConstants.SECTION_NAMES) {
+      sectionStrs.add(toString(sectionName, iniFile.getSection(sectionName)));
+    }
+
+    String contents = Joiner.on(NL).join(sectionStrs.iterator());
+    try (OutputStream out = fileSystem.create(path)) {
+      ByteArrayInputStream in = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+      IOUtils.copyBytes(in, out, fileSystem.getConf());
+    }
+  }
+
+  private static String toString(String name, Ini.Section mapping) {
+    if(mapping == null || mapping.isEmpty()) {
+      return "";
+    }
+    Joiner kvJoiner = Joiner.on(" = ");
+    List<String> lines = Lists.newArrayList();
+    lines.add(NL);
+    lines.add("[" + name + "]");
+    for(String key : mapping.keySet()) {
+      lines.add(kvJoiner.join(key, mapping.get(key)));
+    }
+    return Joiner.on(NL).join(lines);
+  }
+
   private PolicyFiles() {
     // Make constructor private to avoid instantiation
   }

http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/StrictStringTokenizer.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/StrictStringTokenizer.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/StrictStringTokenizer.java
new file mode 100644
index 0000000..eab6ffc
--- /dev/null
+++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/StrictStringTokenizer.java
@@ -0,0 +1,59 @@
+/*
+ * 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.core.common.utils;
+
+
+/**
+ * Used for parsing Version strings so we don't have to
+ * use overkill String.split nor StringTokenizer (which silently
+ * skips empty tokens).
+ * Note - implementation of this class is copied from a similar
+ * functionality in Apache Lucene/Solr project.
+ **/
+final class StrictStringTokenizer {
+
+  public StrictStringTokenizer(String s, char delimiter) {
+    this.s = s;
+    this.delimiter = delimiter;
+  }
+
+  public String nextToken() {
+    if (pos < 0) {
+      throw new IllegalStateException("no more tokens");
+    }
+
+    int pos1 = s.indexOf(delimiter, pos);
+    String s1;
+    if (pos1 >= 0) {
+      s1 = s.substring(pos, pos1);
+      pos = pos1+1;
+    } else {
+      s1 = s.substring(pos);
+      pos=-1;
+    }
+
+    return s1;
+  }
+
+  public boolean hasMoreTokens() {
+    return pos >= 0;
+  }
+
+  private final String s;
+  private final char delimiter;
+  private int pos;
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/Version.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/Version.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/Version.java
new file mode 100644
index 0000000..1fc3fc1
--- /dev/null
+++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/Version.java
@@ -0,0 +1,239 @@
+/*
+ * 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.core.common.utils;
+
+import java.text.ParseException;
+import java.util.Locale;
+
+/**
+ * Use by certain classes to match version compatibility
+ * across releases of Sentry. Note - implementation of this
+ * class is copied from similar functionality in Apache Lucene/Solr project.
+ */
+public final class Version {
+
+  public static final Version SENTRY_2_0_0 = new Version(2, 0, 0);
+
+  // To add a new version:
+  //  * Only add above this comment
+
+  /**
+   * Parse a version number of the form {@code "major.minor.bugfix.prerelease"}.
+   *
+   * Part {@code ".bugfix"} and part {@code ".prerelease"} are optional.
+   * Note that this is forwards compatible: the parsed version does not have to exist as
+   * a constant.
+   */
+  public static Version parse(String version) throws ParseException {
+
+    StrictStringTokenizer tokens = new StrictStringTokenizer(version, '.');
+    if (tokens.hasMoreTokens() == false) {
+      throw new ParseException("Version is not in form major.minor.bugfix(.prerelease) (got: " +
+                                version + ")", 0);
+    }
+
+    int major;
+    String token = tokens.nextToken();
+    try {
+      major = Integer.parseInt(token);
+    } catch (NumberFormatException nfe) {
+      ParseException p = new ParseException("Failed to parse major version from \"" + token +
+                                            "\" (got: " + version + ")", 0);
+      p.initCause(nfe);
+      throw p;
+    }
+
+    if (tokens.hasMoreTokens() == false) {
+      throw new ParseException("Version is not in form major.minor.bugfix(.prerelease) (got: " +
+                               version + ")", 0);
+    }
+
+    int minor;
+    token = tokens.nextToken();
+    try {
+      minor = Integer.parseInt(token);
+    } catch (NumberFormatException nfe) {
+      ParseException p = new ParseException("Failed to parse minor version from \"" + token +
+                                            "\" (got: " + version + ")", 0);
+      p.initCause(nfe);
+      throw p;
+    }
+
+    int bugfix = 0;
+    int prerelease = 0;
+    if (tokens.hasMoreTokens()) {
+
+      token = tokens.nextToken();
+      try {
+        bugfix = Integer.parseInt(token);
+      } catch (NumberFormatException nfe) {
+        ParseException p = new ParseException("Failed to parse bugfix version from \"" + token +
+                                              "\" (got: " + version + ")", 0);
+        p.initCause(nfe);
+        throw p;
+      }
+
+      if (tokens.hasMoreTokens()) {
+        token = tokens.nextToken();
+        try {
+          prerelease = Integer.parseInt(token);
+        } catch (NumberFormatException nfe) {
+          ParseException p = new ParseException("Failed to parse prerelease version from \"" +
+                                                token + "\" (got: " + version + ")", 0);
+          p.initCause(nfe);
+          throw p;
+        }
+        if (prerelease == 0) {
+          throw new ParseException("Invalid value " + prerelease +
+                                   " for prerelease; should be 1 or 2 (got: " + version + ")", 0);
+        }
+
+        if (tokens.hasMoreTokens()) {
+          // Too many tokens!
+          throw new ParseException("Version is not in form major.minor.bugfix(.prerelease) (got: "
+                                   + version + ")", 0);
+        }
+      }
+    }
+
+    try {
+      return new Version(major, minor, bugfix, prerelease);
+    } catch (IllegalArgumentException iae) {
+      ParseException pe = new ParseException("failed to parse version string \"" + version +
+                                             "\": " + iae.getMessage(), 0);
+      pe.initCause(iae);
+      throw pe;
+    }
+  }
+
+  /**
+   * Parse the given version number as a constant or dot based version.
+   * <p>This method allows to use {@code "SENTRY_X_Y"} constant names,
+   * or version numbers in the format {@code "x.y.z"}.
+   *
+   * @lucene.internal
+   */
+  public static Version parseLeniently(String version) throws ParseException {
+    String versionOrig = version;
+    version = version.toUpperCase(Locale.ROOT);
+    switch (version) {
+      default:
+        version = version
+          .replaceFirst("^SENTRY_(\\d+)_(\\d+)_(\\d+)$", "$1.$2.$3")
+          .replaceFirst("^SENTRY_(\\d+)_(\\d+)$", "$1.$2.0")
+          .replaceFirst("^SENTRY_(\\d)(\\d)$", "$1.$2.0");
+        try {
+          return parse(version);
+        } catch (ParseException pe) {
+          ParseException pe2 = new ParseException(
+              "failed to parse lenient version string \"" + versionOrig +
+              "\": " + pe.getMessage(), 0);
+          pe2.initCause(pe);
+          throw pe2;
+        }
+    }
+  }
+
+  /** Returns a new version based on raw numbers
+   *
+   *  @lucene.internal */
+  public static Version fromBits(int major, int minor, int bugfix) {
+    return new Version(major, minor, bugfix);
+  }
+
+  /** Major version, the difference between stable and trunk */
+  public final int major;
+  /** Minor version, incremented within the stable branch */
+  public final int minor;
+  /** Bugfix number, incremented on release branches */
+  public final int bugfix;
+  /** Prerelease version, currently 0 (alpha), 1 (beta), or 2 (final) */
+  public final int prerelease;
+
+  // stores the version pieces, with most significant pieces in high bits
+  // ie:  | 1 byte | 1 byte | 1 byte |   2 bits   |
+  //         major   minor    bugfix   prerelease
+  private final int encodedValue;
+
+  private Version(int major, int minor, int bugfix) {
+    this(major, minor, bugfix, 0);
+  }
+
+  private Version(int major, int minor, int bugfix, int prerelease) {
+    this.major = major;
+    this.minor = minor;
+    this.bugfix = bugfix;
+    this.prerelease = prerelease;
+    // NOTE: do not enforce major version so we remain future proof, except to
+    // make sure it fits in the 8 bits we encode it into:
+    if (major > 255 || major < 0) {
+      throw new IllegalArgumentException("Illegal major version: " + major);
+    }
+    if (minor > 255 || minor < 0) {
+      throw new IllegalArgumentException("Illegal minor version: " + minor);
+    }
+    if (bugfix > 255 || bugfix < 0) {
+      throw new IllegalArgumentException("Illegal bugfix version: " + bugfix);
+    }
+    if (prerelease > 2 || prerelease < 0) {
+      throw new IllegalArgumentException("Illegal prerelease version: " + prerelease);
+    }
+    if (prerelease != 0 && (minor != 0 || bugfix != 0)) {
+      throw new IllegalArgumentException(
+          "Prerelease version only supported with major release (got prerelease: "
+          + prerelease + ", minor: " + minor + ", bugfix: " + bugfix + ")");
+    }
+
+    encodedValue = major << 18 | minor << 10 | bugfix << 2 | prerelease;
+
+    assert encodedIsValid();
+  }
+
+  /**
+   * Returns true if this version is the same or after the version from the argument.
+   */
+  public boolean onOrAfter(Version other) {
+    return encodedValue >= other.encodedValue;
+  }
+
+  @Override
+  public String toString() {
+    if (prerelease == 0) {
+      return "" + major + "." + minor + "." + bugfix;
+    }
+    return "" + major + "." + minor + "." + bugfix + "." + prerelease;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return o != null && o instanceof Version && ((Version)o).encodedValue == encodedValue;
+  }
+
+  // Used only by assert:
+  private boolean encodedIsValid() {
+    assert major == ((encodedValue >>> 18) & 0xFF);
+    assert minor == ((encodedValue >>> 10) & 0xFF);
+    assert bugfix == ((encodedValue >>> 2) & 0xFF);
+    assert prerelease == (encodedValue & 0x03);
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return encodedValue;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PrivilegeUtils.java
----------------------------------------------------------------------
diff --git a/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PrivilegeUtils.java b/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PrivilegeUtils.java
index 6628a2f..2e1333e 100644
--- a/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PrivilegeUtils.java
+++ b/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PrivilegeUtils.java
@@ -16,15 +16,24 @@
  */
 package org.apache.sentry.policy.common;
 
+import java.util.Collection;
 import java.util.Set;
 
 import org.apache.shiro.util.PermissionUtils;
+import org.apache.shiro.util.StringUtils;
 
 public class PrivilegeUtils {
   public static Set<String> toPrivilegeStrings(String s) {
     return PermissionUtils.toPermissionStrings(s);
   }
-  
+
+  /**
+   * Transform the specified {@linkplain Set} of privileges to a {@linkplain String} value.
+   */
+  public static String fromPrivilegeStrings (Collection<String> s) {
+    return StringUtils.toDelimitedString(s, String.valueOf(StringUtils.DEFAULT_DELIMITER_CHAR));
+  }
+
   private PrivilegeUtils() {
     // Make constructor private to avoid instantiation
   }

http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolCommon.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolCommon.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolCommon.java
new file mode 100644
index 0000000..8c2ec32
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolCommon.java
@@ -0,0 +1,343 @@
+/**
+ * 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.generic.tools;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.Parser;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.sentry.core.common.ActiveRoleSet;
+import org.apache.sentry.core.common.utils.PolicyFileConstants;
+import org.apache.sentry.core.common.utils.PolicyFiles;
+import org.apache.sentry.core.common.utils.Version;
+import org.apache.sentry.policy.common.PrivilegeUtils;
+import org.apache.sentry.provider.common.ProviderBackendContext;
+import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceClient;
+import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceClientFactory;
+import org.apache.sentry.provider.db.generic.service.thrift.TSentryPrivilege;
+import org.apache.sentry.provider.db.generic.service.thrift.TSentryRole;
+import org.apache.sentry.provider.file.SimpleFileProviderBackend;
+import org.apache.shiro.config.Ini;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Table;
+
+/**
+ * This class provides basic framework required to migrate permissions between different Sentry
+ * versions. Individual components (e.g. SOLR, KAFKA) needs to override the this class
+ * to provide component specific migration functionality.
+ */
+public abstract class PermissionsMigrationToolCommon {
+  private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsMigrationToolCommon.class);
+  public static final String SOLR_SERVICE_NAME = "sentry.service.client.solr.service.name";
+
+  private Version sourceVersion;
+  private Optional<String> confPath = Optional.empty();
+  private Optional<String> policyFile = Optional.empty();
+  private Optional<String> outputFile = Optional.empty();
+  private boolean dryRun = false;
+
+  /**
+   * @return version of Sentry for which the privileges need to be migrated.
+   */
+  public final Version getSourceVersion() {
+    return sourceVersion;
+  }
+
+  /**
+   * This method returns the name of the component for the migration purpose.
+   * @param conf The Sentry configuration
+   * @return the name of the component
+   */
+  protected abstract String getComponent(Configuration conf);
+
+
+  /**
+   * This method returns the name of the service name for the migration purpose.
+   *
+   * @param conf The Sentry configuration
+   * @return the name of the service
+   */
+  protected abstract String getServiceName(Configuration conf);
+
+  /**
+   * Migrate the privileges specified via <code>privileges</code>.
+   *
+   * @param privileges A collection of privileges to be migrated.
+   * @return A collection of migrated privileges
+   *         An empty collection if migration is not necessary for the specified privileges.
+   */
+  protected abstract Collection<String> transformPrivileges (Collection<String> privileges);
+
+  /**
+   *  parse arguments
+   * <pre>
+   *   -s,--source                   Sentry source version
+   *   -c,--sentry_conf <filepath>   sentry config file path
+   *   -p --policy_file <filepath>   sentry (source) policy file path
+   *   -o --output      <filepath>   sentry (target) policy file path
+   *   -d --dry_run                  provides the output the migration for inspection without
+   *                                 making any configuration changes.
+   *   -h,--help                     print usage
+   * </pre>
+   * @param args
+   */
+  protected boolean parseArgs(String [] args) {
+    Options options = new Options();
+
+    Option sourceVersionOpt = new Option("s", "source", true, "Source Sentry version");
+    sourceVersionOpt.setRequired(true);
+    options.addOption(sourceVersionOpt);
+
+    Option sentryConfPathOpt = new Option("c", "sentry_conf", true,
+        "sentry-site.xml file path (only required in case of Sentry service)");
+    sentryConfPathOpt.setRequired(false);
+    options.addOption(sentryConfPathOpt);
+
+    Option sentryPolicyFileOpt = new Option("p", "policy_file", true,
+        "sentry (source) policy file path (only in case of file based Sentry configuration)");
+    sentryPolicyFileOpt.setRequired(false);
+    options.addOption(sentryPolicyFileOpt);
+
+    Option sentryOutputFileOpt = new Option("o", "output", true,
+        "sentry (target) policy file path (only in case of file based Sentry configuration)");
+    sentryOutputFileOpt.setRequired(false);
+    options.addOption(sentryOutputFileOpt);
+
+    Option dryRunOpt = new Option("d", "dry_run", false,
+        "provides the output the migration for inspection without making actual configuration changes");
+    dryRunOpt.setRequired(false);
+    options.addOption(dryRunOpt);
+
+    // help option
+    Option helpOpt = new Option("h", "help", false, "Shell usage");
+    helpOpt.setRequired(false);
+    options.addOption(helpOpt);
+
+    // this Option is parsed first for help option
+    Options helpOptions = new Options();
+    helpOptions.addOption(helpOpt);
+
+    try {
+      Parser parser = new GnuParser();
+
+      // parse help option first
+      CommandLine cmd = parser.parse(helpOptions, args, true);
+      for (Option opt : cmd.getOptions()) {
+        if (opt.getOpt().equals("h")) {
+          // get the help option, print the usage and exit
+          usage(options);
+          return false;
+        }
+      }
+
+      // without help option
+      cmd = parser.parse(options, args);
+
+      String sourceVersionStr = null;
+
+      for (Option opt : cmd.getOptions()) {
+        if (opt.getOpt().equals("s")) {
+          sourceVersionStr = opt.getValue();
+        } else if (opt.getOpt().equals("c")) {
+          confPath = Optional.of(opt.getValue());
+        } else if (opt.getOpt().equals("p")) {
+          policyFile = Optional.of(opt.getValue());
+        }  else if (opt.getOpt().equals("o")) {
+          outputFile = Optional.of(opt.getValue());
+        }  else if (opt.getOpt().equals("d")) {
+          dryRun = true;
+        }
+      }
+
+      sourceVersion = Version.parse(sourceVersionStr);
+
+      if (!(confPath.isPresent() || policyFile.isPresent())) {
+        System.out.println("Please select either file-based Sentry configuration (-p and -o flags)"
+            + " or Sentry service (-c flag) for migration.");
+        usage(options);
+        return false;
+      }
+
+      if (confPath.isPresent() && (policyFile.isPresent() || outputFile.isPresent())) {
+        System.out.println("In order to migrate service based Sentry configuration,"
+            + " do not specify either -p or -o parameters");
+        usage(options);
+        return false;
+      }
+
+      if (!confPath.isPresent() && (policyFile.isPresent() ^ outputFile.isPresent())) {
+        System.out.println("In order to migrate file based Sentry configuration,"
+            + " please make sure to specify both -p and -o parameters.");
+        usage(options);
+        return false;
+      }
+
+    } catch (ParseException | java.text.ParseException pe) {
+      System.out.println(pe.getMessage());
+      usage(options);
+      return false;
+    }
+    return true;
+  }
+
+  // print usage
+  private void usage(Options sentryOptions) {
+    HelpFormatter formatter = new HelpFormatter();
+    formatter.printHelp("sentryMigrationTool", sentryOptions);
+  }
+
+  public void run() throws Exception {
+    if (policyFile.isPresent()) {
+      migratePolicyFile();
+    } else {
+      migrateSentryServiceConfig();
+    }
+  }
+
+  private void migrateSentryServiceConfig() throws Exception {
+    Configuration conf = getSentryConf();
+    String component = getComponent(conf);
+    String serviceName = getServiceName(conf);
+    GenericPrivilegeConverter converter = new GenericPrivilegeConverter(component, serviceName, false);
+
+    // instantiate a client for sentry service.  This sets the ugi, so must
+    // be done before getting the ugi below.
+    try(SentryGenericServiceClient client =
+                SentryGenericServiceClientFactory.create(getSentryConf())) {
+      UserGroupInformation ugi = UserGroupInformation.getLoginUser();
+      String requestorName = ugi.getShortUserName();
+
+      for (TSentryRole r : client.listAllRoles(requestorName, component)) {
+        for (TSentryPrivilege p : client.listAllPrivilegesByRoleName(requestorName,
+            r.getRoleName(), component, serviceName)) {
+
+          Collection<String> privileges = Collections.singleton(converter.toString(p));
+          Collection<String> migrated = transformPrivileges(privileges);
+          if (!migrated.isEmpty()) {
+            LOGGER.info("{} For role {} migrating privileges from {} to {}", getDryRunMessage(), r.getRoleName(),
+                privileges, migrated);
+
+            if (!dryRun) {
+              Collection<TSentryPrivilege> tmp = new ArrayList<>();
+              for (String perm : migrated) {
+                tmp.add(converter.fromString(perm));
+              }
+
+              /*
+               * Note that it is not possible to provide transactional (all-or-nothing) behavior for these configuration
+               * changes since the Sentry client/server protocol does not support. e.g. under certain failure conditions
+               * like crash of Sentry server or network disconnect between client/server, it is possible that the migration
+               * can not complete but can also not be rolled back. Hence this migration tool relies on the fact that privilege
+               * grant/revoke operations are idempotent and hence re-execution of the migration tool will fix any inconsistency
+               * due to such failures.
+               **/
+              for (TSentryPrivilege x : tmp) { // grant new permissions
+                client.grantPrivilege(requestorName, r.getRoleName(), component, x);
+              }
+
+              // Revoke old permission (only if not part of migrated permissions)
+              if (!tmp.contains(p)) {
+                client.revokePrivilege(requestorName, r.getRoleName(), component, p);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private void migratePolicyFile () throws Exception {
+    Configuration conf = getSentryConf();
+    Path sourceFile = new Path (policyFile.get());
+    SimpleFileProviderBackend policyFileBackend = new SimpleFileProviderBackend(conf, sourceFile);
+    ProviderBackendContext ctx = new ProviderBackendContext();
+    policyFileBackend.initialize(ctx);
+
+    Set<String> roles = Sets.newHashSet();
+    Table<String, String, Set<String>> groupRolePrivilegeTable =
+        policyFileBackend.getGroupRolePrivilegeTable();
+
+    Ini output = PolicyFiles.loadFromPath(sourceFile.getFileSystem(conf), sourceFile);
+    Ini.Section rolesSection = output.get(PolicyFileConstants.ROLES);
+
+    for (String groupName : groupRolePrivilegeTable.rowKeySet()) {
+      for (String roleName : policyFileBackend.getRoles(Collections.singleton(groupName), ActiveRoleSet.ALL)) {
+        if (!roles.contains(roleName)) {
+          // Do the actual migration
+          Set<String> privileges = groupRolePrivilegeTable.get(groupName, roleName);
+          Collection<String> migrated = transformPrivileges(privileges);
+
+          if (!migrated.isEmpty()) {
+            LOGGER.info("{} For role {} migrating privileges from {} to {}", getDryRunMessage(),
+                roleName, privileges, migrated);
+            if (!dryRun) {
+              rolesSection.put(roleName, PrivilegeUtils.fromPrivilegeStrings(migrated));
+            }
+          }
+
+          roles.add(roleName);
+        }
+      }
+    }
+
+    if (!dryRun) {
+      Path targetFile = new Path (outputFile.get());
+      PolicyFiles.writeToPath(output, targetFile.getFileSystem(conf), targetFile);
+      LOGGER.info("Successfully saved migrated Sentry policy file at {}", outputFile.get());
+    }
+  }
+
+  private String getDryRunMessage() {
+    return dryRun ? "[Dry Run]" : "";
+  }
+
+  private Configuration getSentryConf() {
+    Configuration conf = new Configuration();
+    if (confPath.isPresent()) {
+      conf.addResource(new Path(confPath.get()));
+    }
+    return conf;
+  }
+
+  @VisibleForTesting
+  public boolean executeConfigTool(String [] args) throws Exception {
+    boolean result = true;
+    if (parseArgs(args)) {
+      run();
+    } else {
+      result = false;
+    }
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolSolr.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolSolr.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolSolr.java
new file mode 100644
index 0000000..5799993
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/PermissionsMigrationToolSolr.java
@@ -0,0 +1,109 @@
+/**
+ * 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.generic.tools;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.core.common.utils.SentryConstants;
+import org.apache.sentry.core.model.solr.validator.SolrPrivilegeValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class provides SOLR specific functionality required for migrating Sentry privileges.
+ */
+public class PermissionsMigrationToolSolr extends PermissionsMigrationToolCommon {
+  private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsMigrationToolSolr.class);
+
+
+  @Override
+  protected String getComponent(Configuration conf) {
+    return "SOLR";
+  }
+
+  @Override
+  protected String getServiceName(Configuration conf) {
+    return conf.get(SOLR_SERVICE_NAME, "service1");
+  }
+
+  @Override
+  protected Collection<String> transformPrivileges(Collection<String> privileges) {
+    List<String> result = new ArrayList<>();
+    boolean migrated = false;
+
+    if (getSourceVersion().major == 1) { // Migrate only Sentry 1.x permissions
+      for (String p : privileges) {
+        SolrPrivilegeValidator v = new SolrPrivilegeValidator();
+        v.validate(p, false);
+
+        if ("collection".equalsIgnoreCase(v.getEntityType()) && "admin".equalsIgnoreCase(v.getEntityName())) {
+          result.add(getPermissionStr("admin", "collections", v.getActionName()));
+          result.add(getPermissionStr("admin", "cores", v.getActionName()));
+          migrated = true;
+        } else if ("collection".equalsIgnoreCase(v.getEntityType()) && "*".equals(v.getEntityName())) {
+          result.add(getPermissionStr("admin", "collections", v.getActionName()));
+          result.add(getPermissionStr("admin", "cores", v.getActionName()));
+          result.add(p);
+          migrated = true;
+        } else {
+          result.add(p);
+        }
+      }
+    }
+
+    return migrated ? result : Collections.emptyList();
+  }
+
+  private String getPermissionStr (String entityType, String entityName, String action) {
+    StringBuilder builder = new StringBuilder();
+    builder.append(entityType);
+    builder.append(SentryConstants.KV_SEPARATOR);
+    builder.append(entityName);
+    if (action != null) {
+      builder.append(SentryConstants.AUTHORIZABLE_SEPARATOR);
+      builder.append(SentryConstants.PRIVILEGE_NAME);
+      builder.append(SentryConstants.KV_SEPARATOR);
+      builder.append(action);
+    }
+    return builder.toString();
+  }
+
+  public static void main(String[] args) throws Exception {
+    PermissionsMigrationToolSolr solrTool = new PermissionsMigrationToolSolr();
+    try {
+      solrTool.executeConfigTool(args);
+    } catch (Exception e) {
+      LOGGER.error(e.getMessage(), e);
+      Throwable current = e;
+      // find the first printable message;
+      while (current != null && current.getMessage() == null) {
+        current = current.getCause();
+      }
+      String error = "";
+      if (current != null && current.getMessage() != null) {
+        error = "Message: " + current.getMessage();
+      }
+      System.out.println("The operation failed. " + error);
+      System.exit(1);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/9fd29f9d/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestPermissionsMigrationToolSolr.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestPermissionsMigrationToolSolr.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestPermissionsMigrationToolSolr.java
new file mode 100644
index 0000000..69c067f
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestPermissionsMigrationToolSolr.java
@@ -0,0 +1,362 @@
+ /**
+ * 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.generic.tools;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.sentry.core.common.exception.SentryUserException;
+import org.apache.sentry.provider.common.ProviderBackendContext;
+import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceIntegrationBase;
+import org.apache.sentry.provider.db.generic.service.thrift.TAuthorizable;
+import org.apache.sentry.provider.db.generic.service.thrift.TSentryPrivilege;
+import org.apache.sentry.provider.db.generic.service.thrift.TSentryRole;
+import org.apache.sentry.provider.file.PolicyFile;
+import org.apache.sentry.provider.file.SimpleFileProviderBackend;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+import com.google.common.collect.Table;
+import com.google.common.io.Files;
+
+public class TestPermissionsMigrationToolSolr extends SentryGenericServiceIntegrationBase {
+  private File confDir;
+  private File confPath;
+  private String requestorName = "";
+  private String service = "service1";
+
+  @Before
+  public void prepareForTest() throws Exception {
+    confDir = Files.createTempDir();
+    confPath = new File(confDir, "sentry-site.xml");
+    if (confPath.createNewFile()) {
+      FileOutputStream to = new FileOutputStream(confPath);
+      conf.writeXml(to);
+      to.close();
+    }
+    requestorName = clientUgi.getShortUserName();//System.getProperty("user.name", "");
+    Set<String> requestorUserGroupNames = Sets.newHashSet(ADMIN_GROUP);
+    setLocalGroupMapping(requestorName, requestorUserGroupNames);
+    // add ADMIN_USER for the after() in SentryServiceIntegrationBase
+    setLocalGroupMapping(ADMIN_USER, requestorUserGroupNames);
+    setLocalGroupMapping("dev", Sets.newHashSet("dev_group"));
+    setLocalGroupMapping("user", Sets.newHashSet("user_group"));
+    writePolicyFile();
+  }
+
+  @After
+  public void clearTestData() throws Exception {
+    FileUtils.deleteQuietly(confDir);
+
+    // clear roles and privileges
+    Set<TSentryRole> tRoles = client.listAllRoles(requestorName, SOLR);
+    for (TSentryRole tRole : tRoles) {
+      String role = tRole.getRoleName();
+      Set<TSentryPrivilege> privileges = client.listAllPrivilegesByRoleName(
+          requestorName, role, SOLR, service);
+      for (TSentryPrivilege privilege : privileges) {
+        client.revokePrivilege(requestorName, role, SOLR, privilege);
+      }
+      client.dropRole(requestorName, role, SOLR);
+    }
+  }
+
+  @Test
+  public void testPermissionsMigrationFromSentrySvc_v1() throws Exception {
+    initializeSentryService();
+
+    String[] args = { "-s", "1.8.0", "-c", confPath.getAbsolutePath()};
+    PermissionsMigrationToolSolr sentryTool = new PermissionsMigrationToolSolr();
+    sentryTool.executeConfigTool(args);
+
+    Map<String, Set<String>> groupMapping = new HashMap<String, Set<String>>();
+    groupMapping.put("admin_role", Sets.newHashSet("admin_group"));
+    groupMapping.put("dev_role", Sets.newHashSet("dev_group"));
+    groupMapping.put("user_role", Sets.newHashSet("user_group"));
+
+    Map<String, Set<String>> privilegeMapping = new HashMap<String, Set<String>>();
+    privilegeMapping.put("admin_role",
+        Sets.newHashSet("admin=collections->action=*", "admin=cores->action=*"));
+    privilegeMapping.put("dev_role",
+        Sets.newHashSet("collection=*->action=*", "admin=collections->action=*", "admin=cores->action=*"));
+    privilegeMapping.put("user_role",
+        Sets.newHashSet("collection=foo->action=*"));
+
+    verifySentryServiceState(groupMapping, privilegeMapping);
+  }
+
+  @Test
+  public void testPermissionsMigrationFromSentryPolicyFile_v1() throws Exception {
+    Path policyFilePath = initializeSentryPolicyFile();
+    Path outputFilePath = Paths.get(confDir.getAbsolutePath(), "sentry-provider_migrated.ini");
+
+    String[] args = { "-s", "1.8.0", "-p", policyFilePath.toFile().getAbsolutePath(),
+                      "-o", outputFilePath.toFile().getAbsolutePath() };
+    PermissionsMigrationToolSolr sentryTool = new PermissionsMigrationToolSolr();
+    assertTrue(sentryTool.executeConfigTool(args));
+
+    Set<String> groups = new HashSet<>();
+    groups.add("admin_group");
+    groups.add("dev_group");
+    groups.add("user_group");
+
+    Map<String, Set<String>> privilegeMapping = new HashMap<String, Set<String>>();
+    privilegeMapping.put("admin_role",
+        Sets.newHashSet("admin=collections->action=*", "admin=cores->action=*"));
+    privilegeMapping.put("dev_role",
+        Sets.newHashSet("collection=*->action=*", "admin=collections->action=*", "admin=cores->action=*"));
+    privilegeMapping.put("user_role",
+        Sets.newHashSet("collection=foo->action=*"));
+
+    verifySentryPolicyFile(groups, privilegeMapping, outputFilePath);
+  }
+
+  @Test
+  // For permissions created with Sentry 2.x, no migration necessary
+  public void testPermissionsMigrationFromSentrySvc_v2() throws Exception {
+    initializeSentryService();
+
+    String[] args = { "-s", "2.0.0", "-c", confPath.getAbsolutePath()};
+    PermissionsMigrationToolSolr sentryTool = new PermissionsMigrationToolSolr();
+    sentryTool.executeConfigTool(args);
+
+    Map<String, Set<String>> groupMapping = new HashMap<String, Set<String>>();
+    groupMapping.put("admin_role", Sets.newHashSet("admin_group"));
+    groupMapping.put("dev_role", Sets.newHashSet("dev_group"));
+    groupMapping.put("user_role", Sets.newHashSet("user_group"));
+
+    Map<String, Set<String>> privilegeMapping = new HashMap<String, Set<String>>();
+    privilegeMapping.put("admin_role",
+        Sets.newHashSet("collection=admin->action=*"));
+    privilegeMapping.put("dev_role",
+        Sets.newHashSet("collection=*->action=*"));
+    privilegeMapping.put("user_role",
+        Sets.newHashSet("collection=foo->action=*"));
+
+    verifySentryServiceState(groupMapping, privilegeMapping);
+  }
+
+  @Test
+  // For permissions created with Sentry 2.x, no migration necessary
+  public void testPermissionsMigrationFromSentryPolicyFile_v2() throws Exception {
+    Path policyFilePath = initializeSentryPolicyFile();
+    Path outputFilePath = Paths.get(confDir.getAbsolutePath(), "sentry-provider_migrated.ini");
+
+    String[] args = { "-s", "2.0.0", "-p", policyFilePath.toFile().getAbsolutePath(),
+                      "-o", outputFilePath.toFile().getAbsolutePath() };
+    PermissionsMigrationToolSolr sentryTool = new PermissionsMigrationToolSolr();
+    assertTrue(sentryTool.executeConfigTool(args));
+
+    Set<String> groups = new HashSet<>();
+    groups.add("admin_group");
+    groups.add("dev_group");
+    groups.add("user_group");
+
+    Map<String, Set<String>> privilegeMapping = new HashMap<String, Set<String>>();
+    privilegeMapping.put("admin_role",
+        Sets.newHashSet("collection=admin->action=*"));
+    privilegeMapping.put("dev_role",
+        Sets.newHashSet("collection=*->action=*"));
+    privilegeMapping.put("user_role",
+        Sets.newHashSet("collection=foo->action=*"));
+
+    verifySentryPolicyFile(groups, privilegeMapping, outputFilePath);
+  }
+
+  @Test
+  public void testDryRunOption() throws Exception {
+    initializeSentryService();
+
+    String[] args = { "-s", "1.8.0", "-c", confPath.getAbsolutePath(), "--dry_run"};
+    PermissionsMigrationToolSolr sentryTool = new PermissionsMigrationToolSolr();
+    sentryTool.executeConfigTool(args);
+
+    Map<String, Set<String>> groupMapping = new HashMap<String, Set<String>>();
+    groupMapping.put("admin_role", Sets.newHashSet("admin_group"));
+    groupMapping.put("dev_role", Sets.newHashSet("dev_group"));
+    groupMapping.put("user_role", Sets.newHashSet("user_group"));
+
+    // No change in the privileges
+    Map<String, Set<String>> privilegeMapping = new HashMap<String, Set<String>>();
+    privilegeMapping.put("admin_role",
+        Sets.newHashSet("collection=admin->action=*"));
+    privilegeMapping.put("dev_role",
+        Sets.newHashSet("collection=*->action=*"));
+    privilegeMapping.put("user_role",
+        Sets.newHashSet("collection=foo->action=*"));
+
+    verifySentryServiceState(groupMapping, privilegeMapping);
+  }
+
+  @Test
+  public void testInvalidToolArguments() throws Exception {
+    PermissionsMigrationToolSolr sentryTool = new PermissionsMigrationToolSolr();
+
+    {
+      String[] args = { "-c", confPath.getAbsolutePath()};
+      assertFalse("The execution should have failed due to missing source version",
+          sentryTool.executeConfigTool(args));
+    }
+
+    {
+      String[] args = { "-s", "1.8.0" };
+      sentryTool.executeConfigTool(args);
+      assertFalse("The execution should have failed due to missing Sentry config file"
+          + " (or policy file) path",
+          sentryTool.executeConfigTool(args));
+    }
+
+    {
+      String[] args = { "-s", "1.8.0", "-p", "/test/path" };
+      sentryTool.executeConfigTool(args);
+      assertFalse("The execution should have failed due to missing Sentry config output file path",
+          sentryTool.executeConfigTool(args));
+    }
+
+    {
+      String[] args = { "-s", "1.8.0", "-c", "/test/path1", "-p", "/test/path2" };
+      sentryTool.executeConfigTool(args);
+      assertFalse("The execution should have failed due to providing both Sentry config file"
+          + " as well as policy file params",
+          sentryTool.executeConfigTool(args));
+    }
+  }
+
+  private void initializeSentryService() throws SentryUserException {
+    // Define an admin role
+    client.createRoleIfNotExist(requestorName, "admin_role", SOLR);
+    client.grantRoleToGroups(requestorName, "admin_role", SOLR, Sets.newHashSet("admin_group"));
+
+    // Define a developer role
+    client.createRoleIfNotExist(requestorName, "dev_role", SOLR);
+    client.grantRoleToGroups(requestorName, "dev_role", SOLR, Sets.newHashSet("dev_group"));
+
+    // Define a user role
+    client.createRoleIfNotExist(requestorName, "user_role", SOLR);
+    client.grantRoleToGroups(requestorName, "user_role", SOLR, Sets.newHashSet("user_group"));
+
+    // Grant permissions
+    client.grantPrivilege(requestorName, "admin_role", SOLR,
+        new TSentryPrivilege(SOLR, "service1",
+            Arrays.asList(new TAuthorizable("collection", "admin")), "*"));
+    client.grantPrivilege(requestorName, "dev_role", SOLR,
+        new TSentryPrivilege(SOLR, "service1",
+            Arrays.asList(new TAuthorizable("collection", "*")), "*"));
+    client.grantPrivilege(requestorName, "user_role", SOLR,
+        new TSentryPrivilege(SOLR, "service1",
+            Arrays.asList(new TAuthorizable("collection", "foo")), "*"));
+  }
+
+  private void verifySentryServiceState(Map<String, Set<String>> groupMapping,
+      Map<String, Set<String>> privilegeMapping) throws SentryUserException {
+    // check roles
+    Set<TSentryRole> tRoles = client.listAllRoles(requestorName, SOLR);
+    assertEquals("Unexpected number of roles", groupMapping.keySet().size(), tRoles.size());
+    Set<String> roles = new HashSet<String>();
+    for (TSentryRole tRole : tRoles) {
+      roles.add(tRole.getRoleName());
+    }
+
+    for (String expectedRole : groupMapping.keySet()) {
+      assertTrue("Didn't find expected role: " + expectedRole, roles.contains(expectedRole));
+    }
+
+    // check groups
+    for (TSentryRole tRole : tRoles) {
+      Set<String> expectedGroups = groupMapping.get(tRole.getRoleName());
+      assertEquals("Group size doesn't match for role: " + tRole.getRoleName(),
+          expectedGroups.size(), tRole.getGroups().size());
+      assertTrue("Group does not contain all expected members for role: " + tRole.getRoleName(),
+          tRole.getGroups().containsAll(expectedGroups));
+    }
+
+    // check privileges
+    GenericPrivilegeConverter convert = new GenericPrivilegeConverter(SOLR, service);
+    for (String role : roles) {
+      Set<TSentryPrivilege> privileges = client.listAllPrivilegesByRoleName(
+          requestorName, role, SOLR, service);
+      Set<String> expectedPrivileges = privilegeMapping.get(role);
+      assertEquals("Privilege set size doesn't match for role: " + role + " Actual permissions : " + privileges,
+          expectedPrivileges.size(), privileges.size());
+
+      Set<String> privilegeStrs = new HashSet<String>();
+      for (TSentryPrivilege privilege : privileges) {
+        privilegeStrs.add(convert.toString(privilege).toLowerCase());
+      }
+
+      for (String expectedPrivilege : expectedPrivileges) {
+        assertTrue("Did not find expected privilege: " + expectedPrivilege + " in " + privilegeStrs,
+            privilegeStrs.contains(expectedPrivilege));
+      }
+    }
+  }
+
+  private Path initializeSentryPolicyFile() throws Exception {
+    PolicyFile file = new PolicyFile();
+
+    file.addRolesToGroup("admin_group", "admin_role");
+    file.addRolesToGroup("dev_group", "dev_role");
+    file.addRolesToGroup("user_group", "user_role");
+
+    file.addPermissionsToRole("admin_role", "collection=admin->action=*");
+    file.addPermissionsToRole("dev_role", "collection=*->action=*");
+    file.addPermissionsToRole("user_role", "collection=foo->action=*");
+
+    Path policyFilePath = Paths.get(confDir.getAbsolutePath(), "sentry-provider.ini");
+    file.write(policyFilePath.toFile());
+
+    return policyFilePath;
+  }
+
+  private void verifySentryPolicyFile (Set<String> groups, Map<String, Set<String>> privilegeMapping,
+      Path policyFilePath) throws IOException {
+    SimpleFileProviderBackend policyFileBackend = new SimpleFileProviderBackend(conf,
+        new org.apache.hadoop.fs.Path(policyFilePath.toUri()));
+    policyFileBackend.initialize(new ProviderBackendContext());
+    Table<String, String, Set<String>> groupRolePrivilegeTable =
+        policyFileBackend.getGroupRolePrivilegeTable();
+
+    assertEquals(groups, groupRolePrivilegeTable.rowKeySet());
+    assertEquals(privilegeMapping.keySet(), groupRolePrivilegeTable.columnKeySet());
+
+    for (String groupName : groupRolePrivilegeTable.rowKeySet()) {
+      for (String roleName : groupRolePrivilegeTable.columnKeySet()) {
+        if (groupRolePrivilegeTable.contains(groupName, roleName)) {
+          Set<String> privileges = groupRolePrivilegeTable.get(groupName, roleName);
+          assertEquals(privilegeMapping.get(roleName), privileges);
+        }
+      }
+    }
+  }
+}


Mime
View raw message