-
Notifications
You must be signed in to change notification settings - Fork 415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#6536] improvement(authz): Create Ranger service if service is absent #6575
Conversation
...n/src/main/java/org/apache/gravitino/authorization/common/RangerAuthorizationProperties.java
Outdated
Show resolved
Hide resolved
|
||
@Override | ||
protected String getServiceType() { | ||
return "hdfs"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better not use hardcoded string here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will put these constants to one place to manage them
...r/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationHDFSPlugin.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
String passwordKey = "password"; | ||
String passwordVal = "admin"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This .... is dangerous?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could pass fake value. I can add comment to avoid others worrying about the code.
rangerClient = new RangerClientExtension(rangerUrl, authType, rangerAdminName, password); | ||
|
||
if (Boolean.parseBoolean( | ||
config.get(RangerAuthorizationProperties.RANGER_SERVICE_CREATE_IF_ABSENT))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can we be so sure that this property is there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Boolean.parseBoolean can handle null value. Null value equals false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure there won't be uncaught exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see the code
public static boolean parseBoolean(String s) {
return ((s != null) && s.equalsIgnoreCase("true"));
}
...anger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationPlugin.java
Outdated
Show resolved
Hide resolved
...anger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationPlugin.java
Outdated
Show resolved
Hide resolved
@Override | ||
public void close() throws IOException {} | ||
public void close() throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this method invoked?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we dropCatalog, this method will be invoked.
rangerClient = new RangerClientExtension(rangerUrl, authType, rangerAdminName, password); | ||
|
||
if (Boolean.parseBoolean( | ||
config.get(RangerAuthorizationProperties.RANGER_SERVICE_CREATE_IF_ABSENT))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure there won't be uncaught exceptions.
...anger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationPlugin.java
Outdated
Show resolved
Hide resolved
@@ -45,6 +45,9 @@ | |||
public abstract class BaseAuthorization<T extends BaseAuthorization> | |||
implements AuthorizationProvider, Closeable { | |||
|
|||
/** UUID represents */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I will correct it.
// catalog | ||
// will close the authorization plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. Gradle format will cause this issue.
List<RangerPolicy> policies = rangerClient.getPoliciesInService(serviceName); | ||
for (RangerPolicy policy : policies) { | ||
rangerClient.deletePolicy(policy.getId()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a description of why the policy should be deleted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I can add the comment.
try { | ||
rangerClient.deleteService(rangerServiceName); | ||
} catch (RangerServiceException rse) { | ||
throw new AuthorizationPluginException( | ||
"Fail to delete Ranger service %s, exception: %s", rangerServiceName, rse.getMessage()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need the automated delete Reanger service?
Is the RangerAuthorizationProperties.RANGER_SERVICE_CREATE_IF_ABSENT
only used by ITs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. It's not only used by UT.
If we have a Ranger server and Ranger HDFS or Hive services are not pre-created , we need the to create the by our Gravitino.
If these Ranger Hive services or HDFS services are created by Gravitino, Gravitino should delete them, too.
/** | ||
* IF rename the SCHEMA, Need to rename these the relevant policies, `{schema}`, `{schema}.*`, | ||
* `{schema}.*.*` <br> | ||
* IF rename the TABLE, Need to rename these the relevant policies, `{schema}.*`, `{schema}.*.*` | ||
* <br> | ||
* IF rename the COLUMN, Only need to rename `{schema}.*.*` <br> | ||
*/ | ||
protected abstract void renameMetadataObject( | ||
AuthorizationMetadataObject authzMetadataObject, | ||
AuthorizationMetadataObject newAuthzMetadataObject); | ||
|
||
protected abstract void removeMetadataObject(AuthorizationMetadataObject authzMetadataObject); | ||
|
||
/** | ||
* Remove the policy by the metadata object names. <br> | ||
* | ||
* @param authzMetadataObject The authorization metadata object. | ||
*/ | ||
protected void removePolicyByMetadataObject(AuthorizationMetadataObject authzMetadataObject) { | ||
RangerPolicy policy = findManagedPolicy(authzMetadataObject); | ||
if (policy != null) { | ||
rangerHelper.removeAllGravitinoManagedPolicyItem(policy); | ||
} | ||
} | ||
|
||
@Override | ||
public void close() throws IOException {} | ||
|
||
/** Generate authorization securable object */ | ||
public abstract AuthorizationSecurableObject generateAuthorizationSecurableObject( | ||
List<String> names, | ||
String path, | ||
AuthorizationMetadataObject.Type type, | ||
Set<AuthorizationPrivilege> privileges); | ||
|
||
public boolean validAuthorizationOperation(List<SecurableObject> securableObjects) { | ||
return securableObjects.stream() | ||
.allMatch( | ||
securableObject -> { | ||
AtomicBoolean match = new AtomicBoolean(true); | ||
securableObject.privileges().stream() | ||
.forEach( | ||
privilege -> { | ||
if (!privilege.canBindTo(securableObject.type())) { | ||
LOG.error( | ||
"The privilege({}) is not supported for the metadata object({})!", | ||
privilege.name(), | ||
securableObject.fullName()); | ||
match.set(false); | ||
} | ||
}); | ||
return match.get(); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is because of the changes brought about by moving the code, which makes it more difficult to review, and it is better to be able to fall back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can revert this change.
/** Generate authorization securable object */ | ||
public abstract AuthorizationSecurableObject generateAuthorizationSecurableObject( | ||
List<String> names, | ||
String path, | ||
AuthorizationMetadataObject.Type type, | ||
Set<AuthorizationPrivilege> privileges); | ||
|
||
public boolean validAuthorizationOperation(List<SecurableObject> securableObjects) { | ||
return securableObjects.stream() | ||
.allMatch( | ||
securableObject -> { | ||
AtomicBoolean match = new AtomicBoolean(true); | ||
securableObject.privileges().stream() | ||
.forEach( | ||
privilege -> { | ||
if (!privilege.canBindTo(securableObject.type())) { | ||
LOG.error( | ||
"The privilege({}) is not supported for the metadata object({})!", | ||
privilege.name(), | ||
securableObject.fullName()); | ||
match.set(false); | ||
} | ||
}); | ||
return match.get(); | ||
}); | ||
} | ||
|
||
/** | ||
* IF rename the SCHEMA, Need to rename these the relevant policies, `{schema}`, `{schema}.*`, | ||
* `{schema}.*.*` <br> | ||
* IF rename the TABLE, Need to rename these the relevant policies, `{schema}.*`, `{schema}.*.*` | ||
* <br> | ||
* IF rename the COLUMN, Only need to rename `{schema}.*.*` <br> | ||
*/ | ||
protected abstract void renameMetadataObject( | ||
AuthorizationMetadataObject authzMetadataObject, | ||
AuthorizationMetadataObject newAuthzMetadataObject); | ||
|
||
protected abstract void removeMetadataObject(AuthorizationMetadataObject authzMetadataObject); | ||
|
||
/** | ||
* Remove the policy by the metadata object names. <br> | ||
* | ||
* @param authzMetadataObject The authorization metadata object. | ||
*/ | ||
protected void removePolicyByMetadataObject(AuthorizationMetadataObject authzMetadataObject) { | ||
RangerPolicy policy = findManagedPolicy(authzMetadataObject); | ||
if (policy != null) { | ||
rangerHelper.removeAllGravitinoManagedPolicyItem(policy); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is because of the changes brought about by moving the code, which makes it more difficult to review, and it is better to be able to fall back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I just put public methods together, protected methods together, private methods together. I can revert this change.
RangerAuthorizationProperties.RANGER_SERVICE_CREATE_IF_ABSENT, | ||
"true")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to add a check to see if Ranger service success was created.
// If we call the authorization plugin after dropping catalog, we can't load the plugin of the | ||
// catalog | ||
Catalog catalog = dispatcher.loadCatalog(ident); | ||
boolean dropped = dispatcher.dropCatalog(ident, force); | ||
|
||
if (dropped && catalog != null) { | ||
if (catalog != null) { | ||
List<String> locations = | ||
AuthorizationUtils.getMetadataObjectLocation(ident, Entity.EntityType.CATALOG); | ||
AuthorizationUtils.removeCatalogPrivileges(catalog, locations); | ||
} | ||
|
||
return dropped; | ||
// We should call the authorization plugin before dropping the catalog, because the dropping | ||
// catalog will close the authorization plugin. | ||
return dispatcher.dropCatalog(ident, force); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the order of implementation being adjusted and what are the implications?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see the comment.
We should call the authorization plugin before dropping the catalog, because the dropping
catalog will close the authorization plugi
if (conf.containsKey(BaseCatalog.CATALOG_BYPASS_PREFIX + key)) { | ||
return conf.get(BaseCatalog.CATALOG_BYPASS_PREFIX + key); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need BaseCatalog.CATALOG_BYPASS_PREFIX
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because properties like hadoop.security.xxx
should use PYPASS_PREFIX. It's the common behaviour in our system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe getByPassConfValue()
is a better function name?
"true"); | ||
|
||
metalake.createCatalog("test", Catalog.Type.RELATIONAL, provider, "comment", uuidProperties); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to add check catalog logic after createCatalog()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a new assertion.
Map<String, String> uuidProperties = | ||
ImmutableMap.of( | ||
HiveConstants.METASTORE_URIS, | ||
HIVE_METASTORE_URIS, | ||
IMPERSONATION_ENABLE, | ||
"true", | ||
AUTHORIZATION_PROVIDER, | ||
"ranger", | ||
RangerAuthorizationProperties.RANGER_SERVICE_TYPE, | ||
"HadoopSQL", | ||
RangerAuthorizationProperties.RANGER_ADMIN_URL, | ||
RangerITEnv.RANGER_ADMIN_URL, | ||
RangerAuthorizationProperties.RANGER_AUTH_TYPE, | ||
RangerContainer.authType, | ||
RangerAuthorizationProperties.RANGER_USERNAME, | ||
RangerContainer.rangerUserName, | ||
RangerAuthorizationProperties.RANGER_PASSWORD, | ||
RangerContainer.rangerPassword, | ||
RangerAuthorizationProperties.RANGER_SERVICE_CREATE_IF_ABSENT, | ||
"true"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to set a BaseAuthorization.UUID
in the uuidProperties
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need. The uuid will be generated automatically.
if (conf.containsKey(BaseCatalog.CATALOG_BYPASS_PREFIX + key)) { | ||
return conf.get(BaseCatalog.CATALOG_BYPASS_PREFIX + key); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe getByPassConfValue()
is a better function name?
I renamed the method. |
for (Map.Entry<String, String> entry : properties.entrySet()) { | ||
if (!entry | ||
.getKey() | ||
.startsWith(ChainedAuthorizationProperties.CHAIN_PLUGINS_PROPERTIES_KEY)) { | ||
pluginConfig.put(entry.getKey(), entry.getValue()); | ||
} | ||
} | ||
pluginConfig.put( | ||
BaseAuthorization.UUID, pluginConfig.get(BaseAuthorization.UUID) + pluginName); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you went pass hadoop.security.authentication
properties to the plugin, you can use authorization.chain.hdfs-xxx.hadoop.security.authentication
.
Another thing is, I found you added some new properties key in the ranger-hive-plugin and ranger-hdfs-plugin, please update relation doc. thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Catalog catalogTest = | ||
metalake.createCatalog("test", Catalog.Type.RELATIONAL, "hive", "comment", autoProperties); | ||
Map<String, String> newProperties = catalogTest.properties(); | ||
Assertions.assertTrue( | ||
newProperties.containsKey("authorization.chain.hdfs1.ranger.service.name")); | ||
Assertions.assertTrue( | ||
newProperties.containsKey("authorization.chain.hive1.ranger.service.name")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you need to check if test833
and test899
are in the Ranger service repo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
protected abstract Map<String, String> getServiceConfigs(Map<String, String> config); | ||
|
||
protected int getPrefixLength() { | ||
return prefixLength; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove prefixLength variable.
protected int getPrefixLength() {
String.format("%s.", rangerAuthorizationProperties.getPropertiesPrefix()).length();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rangerAuthorizationProperties
isn't field variable. We need to add the field variable rangerAuthorizationProperities
. Isn't ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
What changes were proposed in this pull request?
Create Ranger service if service is absent
Why are the changes needed?
Fix: #6536
Does this PR introduce any user-facing change?
Yes, I will add the document.
How was this patch tested?
Add a UT.