Skip to content

[#8942]feat(authz): support tag access control#9018

Merged
roryqi merged 40 commits intoapache:mainfrom
hdygxsj:feat-8942
Nov 21, 2025
Merged

[#8942]feat(authz): support tag access control#9018
roryqi merged 40 commits intoapache:mainfrom
hdygxsj:feat-8942

Conversation

@hdygxsj
Copy link
Contributor

@hdygxsj hdygxsj commented Nov 4, 2025

What changes were proposed in this pull request?

support tag access control

Why are the changes needed?

Fix: #8942

Does this PR introduce any user-facing change?

None

How was this patch tested?

org.apache.gravitino.client.integration.test.authorization.TagOperationsAuthorizationIT

@hdygxsj
Copy link
Contributor Author

hdygxsj commented Nov 4, 2025

PHAL @jerqi

@hdygxsj hdygxsj requested a review from roryqi November 4, 2025 17:00

@Override
public boolean canBindTo(MetadataObject.Type type) {
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could the tag and role be tagged?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems there are no restrictions in TagManager.

Copy link
Contributor

Choose a reason for hiding this comment

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

cc @jerryshao WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could the tag and role be tagged?

I misunderstood earlier—CREATE_TAG should be bound to the metalake.

@roryqi
Copy link
Contributor

roryqi commented Nov 5, 2025

Could u add the documents for this PR?

| set owner | The owner of the securable object |
| list tags | The owner of the metalake can see all the tags. Others can see his owned tags. |
| create tag | `CREATE_TAG` on the metalake or the owner of the metalake |
| get tag | The owner of the metalake or the tag. |
Copy link
Contributor

Choose a reason for hiding this comment

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

I have some concern about this. Maybe we can't use this.

@roryqi
Copy link
Contributor

roryqi commented Nov 12, 2025

  1. Could you add the tag to the metadata object type in the openapi.yaml
  2. Could you confirm influence about tag, policy when adding tag to the metadata object type?

metadataObject,
authorizationPlugin ->
authorizationPlugin.onOwnerSet(metadataObject, originOwner.orElse(null), newOwner));
if (metadataObject.type() != MetadataObject.Type.TAG) {
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 this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

org.apache.gravitino.authorization.AuthorizationUtils#callAuthorizationPluginForMetadataObject can not handle tag
image

Copy link
Contributor

Choose a reason for hiding this comment

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

We should let this method handle the tag. Because tag becomes a metadata object.

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 should let this method handle the tag. Because tag becomes a metadata object.

This method seems to be for handling pushdown, tags shouldn’t be in the underlying engine, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

# Conflicts:
#	api/src/main/java/org/apache/gravitino/authorization/Privilege.java
#	api/src/main/java/org/apache/gravitino/authorization/Privileges.java
#	core/src/main/java/org/apache/gravitino/GravitinoEnv.java
#	docs/security/access-control.md
.forEach(
tag -> {
boolean result =
new AuthorizationExpressionEvaluator(
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 this instead of expression?

Copy link
Contributor Author

@hdygxsj hdygxsj Nov 18, 2025

Choose a reason for hiding this comment

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

Authorization expressions are also used here, but interceptor is not used.

Just like with list operations, it's challenging in interceptors to extract an array from the request body and perform authorization and filtering on it.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we have the tag which we don't have privilege, we should forbid the behaviour.

You should move the logic to GravitinoInterceptionService
We can add the new annotation for the request like

      AuthorizationRequest authorizeRequest =
          parameter.getAnnotation(AuthorizationRequest.class);

then

preAuthzRequest(authorizeRequest);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AuthorizationExpressionEvaluator and authorization expressions are not suitable for authorizing lists/arrays—or even multiple metadata objects within multiple lists/arrays(tagsToAdd, tagsToRemove). Should we consider designing a new authorization mechanism specifically for this scenario?

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 should design this. We should have the semantics like scala forall.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@roryqi
Copy link
Contributor

roryqi commented Nov 18, 2025

We would like to load a metadata object first. Then, we associate the metadata object with tags. It requires we must have the privilege to load the table. Actually, if we have the privilege BROWSE in the future, we can only list the metadata objects and know the names of the metadata objects. Maybe we need to add an interface for Tag

SupportsTagOperations {
   void associateObjects(List<MetadataObject> addedObjects,  List<MetadataObject> removeObjects)
}

@jerryshao WDYT?

@Timed(name = "get-object-tag." + MetricNames.HTTP_PROCESS_DURATION, absolute = true)
@ResponseMetered(name = "get-object-tag", absolute = true)
@AuthorizationExpression(expression = "CAN_ACCESS_METADATA && ( METALAKE::OWNER || TAG::OWNER)")
public Response getTagForObject(
Copy link
Contributor

Choose a reason for hiding this comment

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

This expression is wrong

METALAKE_OWNER || (CAN_LOAD_OBJECT && CAN_LOAD_TAG)

# Conflicts:
#	server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
expression);

return buildNoAuthResponse(errorMessage, accessMetadataName, currentUser, methodName);
if (!isBatch) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could extract some classes to handle the conditions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

extractAuthorizationRequestTypeFromParameters(parameters);
executor =
switch (requestType) {
case COMMON -> new CommonAuthorizerExecutor(
Copy link
Contributor

Choose a reason for hiding this comment

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

Could extract these classes to single files?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed


// Authorize both 'tagsToAdd' and 'tagsToRemove' fields.
// Short-circuit on first failure.
return authorizeTagField(request, "tagsToAdd", context, targetType)
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need use reflection 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.

fixed

# Conflicts:
#	server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java

public static void callAuthorizationPluginForMetadataObject(
String metalake, MetadataObject metadataObject, Consumer<AuthorizationPlugin> consumer) {
if (metadataObject.type() == MetadataObject.Type.TAG) {
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 still need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, otherwise setting the tag's owner here will throw an exception.

Copy link
Contributor

Choose a reason for hiding this comment

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

image
We have already handled this in this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

try {
// Set the creator as the owner of the catalog.
OwnerDispatcher ownerDispatcher = GravitinoEnv.getInstance().ownerDispatcher();
if (ownerDispatcher != null) {
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 try catch 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.

fixed

* @return The created tag.
*/
Tag createTag(String metalake, String name, String comment, Map<String, String> properties);
Tag createTag(String metalake, String name, String comment, Map<String, String> properties)
Copy link
Contributor

@roryqi roryqi Nov 21, 2025

Choose a reason for hiding this comment

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

Why do need to throw Exception here?

return authorizeTagField(tagsAssociateRequest.getTagsToAdd(), context, targetType)
&& authorizeTagField(tagsAssociateRequest.getTagsToRemove(), context, targetType);
}
throw new UnsupportedOperationException("Unsupported request type: " + request.getClass());
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you use Precondition.checkArguments(). This should throw illegalAgurmentException here.

Copy link
Contributor

@roryqi roryqi left a comment

Choose a reason for hiding this comment

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

LGTM.

@roryqi roryqi merged commit f1ca524 into apache:main Nov 21, 2025
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.

[FEATURE] Add access control for tags

2 participants