-
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
Changes from 5 commits
3cdb335
5b018ce
52444df
9f0743f
74b88fd
d60cc87
69e34b2
69e261f
384e6e4
531ec52
2fb0d96
83a5f42
2fdc026
bef8a8f
2793f78
2b07cef
bdbca22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ | |
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.collect.ImmutableMap; | ||
import com.sun.jersey.api.client.ClientResponse; | ||
import java.io.IOException; | ||
import java.time.Instant; | ||
import java.util.Arrays; | ||
|
@@ -48,14 +49,17 @@ | |
import org.apache.gravitino.authorization.ranger.reference.VXGroupList; | ||
import org.apache.gravitino.authorization.ranger.reference.VXUser; | ||
import org.apache.gravitino.authorization.ranger.reference.VXUserList; | ||
import org.apache.gravitino.connector.BaseCatalog; | ||
import org.apache.gravitino.connector.authorization.AuthorizationPlugin; | ||
import org.apache.gravitino.connector.authorization.BaseAuthorization; | ||
import org.apache.gravitino.exceptions.AuthorizationPluginException; | ||
import org.apache.gravitino.meta.AuditInfo; | ||
import org.apache.gravitino.meta.GroupEntity; | ||
import org.apache.gravitino.meta.UserEntity; | ||
import org.apache.gravitino.utils.PrincipalUtils; | ||
import org.apache.ranger.RangerServiceException; | ||
import org.apache.ranger.plugin.model.RangerPolicy; | ||
import org.apache.ranger.plugin.model.RangerService; | ||
import org.apache.ranger.plugin.util.GrantRevokeRoleRequest; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
@@ -74,12 +78,15 @@ | |
public abstract class RangerAuthorizationPlugin | ||
implements AuthorizationPlugin, AuthorizationPrivilegesMappingProvider { | ||
private static final Logger LOG = LoggerFactory.getLogger(RangerAuthorizationPlugin.class); | ||
protected static final String HDFS_SERVICE_TYPE = "hdfs"; | ||
protected static final String HADOOP_SQL_SERVICE_TYPE = "hive"; | ||
|
||
protected String metalake; | ||
protected final String rangerServiceName; | ||
protected final RangerClientExtension rangerClient; | ||
protected final RangerHelper rangerHelper; | ||
@VisibleForTesting public final String rangerAdminName; | ||
private boolean isCreatedByPlugin = false; | ||
|
||
protected RangerAuthorizationPlugin(String metalake, Map<String, String> config) { | ||
this.metalake = metalake; | ||
|
@@ -91,9 +98,20 @@ protected RangerAuthorizationPlugin(String metalake, Map<String, String> config) | |
rangerAdminName = config.get(RangerAuthorizationProperties.RANGER_USERNAME); | ||
// Apache Ranger Password should be minimum 8 characters with min one alphabet and one numeric. | ||
String password = config.get(RangerAuthorizationProperties.RANGER_PASSWORD); | ||
rangerServiceName = config.get(RangerAuthorizationProperties.RANGER_SERVICE_NAME); | ||
|
||
String serviceName = config.get(RangerAuthorizationProperties.RANGER_SERVICE_NAME); | ||
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 commentThe 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 commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. You can see the code
|
||
if (serviceName == null) { | ||
serviceName = config.get(BaseAuthorization.UUID); | ||
} | ||
|
||
createRangerServiceIfNecessary(config, serviceName); | ||
} | ||
|
||
rangerServiceName = serviceName; | ||
rangerHelper = | ||
new RangerHelper( | ||
rangerClient, | ||
|
@@ -745,6 +763,110 @@ public Boolean onGroupAcquired(Group group) { | |
return Boolean.TRUE; | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
if (!isCreatedByPlugin) { | ||
return; | ||
} | ||
|
||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need the automated delete Reanger service? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. It's not only used by UT. |
||
} | ||
|
||
/** 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 commentThe 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 commentThe 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. |
||
|
||
protected String getConfValue(Map<String, String> conf, String key, String defaultValue) { | ||
if (conf.containsKey(BaseCatalog.CATALOG_BYPASS_PREFIX + key)) { | ||
return conf.get(BaseCatalog.CATALOG_BYPASS_PREFIX + key); | ||
} | ||
return defaultValue; | ||
} | ||
|
||
protected abstract String getServiceType(); | ||
|
||
protected abstract Map<String, String> getServiceConfigs(Map<String, String> config); | ||
|
||
private void createRangerServiceIfNecessary(Map<String, String> config, String serviceName) { | ||
try { | ||
rangerClient.getService(serviceName); | ||
} catch (RangerServiceException rse) { | ||
if (rse.getStatus().equals(ClientResponse.Status.NOT_FOUND)) { | ||
try { | ||
RangerService rangerService = new RangerService(); | ||
rangerService.setType(getServiceType()); | ||
rangerService.setName(serviceName); | ||
rangerService.setConfigs(getServiceConfigs(config)); | ||
rangerClient.createService(rangerService); | ||
List<RangerPolicy> policies = rangerClient.getPoliciesInService(serviceName); | ||
for (RangerPolicy policy : policies) { | ||
rangerClient.deletePolicy(policy.getId()); | ||
} | ||
Comment on lines
+798
to
+801
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. OK. I can add the comment. |
||
isCreatedByPlugin = true; | ||
} catch (RangerServiceException crse) { | ||
throw new AuthorizationPluginException( | ||
"Fail to create ranger service %s, exception: %s", serviceName, crse.getMessage()); | ||
} | ||
} else { | ||
throw new AuthorizationPluginException( | ||
"Fail to get ranger service name %s, exception: %s", serviceName, rse.getMessage()); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Add the securable object's privilege to the Ranger policy. <br> | ||
* 1. Find the policy base the metadata object. <br> | ||
|
@@ -909,59 +1031,4 @@ private void removePolicyItemIfEqualRoleName( | |
policyItem.getRoles().removeIf(roleName::equals); | ||
} | ||
} | ||
|
||
/** | ||
* 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. I can revert this change. |
||
} |
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 useauthorization.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.