Skip to content
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

Merged
merged 17 commits into from
Mar 7, 2025

Conversation

jerqi
Copy link
Contributor

@jerqi jerqi commented Feb 28, 2025

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.

@jerqi jerqi marked this pull request as draft February 28, 2025 11:44

@Override
protected String getServiceType() {
return "hdfs";
Copy link
Contributor

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?

Copy link
Contributor Author

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

}

String passwordKey = "password";
String passwordVal = "admin";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This .... is dangerous?

Copy link
Contributor Author

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))) {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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"));
    }

@Override
public void close() throws IOException {}
public void close() throws IOException {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@jerqi jerqi self-assigned this Mar 3, 2025
@jerqi jerqi marked this pull request as ready for review March 3, 2025 12:43
rangerClient = new RangerClientExtension(rangerUrl, authType, rangerAdminName, password);

if (Boolean.parseBoolean(
config.get(RangerAuthorizationProperties.RANGER_SERVICE_CREATE_IF_ABSENT))) {
Copy link
Contributor

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.

@@ -45,6 +45,9 @@
public abstract class BaseAuthorization<T extends BaseAuthorization>
implements AuthorizationProvider, Closeable {

/** UUID represents */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Contributor Author

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.

Comment on lines 143 to 144
// catalog
// will close the authorization plugin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Contributor Author

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.

@jerqi jerqi changed the title [#6536] improvement: Create Ranger service if service is absent [#6536] improvement(authz): Create Ranger service if service is absent Mar 4, 2025
Comment on lines +854 to +857
List<RangerPolicy> policies = rangerClient.getPoliciesInService(serviceName);
for (RangerPolicy policy : policies) {
rangerClient.deletePolicy(policy.getId());
}
Copy link
Member

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

Copy link
Contributor Author

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.

Comment on lines 772 to 777
try {
rangerClient.deleteService(rangerServiceName);
} catch (RangerServiceException rse) {
throw new AuthorizationPluginException(
"Fail to delete Ranger service %s, exception: %s", rangerServiceName, rse.getMessage());
}
Copy link
Member

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?

Copy link
Contributor Author

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.

Comment on lines 913 to 990
/**
* 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();
});
}
Copy link
Member

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.

Copy link
Contributor Author

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.

Comment on lines 780 to 830
/** 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);
}
}
Copy link
Member

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.

Copy link
Contributor Author

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.

Comment on lines 183 to 184
RangerAuthorizationProperties.RANGER_SERVICE_CREATE_IF_ABSENT,
"true"));
Copy link
Member

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.

Comment on lines -134 to +144
// 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);
Copy link
Member

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?

Copy link
Contributor Author

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

Comment on lines 984 to 986
if (conf.containsKey(BaseCatalog.CATALOG_BYPASS_PREFIX + key)) {
return conf.get(BaseCatalog.CATALOG_BYPASS_PREFIX + key);
}
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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);

Copy link
Member

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()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new assertion.

Comment on lines 203 to 222
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");
Copy link
Member

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?

Copy link
Contributor Author

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.

Comment on lines 984 to 986
if (conf.containsKey(BaseCatalog.CATALOG_BYPASS_PREFIX + key)) {
return conf.get(BaseCatalog.CATALOG_BYPASS_PREFIX + key);
}
Copy link
Member

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?

@jerqi
Copy link
Contributor Author

jerqi commented Mar 5, 2025

I renamed the method.

Comment on lines 68 to 77
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);

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 236 to 242
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"));
Copy link
Member

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.

Copy link
Contributor Author

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;
Copy link
Member

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();
}

Copy link
Contributor Author

@jerqi jerqi Mar 6, 2025

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?

Copy link
Member

@xunliu xunliu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jerqi jerqi merged commit 1297713 into apache:main Mar 7, 2025
28 checks passed
@jerqi jerqi deleted the ISSUE-6536 branch March 7, 2025 09:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Improvement] Creating a Ranger repo when creating a catalog with Ranger authz plugin
3 participants