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

[#5775] feat(auth): Chain authorization plugin framework #5786

Merged
merged 9 commits into from
Dec 25, 2024

Conversation

xunliu
Copy link
Member

@xunliu xunliu commented Dec 6, 2024

What changes were proposed in this pull request?

  1. Add Chain auth plugin module
  2. Add auth common module
  3. Add Chain authorization Ranger Hive and Ranger HDFS ITs

Why are the changes needed?

Fix: #5775

Does this PR introduce any user-facing change?

N/A

How was this patch tested?

Add ITs

@xunliu xunliu requested review from yuqi1129 and mchades December 6, 2024 07:30
@xunliu xunliu self-assigned this Dec 6, 2024
@xunliu xunliu requested a review from jerqi December 6, 2024 07:30
@xunliu xunliu marked this pull request as draft December 7, 2024 04:43
@xunliu xunliu force-pushed the issue-5775 branch 2 times, most recently from 4b5dc53 to 4755a53 Compare December 23, 2024 03:24
@xunliu xunliu changed the title [#5775] feat(auth): Chain authorization plugin [#5775] feat(auth): Chain authorization plugin framework Dec 23, 2024
@xunliu xunliu marked this pull request as ready for review December 23, 2024 03:26
@xunliu xunliu force-pushed the issue-5775 branch 4 times, most recently from 5a29c05 to a19a314 Compare December 23, 2024 10:37
ChainAuthorizationProperties.fetchAuthPluginProperties(pluginName, properties);
String authProvider =
ChainAuthorizationProperties.getPluginProvider(pluginName, properties);
if ("ranger".equals(authProvider)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we only support Ranger plugin?

}

@Override
public AuthorizationPlugin newPlugin(
String metalake, String catalogProvider, Map<String, String> config) {
return new TestMySQLAuthorizationPlugin();
switch (catalogProvider) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we only support hive?

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently, we only support the Hive catalog in this PR, But we can support more types in the future.
We need rigorous testing to enable this limit.

ChainAuthorizationProperties.fetchAuthPluginProperties(pluginName, properties);
String authProvider =
ChainAuthorizationProperties.getPluginProvider(pluginName, properties);
if ("ranger".equals(authProvider)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can have a common module to define the constant variable like Ranger.

Copy link
Member Author

Choose a reason for hiding this comment

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

The community discussed it before, just like Catalog shortName uses string variables (Hive, Hadoop), not uses constant variable, So I kept consistent.

switch (metadataObject.type()) {
case METALAKE:
case SCHEMA:
case TABLE:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this right? Table can have a location, too.

Copy link
Member Author

Choose a reason for hiding this comment

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

This PR only supports Create_schema in the Catalog in the chain Ranger Hive and Ranger HDFS. I split other operations in the next PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you add TODO comment at least.

Copy link
Member Author

Choose a reason for hiding this comment

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

I already add todo integration test in the TestChainedAuthorizationIT.

Copy link
Contributor

Choose a reason for hiding this comment

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

We need add TODO in the code. Otherwise I will think this is an error.

public static final String RESOURCE_ALL = "*";
/** The `/` gives access to all path resources */
public static final String RESOURCE_ROOT_PATH = "/test/";
Copy link
Contributor

Choose a reason for hiding this comment

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

Why dow we need this?

Copy link
Member Author

Choose a reason for hiding this comment

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

rollback this change.

} catch (Exception e) {
throw new IllegalStateException("Failed to set environment variable", e);
} finally {
setEnv(key, originalValue);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need recover the property user.name?

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, I improved this code.

gravitinoPrivilege.name(), securableObject.type());
}
break;
case SELECT_TABLE:
Copy link
Contributor

Choose a reason for hiding this comment

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

If HDFS Ranger plugin is used for Hive, we should have the SELECT_TABLE privilege.

Copy link
Member Author

Choose a reason for hiding this comment

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

I split SELECT_TABLE operations in the next PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you add TODO comment at least.

Copy link
Member Author

Choose a reason for hiding this comment

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

I already add todo integration test in the TestChainedAuthorizationIT.

implementation(libs.javax.ws.rs.api)
implementation(libs.jettison)
compileOnly(libs.lombok)
implementation(libs.rome)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you make this alphabetically ordering?

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

public Boolean onMetadataUpdated(MetadataObjectChange... changes)
throws AuthorizationPluginException {
for (AuthorizationPlugin plugin : plugins) {
Boolean result = plugin.onMetadataUpdated(changes);
Copy link
Contributor

Choose a reason for hiding this comment

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

Will we throw exceptions here, what happens if the exception throws in the for loop?

Copy link
Member Author

Choose a reason for hiding this comment

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

In such a for loop, if the plugin calls the onMetadataUpdated function and throws an exception, the exception will propagate to the outer method ChainAuthorizaiton::onMetadataUpdated and will not be caught or handled. This means that the outer method also sell AuthorizationPluginException abnormalities, and the cycle will be interrupted, will not continue to deal with the rest of the plugin.

ImmutableMap.of(Catalog.AUTHORIZATION_PROVIDER, authProvider))
.ifPresent(libAndResourcesPaths::add);
IsolatedClassLoader classLoader =
IsolatedClassLoader.buildClassLoader(libAndResourcesPaths);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to build different classloaders for different plugins, can we just use catalog's classloader?

Copy link
Member Author

Choose a reason for hiding this comment

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

When we load ChainAuthorizationPlugin , We need add authorizaitons/chain/libs in the BaseCatalog IsolatedClassLoader class paths.
We cann't add authorizaitons/ranger/libs at same time. So we need an IsolatedClassLoader to separate load RangerAuthorizationPlugin here.

}
return Boolean.TRUE;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can extract the common part and simplify the code like:

  private Boolean chainedAction(Function<AuthorizationPlugin, Boolean> action) {
    for (AuthorizationPlugin plugin : plugins) {
      if (!action.apply(plugin)) {
        return false;
      }
    }
    return true;
  }

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

@@ -25,12 +25,12 @@
import org.apache.gravitino.authorization.AuthorizationPrivilege;
import org.apache.gravitino.authorization.AuthorizationSecurableObject;

public class RangerPathBaseSecurableObject extends RangerPathBaseMetadataObject
public class RangerHDFSSecurableObject extends RangerHDFSMetadataObject
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we change the class name here? I assume Ranger doesn't care whether it is the HDFS or not, it cares more about the path, am I right?

Copy link
Contributor

Choose a reason for hiding this comment

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

So if users have a Hadoop compatible file system, it can also be controlled by Ranger, but it is not a HDFS actually.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, I rollback this change.

* specific language governing permissions and limitations
* under the License.
*/
package org.apache.gravitino.authorization;
Copy link
Contributor

Choose a reason for hiding this comment

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

How about renaming the package to org.apache.gravitino.authorization.common for better understanding?

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

@xunliu
Copy link
Member Author

xunliu commented Dec 24, 2024

@jerryshao I fixed all the problems, Please help me review it again. Thanks.

Comment on lines +56 to +69
testImplementation(project(":core"))
testImplementation(project(":clients:client-java"))
testImplementation(project(":server"))
testImplementation(project(":catalogs:catalog-common"))
testImplementation(project(":integration-test-common", "testArtifacts"))
testImplementation(project(":authorizations:authorization-ranger"))
testImplementation(project(":authorizations:authorization-ranger", "testArtifacts"))
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.mockito.core)
testImplementation(libs.testcontainers)
testRuntimeOnly(libs.junit.jupiter.engine)
testImplementation(libs.mysql.driver)
testImplementation(libs.postgresql.driver)
testImplementation(libs.ranger.intg) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we still have some alphabetical ordering issues, can you fix them all?

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

import org.apache.gravitino.exceptions.AuthorizationPluginException;
import org.apache.gravitino.utils.IsolatedClassLoader;

/** Chain authorization operations plugin class. <br> */
Copy link
Contributor

Choose a reason for hiding this comment

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

Chained authorization operations...

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

}

private void initPlugins(String catalogProvider, Map<String, String> properties) {
ChainedAuthorizationProperties chainedAuthProperties =
Copy link
Contributor

Choose a reason for hiding this comment

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

"Authz" is short for "Authorization", it would be better change to "Authz".

Copy link
Contributor

Choose a reason for hiding this comment

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

Just minor suggestion, I'm OK with either.

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

{
String locationPath = getLocationPath(securableObject);
if (locationPath != null && !locationPath.isEmpty()) {
RangerPathBaseMetadataObject rangerHDFSMetadataObject =
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also rename the variable to "rangerPathXXX", not "rangerHDFSXXX"?

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

return rangerHDFSMetadataObject;
} else {
return new RangerPathBaseMetadataObject("", RangerPathBaseMetadataObject.Type.PATH);
RangerPathBaseMetadataObject rangerHDFSMetadataObject;
Copy link
Contributor

Choose a reason for hiding this comment

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

Also here.

Copy link
Member Author

Choose a reason for hiding this comment

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

DONE

@xunliu
Copy link
Member Author

xunliu commented Dec 25, 2024

@jerryshao I fixed all the problems, Please help me review it again. Thanks.

Comment on lines 54 to 63
tasks {
val runtimeJars by registering(Copy::class) {
from(configurations.runtimeClasspath)
into("build/libs")
}

jar {
dependsOn(runtimeJars)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this is not needed, am I right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I removed it.

Comment on lines +20 to +34
test {
subprojects.forEach {
dependsOn(":${project.name}:${it.name}:test")
}
}

register("copyLibAndConfig", Copy::class) {
subprojects.forEach {
if (!it.name.startsWith("authorization-common")) {
dependsOn(":${project.name}:${it.name}:copyLibAndConfig")
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need these codes? I found that each module already has these tasks.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can use it to optimize compileDistribution in the root build.gradle.kts.

}
throw new IllegalArgumentException(
"No matching RangerMetadataObject.Type for " + metadataType);
}
}

private final String path;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we rename this class from RangerPathBaseMetadataObject to RangerPathBasedMetadataObject or RangerPathMetadataObject?

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, I rename RangerPathBaseMetadataObject to PathBasedMetadataObject and moved it to authorization-common modul.

@jerryshao jerryshao merged commit 3e7e550 into apache:main Dec 25, 2024
26 checks passed
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.

[Subtask] Chain authorization plugin framework
3 participants