SENTRY-327: Support auth admin delegation via SQL construct 'with grant option' ( Xiaomeng Huang/ Dapeng Sun via Sravya Tirukkovalur)
Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/7b17cef7 Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/7b17cef7 Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/7b17cef7 Branch: refs/heads/master Commit: 7b17cef735e4d35070deb40c2f535c317a1b14ab Parents: 46c506d Author: Sravya Tirukkovalur <[email protected]> Authored: Mon Aug 4 11:37:59 2014 -0700 Committer: Sravya Tirukkovalur <[email protected]> Committed: Mon Aug 4 13:22:10 2014 -0700 ---------------------------------------------------------------------- .../apache/hadoop/hive/SentryHiveConstants.java | 1 - .../hive/ql/exec/SentryGrantRevokeTask.java | 27 +- .../SentryHiveAuthorizationTaskFactoryImpl.java | 6 +- .../TestSentryHiveAuthorizationTaskFactory.java | 19 +- sentry-core/sentry-core-common/pom.xml | 4 + .../sentry/core/common/utils/PathUtils.java | 25 ++ .../sentry/policy/db/DBWildcardPrivilege.java | 26 +- .../db/service/thrift/TSentryGrantOption.java | 48 +++ .../db/service/thrift/TSentryPrivilege.java | 129 ++++++- .../provider/db/SentryGrantDeniedException.java | 25 ++ .../db/service/model/MSentryPrivilege.java | 98 ++++- .../provider/db/service/model/package.jdo | 4 + .../db/service/persistent/SentryStore.java | 134 ++++++- .../thrift/SentryPolicyServiceClient.java | 86 ++++- .../thrift/SentryPolicyStoreProcessor.java | 73 ++-- .../src/main/resources/sentry-db2-1.4.0.sql | 5 +- .../src/main/resources/sentry-derby-1.4.0.sql | 5 +- .../src/main/resources/sentry-mysql-1.4.0.sql | 5 +- .../src/main/resources/sentry-oracle-1.4.0.sql | 5 +- .../main/resources/sentry-postgres-1.4.0.sql | 5 +- .../main/resources/sentry_policy_service.thrift | 12 +- .../service/persistent/TestSentryPrivilege.java | 168 +++++++++ .../db/service/persistent/TestSentryStore.java | 357 ++++++++++++++++++- .../thrift/TestSentryServiceIntegration.java | 54 ++- sentry-provider/sentry-provider-file/pom.xml | 4 - .../TestPrivilegeWithGrantOption.java | 156 ++++++++ 26 files changed, 1368 insertions(+), 113 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/SentryHiveConstants.java ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/SentryHiveConstants.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/SentryHiveConstants.java index 404d5c8..49922f9 100644 --- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/SentryHiveConstants.java +++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/SentryHiveConstants.java @@ -30,5 +30,4 @@ public class SentryHiveConstants { public static final String PARTITION_PRIVS_NOT_SUPPORTED = "Sentry does not support partition level authorization"; public static final String GRANT_REVOKE_NOT_SUPPORTED_ON_OBJECT = "Sentry does not allow grant/revoke on: "; public static final String GRANT_REVOKE_NOT_SUPPORTED_FOR_PRINCIPAL = "Sentry does not allow privileges to be granted/revoked to/from: "; - public static final String GRANT_OPTION_NOT_SUPPORTED = "Sentry does not allow WITH GRANT OPTION"; } http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryGrantRevokeTask.java ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryGrantRevokeTask.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryGrantRevokeTask.java index 4303d88..0b26806 100644 --- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryGrantRevokeTask.java +++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/hadoop/hive/ql/exec/SentryGrantRevokeTask.java @@ -279,16 +279,17 @@ public class SentryGrantRevokeTask extends Task<DDLWork> implements Serializable SentryPolicyServiceClient sentryClient, String subject, String server, GrantDesc desc) throws SentryUserException { return processGrantRevokeDDL(console, sentryClient, subject, - server, true, desc.getPrincipals(), desc.getPrivileges(), desc.getPrivilegeSubjectDesc()); + server, true, desc.getPrincipals(), desc.getPrivileges(), + desc.getPrivilegeSubjectDesc(), desc.isGrantOption()); } - + // For grant option, we use null to stand for revoke the privilege ignore the grant option private int processRevokeDDL(HiveConf conf, LogHelper console, SentryPolicyServiceClient sentryClient, String subject, String server, RevokeDesc desc) throws SentryUserException { return processGrantRevokeDDL(console, sentryClient, subject, server, false, desc.getPrincipals(), desc.getPrivileges(), - desc.getPrivilegeSubjectDesc()); + desc.getPrivilegeSubjectDesc(), null); } private int processShowGrantDDL(HiveConf conf, LogHelper console, SentryPolicyServiceClient sentryClient, @@ -485,8 +486,8 @@ public class SentryGrantRevokeTask extends Task<DDLWork> implements Serializable private static int processGrantRevokeDDL(LogHelper console, SentryPolicyServiceClient sentryClient, String subject, String server, boolean isGrant, List<PrincipalDesc> principals, - List<PrivilegeDesc> privileges, - PrivilegeObjectDesc privSubjectObjDesc) throws SentryUserException { + List<PrivilegeDesc> privileges, PrivilegeObjectDesc privSubjectObjDesc, + Boolean grantOption) throws SentryUserException { if (privileges == null || privileges.size() == 0) { console.printError("No privilege found."); return RETURN_CODE_FAILURE; @@ -535,27 +536,27 @@ public class SentryGrantRevokeTask extends Task<DDLWork> implements Serializable for (PrivilegeDesc privDesc : privileges) { if (isGrant) { if (serverName != null) { - sentryClient.grantServerPrivilege(subject, princ.getName(), serverName); + sentryClient.grantServerPrivilege(subject, princ.getName(), serverName, grantOption); } else if (uriPath != null) { - sentryClient.grantURIPrivilege(subject, princ.getName(), server, uriPath); + sentryClient.grantURIPrivilege(subject, princ.getName(), server, uriPath, grantOption); } else if (tableName == null) { sentryClient.grantDatabasePrivilege(subject, princ.getName(), server, dbName, - toDbSentryAction(privDesc.getPrivilege().getPriv())); + toDbSentryAction(privDesc.getPrivilege().getPriv()), grantOption); } else { sentryClient.grantTablePrivilege(subject, princ.getName(), server, dbName, - tableName, toSentryAction(privDesc.getPrivilege().getPriv())); + tableName, toSentryAction(privDesc.getPrivilege().getPriv()), grantOption); } } else { if (serverName != null) { - sentryClient.revokeServerPrivilege(subject, princ.getName(), serverName); + sentryClient.revokeServerPrivilege(subject, princ.getName(), serverName, grantOption); } else if (uriPath != null) { - sentryClient.revokeURIPrivilege(subject, princ.getName(), server, uriPath); + sentryClient.revokeURIPrivilege(subject, princ.getName(), server, uriPath, grantOption); } else if (tableName == null) { sentryClient.revokeDatabasePrivilege(subject, princ.getName(), server, dbName, - toDbSentryAction(privDesc.getPrivilege().getPriv())); + toDbSentryAction(privDesc.getPrivilege().getPriv()), grantOption); } else { sentryClient.revokeTablePrivilege(subject, princ.getName(), server, dbName, - tableName, toSentryAction(privDesc.getPrivilege().getPriv())); + tableName, toSentryAction(privDesc.getPrivilege().getPriv()), grantOption); } } } http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/SentryHiveAuthorizationTaskFactoryImpl.java ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/SentryHiveAuthorizationTaskFactoryImpl.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/SentryHiveAuthorizationTaskFactoryImpl.java index 56703a1..f38ee91 100644 --- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/SentryHiveAuthorizationTaskFactoryImpl.java +++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/SentryHiveAuthorizationTaskFactoryImpl.java @@ -117,12 +117,12 @@ public class SentryHiveAuthorizationTaskFactoryImpl implements HiveAuthorization List<PrincipalDesc> principalDesc = analyzePrincipalListDef( (ASTNode) ast.getChild(1)); SentryHivePrivilegeObjectDesc privilegeObj = null; - + boolean grantOption = false; if (ast.getChildCount() > 2) { for (int i = 2; i < ast.getChildCount(); i++) { ASTNode astChild = (ASTNode) ast.getChild(i); if (astChild.getType() == HiveParser.TOK_GRANT_WITH_OPTION) { - throw new SemanticException(SentryHiveConstants.GRANT_OPTION_NOT_SUPPORTED); + grantOption = true; } else if (astChild.getType() == HiveParser.TOK_PRIV_OBJECT) { privilegeObj = analyzePrivilegeObject(astChild); } @@ -150,7 +150,7 @@ public class SentryHiveAuthorizationTaskFactoryImpl implements HiveAuthorization } } GrantDesc grantDesc = new GrantDesc(privilegeObj, privilegeDesc, - principalDesc, userName, PrincipalType.USER, false); + principalDesc, userName, PrincipalType.USER, grantOption); return createTask(new DDLWork(inputs, outputs, grantDesc)); } @Override http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-binding/sentry-binding-hive/src/test/java/org/apache/sentry/binding/hive/TestSentryHiveAuthorizationTaskFactory.java ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-hive/src/test/java/org/apache/sentry/binding/hive/TestSentryHiveAuthorizationTaskFactory.java b/sentry-binding/sentry-binding-hive/src/test/java/org/apache/sentry/binding/hive/TestSentryHiveAuthorizationTaskFactory.java index ac0d170..129a6b5 100644 --- a/sentry-binding/sentry-binding-hive/src/test/java/org/apache/sentry/binding/hive/TestSentryHiveAuthorizationTaskFactory.java +++ b/sentry-binding/sentry-binding-hive/src/test/java/org/apache/sentry/binding/hive/TestSentryHiveAuthorizationTaskFactory.java @@ -152,8 +152,21 @@ public class TestSentryHiveAuthorizationTaskFactory { */ @Test public void testGrantRoleTableWithGrantOption() throws Exception { - expectSemanticException("GRANT " + ALL + " ON TABLE " + TABLE + " TO ROLE " + ROLE + - " WITH GRANT OPTION", "Sentry does not allow WITH GRANT OPTION"); + DDLWork work = analyze(parse("GRANT " + ALL + " ON TABLE " + TABLE + " TO ROLE " + ROLE + + " WITH GRANT OPTION")); + GrantDesc grantDesc = work.getGrantDesc(); + Assert.assertNotNull("Grant should not be null", grantDesc); + for (PrincipalDesc principal : assertSize(1, grantDesc.getPrincipals())) { + Assert.assertEquals(PrincipalType.ROLE, principal.getType()); + Assert.assertEquals(ROLE, principal.getName()); + } + for (PrivilegeDesc privilege : assertSize(1, grantDesc.getPrivileges())) { + Assert.assertEquals(Privilege.ALL, privilege.getPrivilege()); + } + Assert.assertTrue("Expected table", grantDesc.getPrivilegeSubjectDesc() + .getTable()); + Assert.assertTrue("Expected grantOption is true", grantDesc.isGrantOption()); + Assert.assertEquals(TABLE, grantDesc.getPrivilegeSubjectDesc().getObject()); } /** @@ -391,4 +404,4 @@ public class TestSentryHiveAuthorizationTaskFactory { Assert.assertEquals(list.toString(), size, list.size()); return list; } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-core/sentry-core-common/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-core/sentry-core-common/pom.xml b/sentry-core/sentry-core-common/pom.xml index f373394..e12469a 100644 --- a/sentry-core/sentry-core-common/pom.xml +++ b/sentry-core/sentry-core-common/pom.xml @@ -29,6 +29,10 @@ limitations under the License. <dependencies> <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cli</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PathUtils.java ---------------------------------------------------------------------- diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PathUtils.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PathUtils.java index 962179f..2e211f8 100644 --- a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PathUtils.java +++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/PathUtils.java @@ -20,9 +20,15 @@ import java.io.File; import java.net.URI; import java.net.URISyntaxException; +import org.apache.commons.lang.text.StrSubstitutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Strings; public class PathUtils { + private static final Logger LOGGER = LoggerFactory + .getLogger(PathUtils.class); /** * URI is a a special case. For URI's, /a implies /a/b. * Therefore the test is "/a/b".startsWith("/a"); @@ -53,6 +59,25 @@ public class PathUtils { return false; } + public static boolean impliesURI(String privilege, String request) { + try { + URI privilegeURI = new URI(new StrSubstitutor(System.getProperties()).replace(privilege)); + URI requestURI = new URI(request); + if(privilegeURI.getScheme() == null || privilegeURI.getPath() == null) { + LOGGER.warn("Privilege URI " + request + " is not valid. Either no scheme or no path."); + return false; + } + if(requestURI.getScheme() == null || requestURI.getPath() == null) { + LOGGER.warn("Request URI " + request + " is not valid. Either no scheme or no path."); + return false; + } + return PathUtils.impliesURI(privilegeURI, requestURI); + } catch (URISyntaxException e) { + LOGGER.warn("Request URI " + request + " is not a URI", e); + return false; + } + } + /** * The URI must be a directory as opposed to a partial * path entry name. To ensure this is true we add a / http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/DBWildcardPrivilege.java ---------------------------------------------------------------------- diff --git a/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/DBWildcardPrivilege.java b/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/DBWildcardPrivilege.java index 896283c..e2de7a7 100644 --- a/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/DBWildcardPrivilege.java +++ b/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/DBWildcardPrivilege.java @@ -21,19 +21,16 @@ package org.apache.sentry.policy.db; -import static org.apache.sentry.provider.file.PolicyFileConstants.AUTHORIZABLE_JOINER; -import static org.apache.sentry.provider.file.PolicyFileConstants.AUTHORIZABLE_SPLITTER; +import static org.apache.sentry.provider.common.ProviderConstants.AUTHORIZABLE_JOINER; +import static org.apache.sentry.provider.common.ProviderConstants.AUTHORIZABLE_SPLITTER; -import java.net.URI; -import java.net.URISyntaxException; import java.util.List; -import org.apache.commons.lang.text.StrSubstitutor; import org.apache.sentry.core.common.utils.PathUtils; import org.apache.sentry.core.model.db.AccessConstants; import org.apache.sentry.core.model.db.DBModelAuthorizable.AuthorizableType; -import org.apache.sentry.policy.common.PrivilegeFactory; import org.apache.sentry.policy.common.Privilege; +import org.apache.sentry.policy.common.PrivilegeFactory; import org.apache.sentry.provider.file.KeyValue; import org.apache.sentry.provider.file.PolicyFileConstants; import org.slf4j.Logger; @@ -142,22 +139,7 @@ public class DBWildcardPrivilege implements Privilege { @VisibleForTesting protected static boolean impliesURI(String privilege, String request) { - try { - URI privilegeURI = new URI(new StrSubstitutor(System.getProperties()).replace(privilege)); - URI requestURI = new URI(request); - if(privilegeURI.getScheme() == null || privilegeURI.getPath() == null) { - LOGGER.warn("Privilege URI " + request + " is not valid. Either no scheme or no path."); - return false; - } - if(requestURI.getScheme() == null || requestURI.getPath() == null) { - LOGGER.warn("Request URI " + request + " is not valid. Either no scheme or no path."); - return false; - } - return PathUtils.impliesURI(privilegeURI, requestURI); - } catch (URISyntaxException e) { - LOGGER.warn("Request URI " + request + " is not a URI", e); - return false; - } + return PathUtils.impliesURI(privilege, request); } @Override http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryGrantOption.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryGrantOption.java b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryGrantOption.java new file mode 100644 index 0000000..856ac21 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryGrantOption.java @@ -0,0 +1,48 @@ +/** + * Autogenerated by Thrift Compiler (0.9.0) + * + * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + * @generated + */ +package org.apache.sentry.provider.db.service.thrift; + + +import java.util.Map; +import java.util.HashMap; +import org.apache.thrift.TEnum; + +public enum TSentryGrantOption implements org.apache.thrift.TEnum { + TRUE(1), + FALSE(0), + UNSET(-1); + + private final int value; + + private TSentryGrantOption(int value) { + this.value = value; + } + + /** + * Get the integer value of this enum value, as defined in the Thrift IDL. + */ + public int getValue() { + return value; + } + + /** + * Find a the enum type by its integer value, as defined in the Thrift IDL. + * @return null if the value is not found. + */ + public static TSentryGrantOption findByValue(int value) { + switch (value) { + case 1: + return TRUE; + case 0: + return FALSE; + case -1: + return UNSET; + default: + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilege.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilege.java b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilege.java index c48e8cc..54b6204 100644 --- a/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilege.java +++ b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilege.java @@ -42,6 +42,7 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg private static final org.apache.thrift.protocol.TField ACTION_FIELD_DESC = new org.apache.thrift.protocol.TField("action", org.apache.thrift.protocol.TType.STRING, (short)7); private static final org.apache.thrift.protocol.TField CREATE_TIME_FIELD_DESC = new org.apache.thrift.protocol.TField("createTime", org.apache.thrift.protocol.TType.I64, (short)8); private static final org.apache.thrift.protocol.TField GRANTOR_PRINCIPAL_FIELD_DESC = new org.apache.thrift.protocol.TField("grantorPrincipal", org.apache.thrift.protocol.TType.STRING, (short)9); + private static final org.apache.thrift.protocol.TField GRANT_OPTION_FIELD_DESC = new org.apache.thrift.protocol.TField("grantOption", org.apache.thrift.protocol.TType.I32, (short)10); private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>(); static { @@ -57,6 +58,7 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg private String action; // required private long createTime; // optional private String grantorPrincipal; // optional + private TSentryGrantOption grantOption; // optional /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ public enum _Fields implements org.apache.thrift.TFieldIdEnum { @@ -67,7 +69,12 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg URI((short)6, "URI"), ACTION((short)7, "action"), CREATE_TIME((short)8, "createTime"), - GRANTOR_PRINCIPAL((short)9, "grantorPrincipal"); + GRANTOR_PRINCIPAL((short)9, "grantorPrincipal"), + /** + * + * @see TSentryGrantOption + */ + GRANT_OPTION((short)10, "grantOption"); private static final Map<String, _Fields> byName = new HashMap<String, _Fields>(); @@ -98,6 +105,8 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg return CREATE_TIME; case 9: // GRANTOR_PRINCIPAL return GRANTOR_PRINCIPAL; + case 10: // GRANT_OPTION + return GRANT_OPTION; default: return null; } @@ -140,7 +149,7 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg // isset id assignments private static final int __CREATETIME_ISSET_ID = 0; private byte __isset_bitfield = 0; - private _Fields optionals[] = {_Fields.DB_NAME,_Fields.TABLE_NAME,_Fields.URI,_Fields.CREATE_TIME,_Fields.GRANTOR_PRINCIPAL}; + private _Fields optionals[] = {_Fields.DB_NAME,_Fields.TABLE_NAME,_Fields.URI,_Fields.CREATE_TIME,_Fields.GRANTOR_PRINCIPAL,_Fields.GRANT_OPTION}; public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; static { Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); @@ -160,6 +169,8 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I64))); tmpMap.put(_Fields.GRANTOR_PRINCIPAL, new org.apache.thrift.meta_data.FieldMetaData("grantorPrincipal", org.apache.thrift.TFieldRequirementType.OPTIONAL, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + tmpMap.put(_Fields.GRANT_OPTION, new org.apache.thrift.meta_data.FieldMetaData("grantOption", org.apache.thrift.TFieldRequirementType.OPTIONAL, + new org.apache.thrift.meta_data.EnumMetaData(org.apache.thrift.protocol.TType.ENUM, TSentryGrantOption.class))); metaDataMap = Collections.unmodifiableMap(tmpMap); org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(TSentryPrivilege.class, metaDataMap); } @@ -173,6 +184,8 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg this.action = ""; + this.grantOption = org.apache.sentry.provider.db.service.thrift.TSentryGrantOption.FALSE; + } public TSentryPrivilege( @@ -213,6 +226,9 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg if (other.isSetGrantorPrincipal()) { this.grantorPrincipal = other.grantorPrincipal; } + if (other.isSetGrantOption()) { + this.grantOption = other.grantOption; + } } public TSentryPrivilege deepCopy() { @@ -234,6 +250,8 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg setCreateTimeIsSet(false); this.createTime = 0; this.grantorPrincipal = null; + this.grantOption = org.apache.sentry.provider.db.service.thrift.TSentryGrantOption.FALSE; + } public String getPrivilegeScope() { @@ -419,6 +437,37 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg } } + /** + * + * @see TSentryGrantOption + */ + public TSentryGrantOption getGrantOption() { + return this.grantOption; + } + + /** + * + * @see TSentryGrantOption + */ + public void setGrantOption(TSentryGrantOption grantOption) { + this.grantOption = grantOption; + } + + public void unsetGrantOption() { + this.grantOption = null; + } + + /** Returns true if field grantOption is set (has been assigned a value) and false otherwise */ + public boolean isSetGrantOption() { + return this.grantOption != null; + } + + public void setGrantOptionIsSet(boolean value) { + if (!value) { + this.grantOption = null; + } + } + public void setFieldValue(_Fields field, Object value) { switch (field) { case PRIVILEGE_SCOPE: @@ -485,6 +534,14 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg } break; + case GRANT_OPTION: + if (value == null) { + unsetGrantOption(); + } else { + setGrantOption((TSentryGrantOption)value); + } + break; + } } @@ -514,6 +571,9 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg case GRANTOR_PRINCIPAL: return getGrantorPrincipal(); + case GRANT_OPTION: + return getGrantOption(); + } throw new IllegalStateException(); } @@ -541,6 +601,8 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg return isSetCreateTime(); case GRANTOR_PRINCIPAL: return isSetGrantorPrincipal(); + case GRANT_OPTION: + return isSetGrantOption(); } throw new IllegalStateException(); } @@ -630,6 +692,15 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg return false; } + boolean this_present_grantOption = true && this.isSetGrantOption(); + boolean that_present_grantOption = true && that.isSetGrantOption(); + if (this_present_grantOption || that_present_grantOption) { + if (!(this_present_grantOption && that_present_grantOption)) + return false; + if (!this.grantOption.equals(that.grantOption)) + return false; + } + return true; } @@ -677,6 +748,11 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg if (present_grantorPrincipal) builder.append(grantorPrincipal); + boolean present_grantOption = true && (isSetGrantOption()); + builder.append(present_grantOption); + if (present_grantOption) + builder.append(grantOption.getValue()); + return builder.toHashCode(); } @@ -768,6 +844,16 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg return lastComparison; } } + lastComparison = Boolean.valueOf(isSetGrantOption()).compareTo(typedOther.isSetGrantOption()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetGrantOption()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.grantOption, typedOther.grantOption); + if (lastComparison != 0) { + return lastComparison; + } + } return 0; } @@ -857,6 +943,16 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg } first = false; } + if (isSetGrantOption()) { + if (!first) sb.append(", "); + sb.append("grantOption:"); + if (this.grantOption == null) { + sb.append("null"); + } else { + sb.append(this.grantOption); + } + first = false; + } sb.append(")"); return sb.toString(); } @@ -978,6 +1074,14 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break; + case 10: // GRANT_OPTION + if (schemeField.type == org.apache.thrift.protocol.TType.I32) { + struct.grantOption = TSentryGrantOption.findByValue(iprot.readI32()); + struct.setGrantOptionIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; default: org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } @@ -1039,6 +1143,13 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg oprot.writeFieldEnd(); } } + if (struct.grantOption != null) { + if (struct.isSetGrantOption()) { + oprot.writeFieldBegin(GRANT_OPTION_FIELD_DESC); + oprot.writeI32(struct.grantOption.getValue()); + oprot.writeFieldEnd(); + } + } oprot.writeFieldStop(); oprot.writeStructEnd(); } @@ -1075,7 +1186,10 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg if (struct.isSetGrantorPrincipal()) { optionals.set(4); } - oprot.writeBitSet(optionals, 5); + if (struct.isSetGrantOption()) { + optionals.set(5); + } + oprot.writeBitSet(optionals, 6); if (struct.isSetDbName()) { oprot.writeString(struct.dbName); } @@ -1091,6 +1205,9 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg if (struct.isSetGrantorPrincipal()) { oprot.writeString(struct.grantorPrincipal); } + if (struct.isSetGrantOption()) { + oprot.writeI32(struct.grantOption.getValue()); + } } @Override @@ -1102,7 +1219,7 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg struct.setServerNameIsSet(true); struct.action = iprot.readString(); struct.setActionIsSet(true); - BitSet incoming = iprot.readBitSet(5); + BitSet incoming = iprot.readBitSet(6); if (incoming.get(0)) { struct.dbName = iprot.readString(); struct.setDbNameIsSet(true); @@ -1123,6 +1240,10 @@ public class TSentryPrivilege implements org.apache.thrift.TBase<TSentryPrivileg struct.grantorPrincipal = iprot.readString(); struct.setGrantorPrincipalIsSet(true); } + if (incoming.get(5)) { + struct.grantOption = TSentryGrantOption.findByValue(iprot.readI32()); + struct.setGrantOptionIsSet(true); + } } } http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryGrantDeniedException.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryGrantDeniedException.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryGrantDeniedException.java new file mode 100644 index 0000000..a470b99 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryGrantDeniedException.java @@ -0,0 +1,25 @@ +/** + * 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; + +public class SentryGrantDeniedException extends SentryAccessDeniedException { + private static final long serialVersionUID = 1962330785835L; + public SentryGrantDeniedException(String msg) { + super(msg); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java index d359abc..5328fff 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java @@ -23,10 +23,9 @@ import java.util.Set; import javax.jdo.annotations.PersistenceCapable; +import org.apache.sentry.core.common.utils.PathUtils; import org.apache.sentry.provider.db.service.persistent.SentryStore; -import com.google.common.base.Strings; - /** * Database backed Sentry Privilege. Any changes to this object * require re-running the maven build so DN an re-enhance. @@ -43,6 +42,7 @@ public class MSentryPrivilege { private String tableName = ""; private String URI = ""; private String action = ""; + private Boolean grantOption = false; // roles this privilege is a part of private Set<MSentryRole> roles; private long createTime; @@ -54,16 +54,38 @@ public class MSentryPrivilege { public MSentryPrivilege(String privilegeName, String privilegeScope, String serverName, String dbName, String tableName, String URI, - String action) { + String action, Boolean grantOption) { this.privilegeScope = privilegeScope; this.serverName = serverName; this.dbName = SentryStore.toNULLCol(dbName); this.tableName = SentryStore.toNULLCol(tableName); this.URI = SentryStore.toNULLCol(URI); this.action = SentryStore.toNULLCol(action); + this.grantOption = grantOption; this.roles = new HashSet<MSentryRole>(); } + public MSentryPrivilege(String privilegeName, String privilegeScope, + String serverName, String dbName, String tableName, String URI, + String action) { + this(privilegeName, privilegeScope, serverName, dbName, tableName, + URI, action, false); + } + + public MSentryPrivilege(MSentryPrivilege other) { + this.privilegeScope = other.privilegeScope; + this.serverName = other.serverName; + this.dbName = SentryStore.toNULLCol(other.dbName); + this.tableName = SentryStore.toNULLCol(other.tableName); + this.URI = SentryStore.toNULLCol(other.URI); + this.action = SentryStore.toNULLCol(other.action); + this.grantOption = other.grantOption; + this.roles = new HashSet<MSentryRole>(); + for (MSentryRole role : other.roles) { + roles.add(role); + } + } + public String getServerName() { return serverName; } @@ -128,6 +150,14 @@ public class MSentryPrivilege { this.privilegeScope = privilegeScope; } + public Boolean getGrantOption() { + return grantOption; + } + + public void setGrantOption(Boolean grantOption) { + this.grantOption = grantOption; + } + public void appendRole(MSentryRole role) { roles.add(role); } @@ -144,10 +174,11 @@ public class MSentryPrivilege { @Override public String toString() { return "MSentryPrivilege [privilegeScope=" + privilegeScope - + ", serverName=" + serverName + ", dbName=" + dbName + + ", serverName=" + serverName + ", dbName=" + dbName + ", tableName=" + tableName + ", URI=" + URI + ", action=" + action + ", roles=[...]" + ", createTime=" - + createTime + ", grantorPrincipal=" + grantorPrincipal + "]"; + + createTime + ", grantorPrincipal=" + grantorPrincipal + + ", grantOption=" + grantOption +"]"; } @Override @@ -160,6 +191,7 @@ public int hashCode() { result = prime * result + ((serverName == null) ? 0 : serverName.hashCode()); result = prime * result + ((tableName == null) ? 0 : tableName.hashCode()); + result = prime * result + ((grantOption == null) ? 0 : grantOption.hashCode()); return result; } @@ -197,8 +229,64 @@ public boolean equals(Object obj) { return false; } else if (!tableName.equals(other.tableName)) return false; + if (grantOption == null) { + if (other.grantOption != null) + return false; + } else if (!grantOption.equals(other.grantOption)) + return false; return true; } + /** + * Return true if this privilege implies other privilege + * Otherwise, return false + * @param other, other privilege + */ + public boolean implies(MSentryPrivilege other) { + // serverName never be null + if (isNULL(serverName) || isNULL(other.serverName)) { + return false; + } else if (!serverName.equals(other.serverName)) { + return false; + } + + // check URI implies + if (!isNULL(URI) && !isNULL(other.URI)) { + if (!PathUtils.impliesURI(URI, other.URI)) { + return false; + } + // if URI is NULL, check dbName and tableName + } else if (isNULL(URI) && isNULL(other.URI)) { + if (!isNULL(dbName)) { + if (isNULL(other.dbName)) { + return false; + } else if (!dbName.equals(other.dbName)) { + return false; + } + } + if (!isNULL(tableName)) { + if (isNULL(other.tableName)) { + return false; + } else if (!tableName.equals(other.tableName)) { + return false; + } + } + // if URI is not equals, return false + } else { + return false; + } + + // check action implies + if (!action.equalsIgnoreCase("*") && + !action.equalsIgnoreCase(other.action)) { + return false; + } + + return true; + } + + private boolean isNULL(String s) { + return SentryStore.isNULL(s); + } } http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo index e3f1372..b39cb18 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo @@ -93,6 +93,7 @@ <field name="tableName"/> <field name="URI"/> <field name="action"/> + <field name="grantOption"/> </index> <field name="privilegeScope"> <column name="PRIVILEGE_SCOPE" length="40" jdbc-type="VARCHAR"/> @@ -118,6 +119,9 @@ <field name="grantorPrincipal"> <column name="GRANTOR_PRINCIPAL" length="4000" jdbc-type="VARCHAR"/> </field> + <field name="grantOption"> + <column name="WITH_GRANT_OPTION" length="1" jdbc-type="CHAR"/> + </field> <field name="roles" mapped-by="privileges"> <collection element-type="org.apache.sentry.provider.db.service.model.MSentryRole"/> </field> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java index a9fe01e..33600e9 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java @@ -40,19 +40,23 @@ import javax.jdo.Transaction; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.SentryUserException; import org.apache.sentry.core.model.db.AccessConstants; import org.apache.sentry.core.model.db.DBModelAuthorizable.AuthorizableType; import org.apache.sentry.provider.common.ProviderConstants; import org.apache.sentry.provider.db.SentryAccessDeniedException; import org.apache.sentry.provider.db.SentryAlreadyExistsException; +import org.apache.sentry.provider.db.SentryGrantDeniedException; import org.apache.sentry.provider.db.SentryInvalidInputException; import org.apache.sentry.provider.db.SentryNoSuchObjectException; import org.apache.sentry.provider.db.service.model.MSentryGroup; import org.apache.sentry.provider.db.service.model.MSentryPrivilege; import org.apache.sentry.provider.db.service.model.MSentryRole; import org.apache.sentry.provider.db.service.model.MSentryVersion; +import org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessor; import org.apache.sentry.provider.db.service.thrift.TSentryActiveRoleSet; import org.apache.sentry.provider.db.service.thrift.TSentryAuthorizable; +import org.apache.sentry.provider.db.service.thrift.TSentryGrantOption; import org.apache.sentry.provider.db.service.thrift.TSentryGroup; import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege; import org.apache.sentry.provider.db.service.thrift.TSentryRole; @@ -89,10 +93,12 @@ public class SentryStore { */ private long commitSequenceId; private final PersistenceManagerFactory pmf; + private Configuration conf; public SentryStore(Configuration conf) throws SentryNoSuchObjectException, SentryAccessDeniedException { commitSequenceId = 0; + this.conf = conf; Properties prop = new Properties(); prop.putAll(ServerConfig.SENTRY_STORE_DEFAULTS); String jdbcUrl = conf.get(ServerConfig.SENTRY_STORE_JDBC_URL, "").trim(); @@ -266,12 +272,15 @@ public class SentryStore { } public CommitContext alterSentryRoleGrantPrivilege(String roleName, TSentryPrivilege privilege) - throws SentryNoSuchObjectException, SentryInvalidInputException { + throws SentryUserException { boolean rollbackTransaction = true; PersistenceManager pm = null; roleName = trimAndLower(roleName); try { pm = openTransaction(); + // first do grant check + grantOptionCheck(pm, privilege); + alterSentryRoleGrantPrivilegeCore(pm, roleName, privilege); CommitContext commit = commitUpdateTransaction(pm); rollbackTransaction = false; @@ -332,12 +341,15 @@ public class SentryStore { } public CommitContext alterSentryRoleRevokePrivilege(String roleName, - TSentryPrivilege tPrivilege) throws SentryNoSuchObjectException, SentryInvalidInputException { + TSentryPrivilege tPrivilege) throws SentryUserException { boolean rollbackTransaction = true; PersistenceManager pm = null; roleName = safeTrimLower(roleName); try { pm = openTransaction(); + // first do revoke check + grantOptionCheck(pm, tPrivilege); + alterSentryRoleRevokePrivilegeCore(pm, roleName, tPrivilege); CommitContext commit = commitUpdateTransaction(pm); @@ -369,7 +381,17 @@ public class SentryStore { mPrivilege = (MSentryPrivilege) pm.detachCopy(mPrivilege); } - Set<MSentryPrivilege> privilegeGraph = Sets.newHashSet(mPrivilege); + Set<MSentryPrivilege> privilegeGraph = Sets.newHashSet(); + if (mPrivilege.getGrantOption() != null) { + privilegeGraph.add(mPrivilege); + } else { + MSentryPrivilege mTure = new MSentryPrivilege(mPrivilege); + mTure.setGrantOption(true); + privilegeGraph.add(mTure); + MSentryPrivilege mFalse = new MSentryPrivilege(mPrivilege); + mFalse.setGrantOption(false); + privilegeGraph.add(mFalse); + } // Get the privilege graph populateChildren(Sets.newHashSet(roleName), mPrivilege, privilegeGraph); for (MSentryPrivilege childPriv : privilegeGraph) { @@ -472,9 +494,10 @@ public class SentryStore { } else { filters.append(" && (dbName != \"__NULL__\" || URI != \"__NULL__\")"); } + query.setFilter(filters.toString()); query - .setResult("privilegeScope, serverName, dbName, tableName, URI, action, grantorPrincipal"); + .setResult("privilegeScope, serverName, dbName, tableName, URI, action, grantorPrincipal, grantOption"); Set<MSentryPrivilege> privileges = new HashSet<MSentryPrivilege>(); for (Object[] privObj : (List<Object[]>) query.execute()) { MSentryPrivilege priv = new MSentryPrivilege(); @@ -485,6 +508,7 @@ public class SentryStore { priv.setURI((String) privObj[4]); priv.setAction((String) privObj[5]); priv.setGrantorPrincipal((String) privObj[6]); + priv.setGrantOption((Boolean) privObj[7]); privileges.add(priv); } rollbackTransaction = false; @@ -498,14 +522,22 @@ public class SentryStore { } private MSentryPrivilege getMSentryPrivilege(TSentryPrivilege tPriv, PersistenceManager pm) { - Query query = pm.newQuery(MSentryPrivilege.class); + Query query = pm.newQuery(MSentryPrivilege.class); query.setFilter("this.serverName == \"" + toNULLCol(tPriv.getServerName()) + "\" " + "&& this.dbName == \"" + toNULLCol(tPriv.getDbName()) + "\" " + "&& this.tableName == \"" + toNULLCol(tPriv.getTableName()) + "\" " + "&& this.URI == \"" + toNULLCol(tPriv.getURI()) + "\" " + + "&& this.grantOption == grantOption " + "&& this.action == \"" + toNULLCol(tPriv.getAction().toLowerCase()) + "\""); + query.declareParameters("Boolean grantOption"); query.setUnique(true); - Object obj = query.execute(); + Boolean grantOption = null; + if (tPriv.getGrantOption().equals(TSentryGrantOption.TRUE)) { + grantOption = true; + } else if (tPriv.getGrantOption().equals(TSentryGrantOption.FALSE)) { + grantOption = false; + } + Object obj = query.execute(grantOption); if (obj != null) return (MSentryPrivilege) obj; return null; @@ -887,6 +919,21 @@ public class SentryStore { } } + private Set<MSentryRole> getRolesForGroups(PersistenceManager pm, Set<String> groups) { + Set<MSentryRole> result = new HashSet<MSentryRole>(); + Query query = pm.newQuery(MSentryGroup.class); + query.setFilter("this.groupName == t"); + query.declareParameters("java.lang.String t"); + query.setUnique(true); + for (String group : groups) { + MSentryGroup sentryGroup = (MSentryGroup) query.execute(group.trim()); + if (sentryGroup != null) { + result = sentryGroup.getRoles(); + } + } + return result; + } + public Set<String> listAllSentryPrivilegesForProvider(Set<String> groups, TSentryActiveRoleSet roleSet) throws SentryInvalidInputException { return listSentryPrivilegesForProvider(groups, roleSet, null); } @@ -1013,6 +1060,11 @@ public class SentryStore { privilege.setTableName(fromNULLCol(mSentryPrivilege.getTableName())); privilege.setURI(fromNULLCol(mSentryPrivilege.getURI())); privilege.setGrantorPrincipal(mSentryPrivilege.getGrantorPrincipal()); + if (mSentryPrivilege.getGrantOption() != null) { + privilege.setGrantOption(TSentryGrantOption.valueOf(mSentryPrivilege.getGrantOption().toString().toUpperCase())); + } else { + privilege.setGrantOption(TSentryGrantOption.UNSET); + } return privilege; } @@ -1032,6 +1084,11 @@ public class SentryStore { mSentryPrivilege.setCreateTime(System.currentTimeMillis()); mSentryPrivilege.setGrantorPrincipal(safeTrim(privilege.getGrantorPrincipal())); mSentryPrivilege.setURI(toNULLCol(safeTrim(privilege.getURI()))); + if ( !privilege.getGrantOption().equals(TSentryGrantOption.UNSET) ) { + mSentryPrivilege.setGrantOption(Boolean.valueOf(privilege.getGrantOption().toString())); + } else { + mSentryPrivilege.setGrantOption(null); + } return mSentryPrivilege; } private static String safeTrim(String s) { @@ -1281,5 +1338,68 @@ public class SentryStore { public static boolean isNULL(String s) { return Strings.isNullOrEmpty(s) || s.equals(NULL_COL); - } + } + + /** + * Grant option check + * @param pm + * @param privilege + * @throws SentryUserException + */ + private void grantOptionCheck(PersistenceManager pm, TSentryPrivilege privilege) + throws SentryUserException { + MSentryPrivilege mPrivilege = convertToMSentryPrivilege(privilege); + String grantorPrincipal = mPrivilege.getGrantorPrincipal(); + if (grantorPrincipal == null) { + throw new SentryInvalidInputException("grantorPrincipal should not be null"); + } + Set<String> groups = SentryPolicyStoreProcessor.getGroupsFromUserName(conf, grantorPrincipal); + if (groups == null || groups.isEmpty()) { + throw new SentryGrantDeniedException(grantorPrincipal + + " has no grant!"); + } + + // if grantor is in adminGroup, don't need to do check + Set<String> admins = getAdminGroups(); + boolean isAdminGroup = false; + if (admins != null && !admins.isEmpty()) { + for (String g : groups) { + if (admins.contains(g)) { + isAdminGroup = true; + break; + } + } + } + + if (!isAdminGroup) { + boolean hasGrant = false; + Set<MSentryRole> roles = getRolesForGroups(pm, groups); + if (roles != null && !roles.isEmpty()) { + for (MSentryRole role: roles) { + Set<MSentryPrivilege> privilegeSet = role.getPrivileges(); + if (privilegeSet != null && !privilegeSet.isEmpty()) { + // if role has a privilege p with grant option + // and mPrivilege is a child privilege of p + for (MSentryPrivilege p : privilegeSet) { + if (p.getGrantOption() && p.implies(mPrivilege)) { + hasGrant = true; + break; + } + } + } + } + } + + if (!hasGrant) { + throw new SentryGrantDeniedException(grantorPrincipal + + " has no grant!"); + } + } + } + + // get adminGroups from conf + private Set<String> getAdminGroups() { + return Sets.newHashSet(conf.getStrings( + ServerConfig.ADMIN_GROUPS, new String[]{})); + } } http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java index 5fd4f8f..5b532ce 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java @@ -233,7 +233,7 @@ public class SentryPolicyServiceClient { } public Set<TSentryPrivilege> listAllPrivilegesByRoleName(String requestorUserName, String roleName) - throws SentryUserException { + throws SentryUserException { return listPrivilegesByRoleName(requestorUserName, roleName, null); } @@ -284,6 +284,13 @@ public class SentryPolicyServiceClient { PrivilegeScope.URI, server, uri, null, null, AccessConstants.ALL); } + public void grantURIPrivilege(String requestorUserName, + String roleName, String server, String uri, Boolean grantOption) + throws SentryUserException { + grantPrivilege(requestorUserName, roleName, + PrivilegeScope.URI, server, uri, null, null, AccessConstants.ALL, grantOption); + } + public void grantServerPrivilege(String requestorUserName, String roleName, String server) throws SentryUserException { @@ -291,6 +298,13 @@ public class SentryPolicyServiceClient { PrivilegeScope.SERVER, server, null, null, null, AccessConstants.ALL); } + public void grantServerPrivilege(String requestorUserName, + String roleName, String server, Boolean grantOption) + throws SentryUserException { + grantPrivilege(requestorUserName, roleName, + PrivilegeScope.SERVER, server, null, null, null, AccessConstants.ALL, grantOption); + } + public void grantDatabasePrivilege(String requestorUserName, String roleName, String server, String db, String action) throws SentryUserException { @@ -298,6 +312,13 @@ public class SentryPolicyServiceClient { PrivilegeScope.DATABASE, server, null, db, null, action); } + public void grantDatabasePrivilege(String requestorUserName, + String roleName, String server, String db, String action, Boolean grantOption) + throws SentryUserException { + grantPrivilege(requestorUserName, roleName, + PrivilegeScope.DATABASE, server, null, db, null, action, grantOption); + } + public void grantTablePrivilege(String requestorUserName, String roleName, String server, String db, String table, String action) throws SentryUserException { @@ -306,6 +327,13 @@ public class SentryPolicyServiceClient { db, table, action); } + public void grantTablePrivilege(String requestorUserName, + String roleName, String server, String db, String table, String action, Boolean grantOption) + throws SentryUserException { + grantPrivilege(requestorUserName, roleName, PrivilegeScope.TABLE, server, + null, db, table, action, grantOption); + } + private TSentryAuthorizable setupSentryAuthorizable( List<? extends Authorizable> authorizable) { TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable(); @@ -328,9 +356,15 @@ public class SentryPolicyServiceClient { return tSentryAuthorizable; } + private void grantPrivilege(String requestorUserName, String roleName, + PrivilegeScope scope, String serverName, String uri, String db, + String table, String action) throws SentryUserException { + grantPrivilege(requestorUserName, roleName, scope, serverName, uri, + db, table, action, false); + } private void grantPrivilege(String requestorUserName, - String roleName, PrivilegeScope scope, String serverName, String uri, String db, String table, String action) + String roleName, PrivilegeScope scope, String serverName, String uri, String db, String table, String action, Boolean grantOption) throws SentryUserException { TAlterSentryRoleGrantPrivilegeRequest request = new TAlterSentryRoleGrantPrivilegeRequest(); request.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT); @@ -345,6 +379,7 @@ public class SentryPolicyServiceClient { privilege.setAction(action); privilege.setGrantorPrincipal(requestorUserName); privilege.setCreateTime(System.currentTimeMillis()); + privilege.setGrantOption(convertTSentryGrantOption(grantOption)); request.setPrivilege(privilege); try { TAlterSentryRoleGrantPrivilegeResponse response = client.alter_sentry_role_grant_privilege(request); @@ -361,6 +396,13 @@ public class SentryPolicyServiceClient { PrivilegeScope.URI, server, uri, null, null, AccessConstants.ALL); } + public void revokeURIPrivilege(String requestorUserName, + String roleName, String server, String uri, Boolean grantOption) + throws SentryUserException { + revokePrivilege(requestorUserName, roleName, + PrivilegeScope.URI, server, uri, null, null, AccessConstants.ALL, grantOption); + } + public void revokeServerPrivilege(String requestorUserName, String roleName, String server) throws SentryUserException { @@ -368,6 +410,13 @@ public class SentryPolicyServiceClient { PrivilegeScope.SERVER, server, null, null, null, AccessConstants.ALL); } + public void revokeServerPrivilege(String requestorUserName, + String roleName, String server, Boolean grantOption) + throws SentryUserException { + revokePrivilege(requestorUserName, roleName, + PrivilegeScope.SERVER, server, null, null, null, AccessConstants.ALL, grantOption); + } + public void revokeDatabasePrivilege(String requestorUserName, String roleName, String server, String db, String action) throws SentryUserException { @@ -375,6 +424,13 @@ public class SentryPolicyServiceClient { PrivilegeScope.DATABASE, server, null, db, null, action); } + public void revokeDatabasePrivilege(String requestorUserName, + String roleName, String server, String db, String action, Boolean grantOption) + throws SentryUserException { + revokePrivilege(requestorUserName, roleName, + PrivilegeScope.DATABASE, server, null, db, null, action, grantOption); + } + public void revokeTablePrivilege(String requestorUserName, String roleName, String server, String db, String table, String action) throws SentryUserException { @@ -383,9 +439,23 @@ public class SentryPolicyServiceClient { db, table, action); } + public void revokeTablePrivilege(String requestorUserName, + String roleName, String server, String db, String table, String action, Boolean grantOption) + throws SentryUserException { + revokePrivilege(requestorUserName, roleName, + PrivilegeScope.TABLE, server, null, + db, table, action, grantOption); + } + private void revokePrivilege(String requestorUserName, String roleName, PrivilegeScope scope, String serverName, String uri, String db, String table, String action) throws SentryUserException { + this.revokePrivilege(requestorUserName, roleName, scope, serverName, uri, db, table, action, false); + } + + private void revokePrivilege(String requestorUserName, String roleName, + PrivilegeScope scope, String serverName, String uri, String db, String table, String action, Boolean grantOption) + throws SentryUserException { TAlterSentryRoleRevokePrivilegeRequest request = new TAlterSentryRoleRevokePrivilegeRequest(); request.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT); request.setRequestorUserName(requestorUserName); @@ -399,6 +469,7 @@ public class SentryPolicyServiceClient { privilege.setAction(action); privilege.setGrantorPrincipal(requestorUserName); privilege.setCreateTime(System.currentTimeMillis()); + privilege.setGrantOption(convertTSentryGrantOption(grantOption)); request.setPrivilege(privilege); try { TAlterSentryRoleRevokePrivilegeResponse response = client.alter_sentry_role_revoke_privilege(request); @@ -408,6 +479,17 @@ public class SentryPolicyServiceClient { } } + private TSentryGrantOption convertTSentryGrantOption(Boolean grantOption) { + if (grantOption == null) { + return TSentryGrantOption.UNSET; + } else if (grantOption.equals(true)) { + return TSentryGrantOption.TRUE; + } else if (grantOption.equals(false)) { + return TSentryGrantOption.FALSE; + } + return TSentryGrantOption.FALSE; + } + public Set<String> listPrivilegesForProvider(Set<String> groups, ActiveRoleSet roleSet, Authorizable... authorizable) throws SentryUserException { TSentryActiveRoleSet thriftRoleSet = new TSentryActiveRoleSet(roleSet.isAll(), roleSet.getRoles()); http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java index 5848e30..f227a02 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java @@ -173,8 +173,6 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface { TAlterSentryRoleGrantPrivilegeResponse response = new TAlterSentryRoleGrantPrivilegeResponse(); try { - authorize(request.getRequestorUserName(), - getRequestorGroups(request.getRequestorUserName())); CommitContext commitContext = sentryStore.alterSentryRoleGrantPrivilege(request.getRoleName(), request.getPrivilege()); response.setStatus(Status.OK()); @@ -207,18 +205,16 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface { (TAlterSentryRoleRevokePrivilegeRequest request) throws TException { TAlterSentryRoleRevokePrivilegeResponse response = new TAlterSentryRoleRevokePrivilegeResponse(); try { - authorize(request.getRequestorUserName(), - getRequestorGroups(request.getRequestorUserName())); CommitContext commitContext = sentryStore.alterSentryRoleRevokePrivilege(request.getRoleName(), request.getPrivilege()); response.setStatus(Status.OK()); notificationHandlerInvoker.alter_sentry_role_revoke_privilege(commitContext, request, response); } catch (SentryNoSuchObjectException e) { - String msg = "Privilege: [server=" + request.getPrivilege().getServerName() + - ",db=" + request.getPrivilege().getDbName() + - ",table=" + request.getPrivilege().getTableName() + - ",URI=" + request.getPrivilege().getURI() + + String msg = "Privilege: [server=" + request.getPrivilege().getServerName() + + ",db=" + request.getPrivilege().getDbName() + + ",table=" + request.getPrivilege().getTableName() + + ",URI=" + request.getPrivilege().getURI() + ",action=" + request.getPrivilege().getAction() + "] doesn't exist."; LOGGER.error(msg, e); response.setStatus(Status.NoSuchObject(msg, e)); @@ -442,35 +438,40 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface { // retrieve the group mapping for the given user name private Set<String> getRequestorGroups(String userName) throws SentryUserException { - String groupMapping = conf.get(ServerConfig.SENTRY_STORE_GROUP_MAPPING, - ServerConfig.SENTRY_STORE_GROUP_MAPPING_DEFAULT); - String authResoruce = conf - .get(ServerConfig.SENTRY_STORE_GROUP_MAPPING_RESOURCE); + return getGroupsFromUserName(this.conf, userName); + } - // load the group mapping provider class - GroupMappingService groupMappingService; - try { - Constructor<?> constrctor = Class.forName(groupMapping).getDeclaredConstructor( - Configuration.class, String.class); - constrctor.setAccessible(true); - groupMappingService = (GroupMappingService) constrctor.newInstance(new Object[] { conf, - authResoruce }); - } catch (NoSuchMethodException e) { - throw new SentryUserException("Unable to instantiate group mapping", e); - } catch (SecurityException e) { - throw new SentryUserException("Unable to instantiate group mapping", e); - } catch (ClassNotFoundException e) { - throw new SentryUserException("Unable to instantiate group mapping", e); - } catch (InstantiationException e) { - throw new SentryUserException("Unable to instantiate group mapping", e); - } catch (IllegalAccessException e) { - throw new SentryUserException("Unable to instantiate group mapping", e); - } catch (IllegalArgumentException e) { - throw new SentryUserException("Unable to instantiate group mapping", e); - } catch (InvocationTargetException e) { - throw new SentryUserException("Unable to instantiate group mapping", e); - } - return groupMappingService.getGroups(userName); + public static Set<String> getGroupsFromUserName(Configuration conf, + String userName) throws SentryUserException { + String groupMapping = conf.get(ServerConfig.SENTRY_STORE_GROUP_MAPPING, + ServerConfig.SENTRY_STORE_GROUP_MAPPING_DEFAULT); + String authResoruce = conf + .get(ServerConfig.SENTRY_STORE_GROUP_MAPPING_RESOURCE); + + // load the group mapping provider class + GroupMappingService groupMappingService; + try { + Constructor<?> constrctor = Class.forName(groupMapping) + .getDeclaredConstructor(Configuration.class, String.class); + constrctor.setAccessible(true); + groupMappingService = (GroupMappingService) constrctor + .newInstance(new Object[] { conf, authResoruce }); + } catch (NoSuchMethodException e) { + throw new SentryUserException("Unable to instantiate group mapping", e); + } catch (SecurityException e) { + throw new SentryUserException("Unable to instantiate group mapping", e); + } catch (ClassNotFoundException e) { + throw new SentryUserException("Unable to instantiate group mapping", e); + } catch (InstantiationException e) { + throw new SentryUserException("Unable to instantiate group mapping", e); + } catch (IllegalAccessException e) { + throw new SentryUserException("Unable to instantiate group mapping", e); + } catch (IllegalArgumentException e) { + throw new SentryUserException("Unable to instantiate group mapping", e); + } catch (InvocationTargetException e) { + throw new SentryUserException("Unable to instantiate group mapping", e); + } + return groupMappingService.getGroups(userName); } @Override http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/resources/sentry-db2-1.4.0.sql ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-db2-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-db2-1.4.0.sql index 3886d29..c1a2778 100644 --- a/sentry-provider/sentry-provider-db/src/main/resources/sentry-db2-1.4.0.sql +++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-db2-1.4.0.sql @@ -24,7 +24,8 @@ CREATE TABLE SENTRY_DB_PRIVILEGE GRANTOR_PRINCIPAL VARCHAR(4000), PRIVILEGE_SCOPE VARCHAR(40), "SERVER_NAME" VARCHAR(4000), - "TABLE_NAME" VARCHAR(4000) + "TABLE_NAME" VARCHAR(4000), + WITH_GRANT_OPTION CHAR(1) NOT NULL ); ALTER TABLE SENTRY_DB_PRIVILEGE ADD CONSTRAINT SENTRY_DB_PRIVILEGE_PK PRIMARY KEY (DB_PRIVILEGE_ID); @@ -78,7 +79,7 @@ CREATE TABLE "SENTRY_VERSION" ( ALTER TABLE SENTRY_VERSION ADD CONSTRAINT SENTRY_VERSION_PK PRIMARY KEY (VER_ID); -- Constraints for table SENTRY_DB_PRIVILEGE for class(es) [org.apache.sentry.provider.db.service.model.MSentryPrivilege] -CREATE UNIQUE INDEX SENTRYPRIVILEGENAME ON SENTRY_DB_PRIVILEGE ("SERVER_NAME",DB_NAME,"TABLE_NAME",URI,"ACTION"); +CREATE UNIQUE INDEX SENTRYPRIVILEGENAME ON SENTRY_DB_PRIVILEGE ("SERVER_NAME",DB_NAME,"TABLE_NAME",URI,"ACTION",WITH_GRANT_OPTION); -- Constraints for table SENTRY_ROLE for class(es) [org.apache.sentry.provider.db.service.model.MSentryRole] http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql index 3886d29..c1a2778 100644 --- a/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql +++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql @@ -24,7 +24,8 @@ CREATE TABLE SENTRY_DB_PRIVILEGE GRANTOR_PRINCIPAL VARCHAR(4000), PRIVILEGE_SCOPE VARCHAR(40), "SERVER_NAME" VARCHAR(4000), - "TABLE_NAME" VARCHAR(4000) + "TABLE_NAME" VARCHAR(4000), + WITH_GRANT_OPTION CHAR(1) NOT NULL ); ALTER TABLE SENTRY_DB_PRIVILEGE ADD CONSTRAINT SENTRY_DB_PRIVILEGE_PK PRIMARY KEY (DB_PRIVILEGE_ID); @@ -78,7 +79,7 @@ CREATE TABLE "SENTRY_VERSION" ( ALTER TABLE SENTRY_VERSION ADD CONSTRAINT SENTRY_VERSION_PK PRIMARY KEY (VER_ID); -- Constraints for table SENTRY_DB_PRIVILEGE for class(es) [org.apache.sentry.provider.db.service.model.MSentryPrivilege] -CREATE UNIQUE INDEX SENTRYPRIVILEGENAME ON SENTRY_DB_PRIVILEGE ("SERVER_NAME",DB_NAME,"TABLE_NAME",URI,"ACTION"); +CREATE UNIQUE INDEX SENTRYPRIVILEGENAME ON SENTRY_DB_PRIVILEGE ("SERVER_NAME",DB_NAME,"TABLE_NAME",URI,"ACTION",WITH_GRANT_OPTION); -- Constraints for table SENTRY_ROLE for class(es) [org.apache.sentry.provider.db.service.model.MSentryRole] http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql index fee5028..d7ce57c 100644 --- a/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql +++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-mysql-1.4.0.sql @@ -34,7 +34,8 @@ CREATE TABLE `SENTRY_DB_PRIVILEGE` ( `URI` VARCHAR(4000) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, `ACTION` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `CREATE_TIME` BIGINT NOT NULL, - `GRANTOR_PRINCIPAL` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL + `GRANTOR_PRINCIPAL` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `WITH_GRANT_OPTION` CHAR(1) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `SENTRY_ROLE` ( @@ -80,7 +81,7 @@ ALTER TABLE `SENTRY_VERSION` ADD CONSTRAINT `SENTRY_VERSION` PRIMARY KEY (`VER_ID`); ALTER TABLE `SENTRY_DB_PRIVILEGE` - ADD UNIQUE `SENTRY_DB_PRIV_PRIV_NAME_UNIQ` (`SERVER_NAME`,`DB_NAME`,`TABLE_NAME`,`URI`(250),`ACTION`); + ADD UNIQUE `SENTRY_DB_PRIV_PRIV_NAME_UNIQ` (`SERVER_NAME`,`DB_NAME`,`TABLE_NAME`,`URI`(250),`ACTION`,`WITH_GRANT_OPTION`); ALTER TABLE `SENTRY_DB_PRIVILEGE` ADD INDEX `SENTRY_PRIV_SERV_IDX` (`SERVER_NAME`); http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql index cbdd337..3f01cda 100644 --- a/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql +++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql @@ -22,7 +22,8 @@ CREATE TABLE "SENTRY_DB_PRIVILEGE" ( "URI" VARCHAR2(4000) NULL, "ACTION" VARCHAR2(128) NOT NULL, "CREATE_TIME" NUMBER NOT NULL, - "GRANTOR_PRINCIPAL" VARCHAR(128) NOT NULL + "GRANTOR_PRINCIPAL" VARCHAR(128) NOT NULL, + "WITH_GRANT_OPTION" CHAR(1) NOT NULL ); CREATE TABLE "SENTRY_ROLE" ( @@ -67,7 +68,7 @@ ALTER TABLE "SENTRY_GROUP" ALTER TABLE "SENTRY_VERSION" ADD CONSTRAINT "SENTRY_VERSION_PK" PRIMARY KEY ("VER_ID"); ALTER TABLE "SENTRY_DB_PRIVILEGE" - ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","URI","ACTION"); + ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","URI","ACTION","WITH_GRANT_OPTION"); CREATE INDEX "SENTRY_SERV_PRIV_IDX" ON "SENTRY_DB_PRIVILEGE" ("SERVER_NAME"); http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql index 5a30aa7..186c968 100644 --- a/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql +++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-postgres-1.4.0.sql @@ -34,7 +34,8 @@ CREATE TABLE "SENTRY_DB_PRIVILEGE" ( "URI" character varying(4000) DEFAULT NULL::character varying, "ACTION" character varying(128) NOT NULL, "CREATE_TIME" BIGINT NOT NULL, - "GRANTOR_PRINCIPAL" VARCHAR(128) NOT NULL + "GRANTOR_PRINCIPAL" VARCHAR(128) NOT NULL, + "WITH_GRANT_OPTION" CHAR(1) NOT NULL ); CREATE TABLE "SENTRY_ROLE" ( @@ -80,7 +81,7 @@ ALTER TABLE ONLY "SENTRY_GROUP" ALTER TABLE ONLY "SENTRY_VERSION" ADD CONSTRAINT "SENTRY_VERSION_PK" PRIMARY KEY ("VER_ID"); ALTER TABLE ONLY "SENTRY_DB_PRIVILEGE" - ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","URI", "ACTION"); + ADD CONSTRAINT "SENTRY_DB_PRIV_PRIV_NAME_UNIQ" UNIQUE ("SERVER_NAME","DB_NAME","TABLE_NAME","URI", "ACTION","WITH_GRANT_OPTION"); CREATE INDEX "SENTRY_PRIV_SERV_IDX" ON "SENTRY_DB_PRIVILEGE" USING btree ("SERVER_NAME"); http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift index eb3e73e..b14616b 100644 --- a/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift +++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift @@ -29,6 +29,15 @@ namespace java org.apache.sentry.provider.db.service.thrift namespace php sentry.provider.db.service.thrift namespace cpp Apache.Sentry.Provider.Db.Service.Thrift +enum TSentryGrantOption { + TRUE = 1, + FALSE = 0, + # UNSET is used for revoke privilege, the component like 'hive' + # didn't support getting grant option, so use UNSET is stand + # for revoke both privileges with grant option and without grant + # option. + UNSET = -1 +} # Represents a Privilege in transport from the client to the server struct TSentryPrivilege { @@ -39,7 +48,8 @@ struct TSentryPrivilege { 6: optional string URI = "", 7: required string action = "", 8: optional i64 createTime, # Set on server side -9: optional string grantorPrincipal # Set on server side +9: optional string grantorPrincipal, # Set on server side +10: optional TSentryGrantOption grantOption = TSentryGrantOption.FALSE } # TODO can this be deleted? it's not adding value to TAlterSentryRoleAddGroupsRequest http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7b17cef7/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryPrivilege.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryPrivilege.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryPrivilege.java new file mode 100644 index 0000000..91d3171 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryPrivilege.java @@ -0,0 +1,168 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.sentry.provider.db.service.persistent; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import org.apache.sentry.core.model.db.AccessConstants; +import org.apache.sentry.provider.db.service.model.MSentryPrivilege; +import org.junit.Test; + +public class TestSentryPrivilege { + @Test + public void testImpliesPrivilegePositive() throws Exception { + // 1.test server+database+table+action + MSentryPrivilege my = new MSentryPrivilege(); + MSentryPrivilege your = new MSentryPrivilege(); + my.setServerName("server1"); + my.setDbName("db1"); + my.setTableName("tb1"); + my.setAction(AccessConstants.SELECT); + your.setServerName("server1"); + your.setDbName("db1"); + your.setTableName("tb1"); + your.setAction(AccessConstants.SELECT); + assertTrue(my.implies(your)); + + my.setAction(AccessConstants.ALL); + assertTrue(my.implies(your)); + + my.setTableName(""); + assertTrue(my.implies(your)); + + my.setDbName(""); + assertTrue(my.implies(your)); + + // 2.test server+URI+action + my = new MSentryPrivilege(); + your = new MSentryPrivilege(); + my.setServerName("server1"); + my.setAction(AccessConstants.ALL); + your.setServerName("server1"); + your.setAction(AccessConstants.ALL); + my.setURI("hdfs://namenode:9000/path"); + your.setURI("hdfs://namenode:9000/path"); + assertTrue(my.implies(your)); + + my.setURI("hdfs://namenode:9000/path"); + your.setURI("hdfs://namenode:9000/path/to/some/dir"); + assertTrue(my.implies(your)); + + my.setURI("file:///path"); + your.setURI("file:///path"); + assertTrue(my.implies(your)); + + my.setURI("file:///path"); + your.setURI("file:///path/to/some/dir"); + assertTrue(my.implies(your)); + } + + @Test + public void testImpliesPrivilegeNegative() throws Exception { + // 1.test server+database+table+action + MSentryPrivilege my = new MSentryPrivilege(); + MSentryPrivilege your = new MSentryPrivilege(); + // bad action + my.setServerName("server1"); + my.setDbName("db1"); + my.setTableName("tb1"); + my.setAction(AccessConstants.SELECT); + your.setServerName("server1"); + your.setDbName("db1"); + your.setTableName("tb1"); + your.setAction(AccessConstants.INSERT); + assertFalse(my.implies(your)); + + // bad action + your.setAction(AccessConstants.ALL); + assertFalse(my.implies(your)); + + // bad table + your.setTableName("tb2"); + assertFalse(my.implies(your)); + + // bad database + your.setTableName("tb1"); + your.setDbName("db2"); + assertFalse(my.implies(your)); + + // bad server + your.setTableName("tb1"); + your.setDbName("db1"); + your.setServerName("server2"); + assertFalse(my.implies(your)); + + // 2.test server+URI+action + my = new MSentryPrivilege(); + your = new MSentryPrivilege(); + my.setServerName("server1"); + my.setAction(AccessConstants.ALL); + your.setServerName("server2"); + your.setAction(AccessConstants.ALL); + + // relative path + my.setURI("hdfs://namenode:9000/path"); + your.setURI("hdfs://namenode:9000/path/to/../../other"); + assertFalse(my.implies(your)); + my.setURI("file:///path"); + your.setURI("file:///path/to/../../other"); + assertFalse(my.implies(your)); + + // bad uri + my.setURI("blah"); + your.setURI("hdfs://namenode:9000/path/to/some/dir"); + assertFalse(my.implies(your)); + my.setURI("hdfs://namenode:9000/path/to/some/dir"); + your.setURI("blah"); + assertFalse(my.implies(your)); + + // bad scheme + my.setURI("hdfs://namenode:9000/path"); + your.setURI("file:///path/to/some/dir"); + assertFalse(my.implies(your)); + my.setURI("hdfs://namenode:9000/path"); + your.setURI("file://namenode:9000/path/to/some/dir"); + assertFalse(my.implies(your)); + + // bad hostname + my.setURI("hdfs://namenode1:9000/path"); + your.setURI("hdfs://namenode2:9000/path"); + assertFalse(my.implies(your)); + + // bad port + my.setURI("hdfs://namenode:9000/path"); + your.setURI("hdfs://namenode:9001/path"); + assertFalse(my.implies(your)); + + // bad path + my.setURI("hdfs://namenode:9000/path1"); + your.setURI("hdfs://namenode:9000/path2"); + assertFalse(my.implies(your)); + my.setURI("file:///path1"); + your.setURI("file:///path2"); + assertFalse(my.implies(your)); + + // bad server + your.setServerName("server2"); + my.setURI("hdfs://namenode:9000/path1"); + your.setURI("hdfs://namenode:9000/path1"); + assertFalse(my.implies(your)); + } +}
