This is an automated email from the ASF dual-hosted git repository.
liuxun pushed a commit to branch branch-metadata-authz
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-metadata-authz by this
push:
new 3a220f381e [#6775] feat(server): Introduce authorization annotations
(#6828)
3a220f381e is described below
commit 3a220f381e9c3bbc006571efa112eed080bf4a2e
Author: Lord of Abyss <[email protected]>
AuthorDate: Thu Apr 24 14:48:50 2025 +0800
[#6775] feat(server): Introduce authorization annotations (#6828)
### What changes were proposed in this pull request?
Introduce authorization annotations
### Why are the changes needed?
Fix: #6775
### Does this PR introduce _any_ user-facing change?
no
### How was this patch tested?
local test.
---
.../annotations/AuthorizationExpression.java | 39 +++++++
.../annotations/AuthorizationMetadata.java | 37 +++++++
.../AuthorizationMetadataPrivileges.java | 49 +++++++++
.../authorization/annotations/TestAnnotations.java | 121 +++++++++++++++++++++
4 files changed, 246 insertions(+)
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationExpression.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationExpression.java
new file mode 100644
index 0000000000..6173f61158
--- /dev/null
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationExpression.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.server.authorization.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is used to implement unified authentication in AOP. Use
Expressions to define the
+ * required privileges for an API.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AuthorizationExpression {
+ /**
+ * The expression to evaluate for authorization, which represents multiple
privileges.
+ *
+ * @return the expression to evaluate for authorization.
+ */
+ String expression() default "";
+}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadata.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadata.java
new file mode 100644
index 0000000000..41d7a3f52b
--- /dev/null
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadata.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.server.authorization.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.gravitino.MetadataObject;
+
+/** This annotation identify which parameters in the request are to be used
for authorization. */
+@Target({ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AuthorizationMetadata {
+ /**
+ * The type of the parameter to be used for authorization.
+ *
+ * @return the type of the parameter to be used for authorization.
+ */
+ MetadataObject.Type type();
+}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadataPrivileges.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadataPrivileges.java
new file mode 100644
index 0000000000..1e2703ccb7
--- /dev/null
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadataPrivileges.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.server.authorization.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.authorization.Privilege;
+
+/**
+ * Defines the annotation for authorizing access to an API. Use the
resourceType and privileges
+ * fields to define the required privileges and resource type for the API.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AuthorizationMetadataPrivileges {
+ /**
+ * The list of privileges required to access the API.
+ *
+ * @return the list of privileges required to access the API.
+ */
+ Privilege.Name[] privileges();
+
+ /**
+ * The resource type of the API.
+ *
+ * @return the resource type of the API.
+ */
+ MetadataObject.Type metadataType();
+}
diff --git
a/server-common/src/test/java/org/apache/gravitino/server/authorization/annotations/TestAnnotations.java
b/server-common/src/test/java/org/apache/gravitino/server/authorization/annotations/TestAnnotations.java
new file mode 100644
index 0000000000..9bad0c47d6
--- /dev/null
+++
b/server-common/src/test/java/org/apache/gravitino/server/authorization/annotations/TestAnnotations.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.server.authorization.annotations;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.authorization.Privilege;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestAnnotations {
+
+ // This class is used to test the AuthorizeResource annotation.
+ static class TestResourceAnnotationClass {
+
+ public void methodWithAnnotatedParam(
+ @AuthorizationMetadata(type = MetadataObject.Type.TABLE) String table)
{
+ // dummy method
+ }
+
+ public void listSchemas(
+ @AuthorizationMetadata(type = MetadataObject.Type.METALAKE) String
metalake,
+ @AuthorizationMetadata(type = MetadataObject.Type.CATALOG) String
catalog) {
+ // dummy method
+ }
+ }
+
+ // This class is used to test the AuthorizeApi annotation.
+ // 1. ResourceAuthorizeApi
+ // 2. ExpressionsAuthorizeApi
+ static class TestAuthorizeAnnotationClass {
+ @AuthorizationMetadataPrivileges(
+ privileges = {Privilege.Name.CREATE_CATALOG,
Privilege.Name.USE_CATALOG},
+ metadataType = MetadataObject.Type.CATALOG)
+ public void testAuthedMethodUseResourceType() {}
+
+ @AuthorizationExpression(expression = "CATALOG::CREATE_TABLE ||
TABLE::CREATE_TABLE")
+ public void testAuthedMethodUseExpression() {}
+ }
+
+ @Test
+ void testAuthorizeApiWithResourceType() throws NoSuchMethodException {
+ Class<TestAuthorizeAnnotationClass> testClass =
TestAuthorizeAnnotationClass.class;
+ Method method = testClass.getMethod("testAuthedMethodUseResourceType");
+
+ boolean hasAnnotation =
method.isAnnotationPresent(AuthorizationMetadataPrivileges.class);
+ Assertions.assertTrue(hasAnnotation);
+
+ AuthorizationMetadataPrivileges annotation =
+ method.getAnnotation(AuthorizationMetadataPrivileges.class);
+ Assertions.assertNotNull(annotation);
+
+ Assertions.assertArrayEquals(
+ new Privilege.Name[] {Privilege.Name.CREATE_CATALOG,
Privilege.Name.USE_CATALOG},
+ annotation.privileges());
+ Assertions.assertEquals(MetadataObject.Type.CATALOG,
annotation.metadataType());
+ }
+
+ @Test
+ void testAuthorizeApiWithExpression() throws NoSuchMethodException {
+ Class<TestAuthorizeAnnotationClass> testClass =
TestAuthorizeAnnotationClass.class;
+ Method method = testClass.getMethod("testAuthedMethodUseExpression");
+
+ boolean hasAnnotation =
method.isAnnotationPresent(AuthorizationExpression.class);
+ Assertions.assertTrue(hasAnnotation);
+
+ AuthorizationExpression annotation =
method.getAnnotation(AuthorizationExpression.class);
+ Assertions.assertNotNull(annotation);
+
+ Assertions.assertEquals(
+ "CATALOG::CREATE_TABLE || TABLE::CREATE_TABLE",
annotation.expression());
+ }
+
+ @Test
+ void testParameterAnnotationPresent() throws NoSuchMethodException {
+ Parameter argument =
+
TestResourceAnnotationClass.class.getMethod("methodWithAnnotatedParam",
String.class)
+ .getParameters()[0];
+ AuthorizationMetadata annotation =
argument.getAnnotation(AuthorizationMetadata.class);
+ Assertions.assertNotNull(annotation);
+ Assertions.assertEquals(MetadataObject.Type.TABLE, annotation.type());
+ }
+
+ @Test
+ void testAnnotateListSchemas() throws NoSuchMethodException {
+ Parameter[] arguments =
+ TestResourceAnnotationClass.class
+ .getMethod("listSchemas", String.class, String.class)
+ .getParameters();
+
+ Parameter argumentMetalake = arguments[0];
+ AuthorizationMetadata metalakeAnnotation =
+ argumentMetalake.getAnnotation(AuthorizationMetadata.class);
+ Assertions.assertNotNull(metalakeAnnotation);
+ Assertions.assertEquals(MetadataObject.Type.METALAKE,
metalakeAnnotation.type());
+
+ Parameter argumentCatalog = arguments[1];
+ AuthorizationMetadata catalogAnnotation =
+ argumentCatalog.getAnnotation(AuthorizationMetadata.class);
+ Assertions.assertNotNull(catalogAnnotation);
+ Assertions.assertEquals(MetadataObject.Type.CATALOG,
catalogAnnotation.type());
+ }
+}