This is an automated email from the ASF dual-hosted git repository.
fanng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new df433b0192 [#6758] feat(core): Support role pre-event to Gravitino
server (#6761)
df433b0192 is described below
commit df433b019232830721e5b0de064f8c0c851951e4
Author: Lord of Abyss <[email protected]>
AuthorDate: Mon Apr 7 11:13:29 2025 +0800
[#6758] feat(core): Support role pre-event to Gravitino server (#6761)
### What changes were proposed in this pull request?
Support role pre-event to Gravitino server

### Why are the changes needed?
Fix: #6758
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
local test.
---
.../api/event/AccessControlEventDispatcher.java | 24 ++
.../listener/api/event/CreateRolePreEvent.java | 97 +++++++
.../listener/api/event/DeleteRolePreEvent.java | 62 +++++
.../listener/api/event/GetRolePreEvent.java | 64 +++++
.../api/event/GrantPrivilegesPreEvent.java | 97 +++++++
.../listener/api/event/ListRoleNamesPreEvent.java | 80 ++++++
.../listener/api/event/OperationType.java | 7 +-
.../api/event/RevokePrivilegesPreEvent.java | 96 +++++++
.../gravitino/listener/api/event/RolePreEvent.java | 42 +++
.../apache/gravitino/utils/NameIdentifierUtil.java | 11 +
.../org/apache/gravitino/utils/NamespaceUtil.java | 10 +
.../listener/api/event/TestRoleEvent.java | 291 +++++++++++++++++++++
.../gravitino/utils/TestNameIdentifierUtil.java | 14 +
.../apache/gravitino/utils/TestNamespaceUtil.java | 11 +
docs/gravitino-server-config.md | 1 +
15 files changed, 906 insertions(+), 1 deletion(-)
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/AccessControlEventDispatcher.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/AccessControlEventDispatcher.java
index f9d478243b..d6db8bfe89 100644
---
a/core/src/main/java/org/apache/gravitino/listener/api/event/AccessControlEventDispatcher.java
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/AccessControlEventDispatcher.java
@@ -335,6 +335,10 @@ public class AccessControlEventDispatcher implements
AccessControlDispatcher {
Map<String, String> properties,
List<SecurableObject> securableObjects)
throws RoleAlreadyExistsException, NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(
+ new CreateRolePreEvent(initiator, metalake, role, properties,
securableObjects));
try {
// TODO: add Event
return dispatcher.createRole(metalake, role, properties,
securableObjects);
@@ -348,6 +352,9 @@ public class AccessControlEventDispatcher implements
AccessControlDispatcher {
@Override
public Role getRole(String metalake, String role)
throws NoSuchRoleException, NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new GetRolePreEvent(initiator, metalake, role));
try {
// TODO: add Event
return dispatcher.getRole(metalake, role);
@@ -360,6 +367,9 @@ public class AccessControlEventDispatcher implements
AccessControlDispatcher {
/** {@inheritDoc} */
@Override
public boolean deleteRole(String metalake, String role) throws
NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new DeleteRolePreEvent(initiator, metalake, role));
try {
// TODO: add Event
return dispatcher.deleteRole(metalake, role);
@@ -372,6 +382,9 @@ public class AccessControlEventDispatcher implements
AccessControlDispatcher {
/** {@inheritDoc} */
@Override
public String[] listRoleNames(String metalake) throws
NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new ListRoleNamesPreEvent(initiator, metalake));
try {
// TODO: add Event
return dispatcher.listRoleNames(metalake);
@@ -385,6 +398,9 @@ public class AccessControlEventDispatcher implements
AccessControlDispatcher {
@Override
public String[] listRoleNamesByObject(String metalake, MetadataObject object)
throws NoSuchMetalakeException, NoSuchMetadataObjectException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new ListRoleNamesPreEvent(initiator, metalake,
object));
try {
// TODO: add Event
return dispatcher.listRoleNamesByObject(metalake, object);
@@ -399,6 +415,10 @@ public class AccessControlEventDispatcher implements
AccessControlDispatcher {
public Role grantPrivilegeToRole(
String metalake, String role, MetadataObject object, Set<Privilege>
privileges)
throws NoSuchGroupException, NoSuchRoleException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(
+ new GrantPrivilegesPreEvent(initiator, metalake, role, object,
privileges));
try {
// TODO: add Event
return dispatcher.grantPrivilegeToRole(metalake, role, object,
privileges);
@@ -413,6 +433,10 @@ public class AccessControlEventDispatcher implements
AccessControlDispatcher {
public Role revokePrivilegesFromRole(
String metalake, String role, MetadataObject object, Set<Privilege>
privileges)
throws NoSuchMetalakeException, NoSuchRoleException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(
+ new RevokePrivilegesPreEvent(initiator, metalake, role, object,
privileges));
try {
// TODO: add Event
return dispatcher.revokePrivilegesFromRole(metalake, role, object,
privileges);
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRolePreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRolePreEvent.java
new file mode 100644
index 0000000000..0fe009fd9f
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRolePreEvent.java
@@ -0,0 +1,97 @@
+/*
+ * 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.listener.api.event;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.authorization.SecurableObject;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+
+/**
+ * Represents an event generated before a role is successfully created. This
class encapsulates the
+ * details of the role creation event prior to its completion.
+ */
+@DeveloperApi
+public class CreateRolePreEvent extends RolePreEvent {
+ private final String roleName;
+ private final Map<String, String> properties;
+ private final List<SecurableObject> securableObjects;
+
+ /**
+ * Constructs a new {@link CreateRolePreEvent} instance with the specified
initiator, identifier,
+ * role, properties, and securable objects.
+ *
+ * @param initiator The user who initiated the event.
+ * @param metalake The name of the metalake.
+ * @param roleName The name of the role being created.
+ * @param properties The properties of the role being created.
+ * @param securableObjects The list of securable objects belonging to the
role.
+ */
+ protected CreateRolePreEvent(
+ String initiator,
+ String metalake,
+ String roleName,
+ Map<String, String> properties,
+ List<SecurableObject> securableObjects) {
+ super(initiator, NameIdentifierUtil.ofRole(metalake, roleName));
+
+ this.roleName = roleName;
+ this.properties = properties;
+ this.securableObjects = securableObjects;
+ }
+
+ /**
+ * Returns the name of the role being created.
+ *
+ * @return The name of the role being created.
+ */
+ public String roleName() {
+ return roleName;
+ }
+
+ /**
+ * Returns the properties of the role being created.
+ *
+ * @return The properties of the role being created.
+ */
+ protected Map<String, String> properties() {
+ return properties;
+ }
+
+ /**
+ * Returns the list of securable objects that belong to the role.
+ *
+ * @return The list of securable objects that belong to the role.
+ */
+ protected List<SecurableObject> securableObjects() {
+ return securableObjects;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.CREATE_ROLE;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteRolePreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteRolePreEvent.java
new file mode 100644
index 0000000000..1c13410e0e
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteRolePreEvent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.listener.api.event;
+
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+
+/** Represents an event that is generated before a role is successfully
deleted. */
+@DeveloperApi
+public class DeleteRolePreEvent extends RolePreEvent {
+ private final String roleName;
+
+ /**
+ * Constructs a new {@link DeleteRolePreEvent} instance with specified
initiator, identifier and
+ * role name.
+ *
+ * @param initiator the user who initiated the event.
+ * @param metalake The name of the metalake.
+ * @param roleName the name of the role to be deleted.
+ */
+ public DeleteRolePreEvent(String initiator, String metalake, String
roleName) {
+ super(initiator, NameIdentifierUtil.ofRole(metalake, roleName));
+
+ this.roleName = roleName;
+ }
+
+ /**
+ * Returns the name of the role to be deleted.
+ *
+ * @return The name of the role to be deleted.
+ */
+ public String roleName() {
+ return roleName;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.DELETE_ROLE;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/GetRolePreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetRolePreEvent.java
new file mode 100644
index 0000000000..6aa0f632ed
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetRolePreEvent.java
@@ -0,0 +1,64 @@
+/*
+ * 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.listener.api.event;
+
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+
+/**
+ * Represents an event triggered before retrieving a role from a specific
metalake. This class
+ * encapsulates the details of the role retrieval event prior to its execution.
+ */
+@DeveloperApi
+public class GetRolePreEvent extends RolePreEvent {
+ private final String roleName;
+
+ /**
+ * Constructs a new {@link GetRolePreEvent} instance with the specified
initiator, identifier, and
+ * role name.
+ *
+ * @param initiator The user who initiated the event.
+ * @param metalake The name of the metalake where the role is being
retrieved from.
+ * @param roleName The name of the role being retrieved.
+ */
+ public GetRolePreEvent(String initiator, String metalake, String roleName) {
+ super(initiator, NameIdentifierUtil.ofRole(metalake, roleName));
+ this.roleName = roleName;
+ }
+
+ /**
+ * Returns the name of the role.
+ *
+ * @return The name of the role being retrieved.
+ */
+ public String roleName() {
+ return roleName;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.GET_ROLE;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesPreEvent.java
new file mode 100644
index 0000000000..3f33c8cce4
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesPreEvent.java
@@ -0,0 +1,97 @@
+/*
+ * 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.listener.api.event;
+
+import java.util.Set;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+
+/**
+ * Represents an event generated before granting a set of privileges to a
role. This class
+ * encapsulates the details of the privilege granting event prior to its
execution.
+ */
+@DeveloperApi
+public class GrantPrivilegesPreEvent extends RolePreEvent {
+ private final String roleName;
+ private final MetadataObject object;
+ private final Set<Privilege> privileges;
+
+ /**
+ * Constructs a new {@link GrantPrivilegesPreEvent} instance with the
specified initiator,
+ * identifier, role name, object, and privileges.
+ *
+ * @param initiator The name of the user who initiated the event.
+ * @param metalake The name of the metalake.
+ * @param roleName The name of the role to which privileges will be granted.
+ * @param object The {@link MetadataObject} instance related to the role.
+ * @param privileges The set of privileges to grant to the role.
+ */
+ public GrantPrivilegesPreEvent(
+ String initiator,
+ String metalake,
+ String roleName,
+ MetadataObject object,
+ Set<Privilege> privileges) {
+ super(initiator, NameIdentifierUtil.ofRole(metalake, roleName));
+
+ this.roleName = roleName;
+ this.object = object;
+ this.privileges = privileges;
+ }
+
+ /**
+ * Returns the name of the role.
+ *
+ * @return The name of the role to which privileges will be granted.
+ */
+ public String roleName() {
+ return roleName;
+ }
+
+ /**
+ * Returns the {@link MetadataObject} instance.
+ *
+ * @return The {@link MetadataObject} instance related to the role.
+ */
+ public MetadataObject object() {
+ return object;
+ }
+
+ /**
+ * Returns the set of privileges to grant.
+ *
+ * @return The set of privileges to grant to the role.
+ */
+ public Set<Privilege> privileges() {
+ return privileges;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.GRANT_PRIVILEGES;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesPreEvent.java
new file mode 100644
index 0000000000..34642a74ae
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesPreEvent.java
@@ -0,0 +1,80 @@
+/*
+ * 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.listener.api.event;
+
+import java.util.Optional;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+
+/**
+ * Represents an event generated before listing the role names of a metalake.
This class
+ * encapsulates the details of the role name listing event prior to its
execution.
+ */
+@DeveloperApi
+public class ListRoleNamesPreEvent extends RolePreEvent {
+ private final Optional<MetadataObject> object;
+
+ /**
+ * Constructs a new {@link ListRoleNamesPreEvent} instance with the
specified initiator and
+ * identifier.
+ *
+ * @param initiator The user who initiated the event.
+ * @param metalake The name of the metalake for which role names are being
listed.
+ */
+ public ListRoleNamesPreEvent(String initiator, String metalake) {
+ super(initiator, NameIdentifierUtil.ofMetalake(metalake));
+
+ this.object = Optional.empty();
+ }
+
+ /**
+ * Constructs a new {@link ListRoleNamesPreEvent} instance with the
specified initiator,
+ * identifier, and {@link MetadataObject} instance.
+ *
+ * @param initiator The user who initiated the event.
+ * @param metalake The name of the metalake for which role names are being
listed.
+ * @param object The {@link MetadataObject} instance related to the role
names.
+ */
+ public ListRoleNamesPreEvent(String initiator, String metalake,
MetadataObject object) {
+ super(initiator, NameIdentifierUtil.ofMetalake(metalake));
+
+ this.object = Optional.ofNullable(object);
+ }
+
+ /**
+ * Returns the {@link MetadataObject} instance related to the role names.
+ *
+ * @return The {@link MetadataObject} instance, if present.
+ */
+ public Optional<MetadataObject> object() {
+ return object;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.LIST_ROLE_NAMES;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
index 7905109421..aebf43c3ba 100644
---
a/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
@@ -131,7 +131,12 @@ public enum OperationType {
GRANT_GROUP_ROLES,
REVOKE_GROUP_ROLES,
- // TODO ROLE
+ CREATE_ROLE,
+ DELETE_ROLE,
+ GET_ROLE,
+ LIST_ROLE_NAMES,
+ GRANT_PRIVILEGES,
+ REVOKE_PRIVILEGES,
UNKNOWN,
}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesPreEvent.java
new file mode 100644
index 0000000000..260eb374f0
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesPreEvent.java
@@ -0,0 +1,96 @@
+/*
+ * 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.listener.api.event;
+
+import java.util.Set;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+
+/**
+ * Represents an event generated before revoking a set of privileges from a
role. This class
+ * encapsulates the details of the privilege revocation event prior to its
execution.
+ */
+@DeveloperApi
+public class RevokePrivilegesPreEvent extends RolePreEvent {
+ private final String roleName;
+ private final MetadataObject object;
+ private final Set<Privilege> privileges;
+
+ /**
+ * Constructs a new {@link RevokePrivilegesPreEvent} instance with the
specified initiator,
+ * identifier, role name, object, and privileges.
+ *
+ * @param initiator The user who initiated the event.
+ * @param metalake The name of the metalake.
+ * @param roleName The name of the role from which privileges will be
revoked.
+ * @param object The {@link MetadataObject} instance related to the role.
+ * @param privileges The set of privileges to revoke from the role.
+ */
+ public RevokePrivilegesPreEvent(
+ String initiator,
+ String metalake,
+ String roleName,
+ MetadataObject object,
+ Set<Privilege> privileges) {
+ super(initiator, NameIdentifierUtil.ofRole(metalake, roleName));
+ this.roleName = roleName;
+ this.object = object;
+ this.privileges = privileges;
+ }
+
+ /**
+ * Returns the name of the role.
+ *
+ * @return The name of the role from which privileges will be revoked.
+ */
+ public String roleName() {
+ return roleName;
+ }
+
+ /**
+ * Returns the {@link MetadataObject} instance related to the role.
+ *
+ * @return The {@link MetadataObject} instance associated with the role.
+ */
+ public MetadataObject object() {
+ return object;
+ }
+
+ /**
+ * Returns the set of privileges to revoke.
+ *
+ * @return The set of privileges to revoke from the role.
+ */
+ public Set<Privilege> privileges() {
+ return privileges;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.REVOKE_PRIVILEGES;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/RolePreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/RolePreEvent.java
new file mode 100644
index 0000000000..0a22ea377f
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/RolePreEvent.java
@@ -0,0 +1,42 @@
+/*
+ * 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.listener.api.event;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * Represents a pre-event for a role operation request. This class serves as a
base class for events
+ * triggered before a role operation takes place.
+ */
+@DeveloperApi
+public abstract class RolePreEvent extends PreEvent {
+
+ /**
+ * Constructs a new {@link RolePreEvent} instance with the specified
initiator and identifier for
+ * a role operation.
+ *
+ * @param initiator The user who triggered the event.
+ * @param identifier The identifier of the metalake being operated on.
+ */
+ protected RolePreEvent(String initiator, NameIdentifier identifier) {
+ super(initiator, identifier);
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
b/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
index b2b3d83bed..e092b85ca8 100644
--- a/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
+++ b/core/src/main/java/org/apache/gravitino/utils/NameIdentifierUtil.java
@@ -115,6 +115,17 @@ public class NameIdentifierUtil {
return NameIdentifier.of(NamespaceUtil.ofUser(metalake), userName);
}
+ /**
+ * Create the role {@link NameIdentifier} with the given metalake and role
name.
+ *
+ * @param metalake The metalake name
+ * @param roleName The role name
+ * @return the created role {@link NameIdentifier}
+ */
+ public static NameIdentifier ofRole(String metalake, String roleName) {
+ return NameIdentifier.of(NamespaceUtil.ofRole(metalake), roleName);
+ }
+
/**
* Create the column {@link NameIdentifier} with the given metalake,
catalog, schema, table and
* column name.
diff --git a/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
b/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
index 76670b0066..f904592789 100644
--- a/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
+++ b/core/src/main/java/org/apache/gravitino/utils/NamespaceUtil.java
@@ -92,6 +92,16 @@ public class NamespaceUtil {
return Namespace.of(metalake, Entity.SYSTEM_CATALOG_RESERVED_NAME,
Entity.USER_SCHEMA_NAME);
}
+ /**
+ * Create a namespace for role.
+ *
+ * @param metalake The metalake name
+ * @return A namespace for role
+ */
+ public static Namespace ofRole(String metalake) {
+ return Namespace.of(metalake, Entity.SYSTEM_CATALOG_RESERVED_NAME,
Entity.ROLE_SCHEMA_NAME);
+ }
+
/**
* Create a namespace for group.
*
diff --git
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestRoleEvent.java
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestRoleEvent.java
new file mode 100644
index 0000000000..e4220e037a
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestRoleEvent.java
@@ -0,0 +1,291 @@
+/*
+ * 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.listener.api.event;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.authorization.Group;
+import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.authorization.Privileges;
+import org.apache.gravitino.authorization.Role;
+import org.apache.gravitino.authorization.SecurableObject;
+import org.apache.gravitino.authorization.User;
+import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.listener.DummyEventListener;
+import org.apache.gravitino.listener.EventBus;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class TestRoleEvent {
+ private static final String METALAKE = "demo_metalake";
+ private String groupName;
+ private String userName;
+ private AccessControlEventDispatcher dispatcher;
+ private AccessControlEventDispatcher failureDispatcher;
+ private DummyEventListener dummyEventListener;
+ private String roleName;
+ private String otherRoleName;
+ private NameIdentifier identifier;
+ private Map<String, String> properties;
+ private List<SecurableObject> securableObjects;
+ private Set<Privilege> privileges;
+ private MetadataObject metadataObject;
+ private Role role;
+
+ @BeforeAll
+ void init() {
+ this.groupName = "demo_group";
+ this.userName = "demo_user";
+ this.roleName = "admin";
+ this.otherRoleName = "user";
+ this.identifier = NameIdentifier.of(METALAKE);
+ this.properties = ImmutableMap.of("comment", "test comment");
+ this.securableObjects =
+ ImmutableList.of(
+ getMockSecurableObjects(Privilege.Name.CREATE_TABLE,
Privilege.Name.MODIFY_TABLE));
+ this.role = getMockRole(roleName, properties, securableObjects);
+ this.privileges =
+ Sets.newHashSet(
+ Privileges.allow(Privilege.Name.CREATE_TABLE),
+ Privileges.allow(Privilege.Name.MODIFY_TABLE));
+ this.metadataObject = getMockMetadataObject("test_metalake",
MetadataObject.Type.METALAKE);
+
+ this.dummyEventListener = new DummyEventListener();
+ EventBus eventBus = new
EventBus(Collections.singletonList(dummyEventListener));
+ this.dispatcher = new AccessControlEventDispatcher(eventBus,
mockRoleDispatcher());
+ this.failureDispatcher =
+ new AccessControlEventDispatcher(eventBus,
mockExceptionRoleDispatcher());
+
+ System.out.println(failureDispatcher);
+ }
+
+ @Test
+ void testCreateRole() {
+ dispatcher.createRole(
+ METALAKE, roleName, ImmutableMap.of("comment", "test comment"),
securableObjects);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(CreateRolePreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.CREATE_ROLE,
preEvent.operationType());
+
+ CreateRolePreEvent createRolePreEvent = (CreateRolePreEvent) preEvent;
+ Assertions.assertEquals(
+ NameIdentifierUtil.ofRole(METALAKE, roleName),
createRolePreEvent.identifier());
+ Assertions.assertEquals(roleName, createRolePreEvent.roleName());
+ Assertions.assertEquals(properties, createRolePreEvent.properties());
+ Assertions.assertEquals(securableObjects,
createRolePreEvent.securableObjects());
+ }
+
+ @Test
+ void testDeleteRolePreEvent() {
+ dispatcher.deleteRole(METALAKE, roleName);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(DeleteRolePreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.DELETE_ROLE,
preEvent.operationType());
+
+ DeleteRolePreEvent deleteRolePreEvent = (DeleteRolePreEvent) preEvent;
+ Assertions.assertEquals(
+ NameIdentifierUtil.ofRole(METALAKE, roleName),
deleteRolePreEvent.identifier());
+ Assertions.assertEquals(roleName, deleteRolePreEvent.roleName());
+ }
+
+ @Test
+ void testGetRolePreEvent() {
+ dispatcher.getRole(METALAKE, roleName);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(GetRolePreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.GET_ROLE, preEvent.operationType());
+
+ GetRolePreEvent getRolePreEvent = (GetRolePreEvent) preEvent;
+ Assertions.assertEquals(
+ NameIdentifierUtil.ofRole(METALAKE, roleName),
getRolePreEvent.identifier());
+ Assertions.assertEquals(roleName, getRolePreEvent.roleName());
+ }
+
+ @Test
+ void testListRolesEvent() {
+ dispatcher.listRoleNames(METALAKE);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(ListRoleNamesPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.LIST_ROLE_NAMES,
preEvent.operationType());
+
+ ListRoleNamesPreEvent listRoleNamesPreEvent = (ListRoleNamesPreEvent)
preEvent;
+ Assertions.assertEquals(identifier, listRoleNamesPreEvent.identifier());
+ Assertions.assertFalse(listRoleNamesPreEvent.object().isPresent());
+ }
+
+ @Test
+ void testListRolesFromObject() {
+ dispatcher.listRoleNamesByObject(METALAKE, securableObjects.get(0));
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(ListRoleNamesPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.LIST_ROLE_NAMES,
preEvent.operationType());
+
+ ListRoleNamesPreEvent listRoleNamesPreEvent = (ListRoleNamesPreEvent)
preEvent;
+ Assertions.assertEquals(identifier, listRoleNamesPreEvent.identifier());
+ Assertions.assertTrue(listRoleNamesPreEvent.object().isPresent());
+ Assertions.assertEquals(securableObjects.get(0),
listRoleNamesPreEvent.object().get());
+ }
+
+ @Test
+ void testGrantPrivilegesToRole() {
+ dispatcher.grantPrivilegeToRole(METALAKE, roleName, metadataObject,
privileges);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(GrantPrivilegesPreEvent.class,
preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.GRANT_PRIVILEGES,
preEvent.operationType());
+
+ GrantPrivilegesPreEvent grantPrivilegesPreEvent =
(GrantPrivilegesPreEvent) preEvent;
+ Assertions.assertEquals(
+ NameIdentifierUtil.ofRole(METALAKE, roleName),
grantPrivilegesPreEvent.identifier());
+ Assertions.assertEquals(roleName, grantPrivilegesPreEvent.roleName());
+ Assertions.assertEquals(metadataObject, grantPrivilegesPreEvent.object());
+ Assertions.assertEquals(privileges, grantPrivilegesPreEvent.privileges());
+ }
+
+ @Test
+ void testRevokePrivilegesFromRole() {
+ dispatcher.revokePrivilegesFromRole(METALAKE, roleName, metadataObject,
privileges);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(RevokePrivilegesPreEvent.class,
preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.REVOKE_PRIVILEGES,
preEvent.operationType());
+
+ RevokePrivilegesPreEvent revokePrivilegesPreEvent =
(RevokePrivilegesPreEvent) preEvent;
+ Assertions.assertEquals(
+ NameIdentifierUtil.ofRole(METALAKE, roleName),
revokePrivilegesPreEvent.identifier());
+ Assertions.assertEquals(roleName, revokePrivilegesPreEvent.roleName());
+ Assertions.assertEquals(metadataObject, revokePrivilegesPreEvent.object());
+ Assertions.assertEquals(privileges, revokePrivilegesPreEvent.privileges());
+ }
+
+ private AccessControlEventDispatcher mockRoleDispatcher() {
+ AccessControlEventDispatcher dispatcher =
mock(AccessControlEventDispatcher.class);
+ Group mockGroup = getMockGroup(groupName, ImmutableList.of(roleName,
otherRoleName));
+ User mockUser = getMockUser(userName, ImmutableList.of(roleName,
otherRoleName));
+
+ when(dispatcher.createRole(METALAKE, roleName, properties,
securableObjects)).thenReturn(role);
+ when(dispatcher.deleteRole(METALAKE, roleName)).thenReturn(true);
+ when(dispatcher.deleteRole(METALAKE, otherRoleName)).thenReturn(false);
+ when(dispatcher.getRole(METALAKE, roleName)).thenReturn(role);
+ when(dispatcher.grantRolesToGroup(
+ METALAKE, ImmutableList.of(roleName, otherRoleName), groupName))
+ .thenReturn(mockGroup);
+ when(dispatcher.grantRolesToUser(METALAKE, ImmutableList.of(roleName,
otherRoleName), userName))
+ .thenReturn(mockUser);
+ when(dispatcher.revokeRolesFromGroup(
+ METALAKE, ImmutableList.of(roleName, otherRoleName), groupName))
+ .thenReturn(mockGroup);
+ when(dispatcher.listRoleNames(METALAKE)).thenReturn(new String[]
{roleName, otherRoleName});
+ when(dispatcher.listRoleNamesByObject(METALAKE, securableObjects.get(0)))
+ .thenReturn(new String[] {roleName, otherRoleName});
+ when(dispatcher.grantPrivilegeToRole(METALAKE, roleName, metadataObject,
privileges))
+ .thenReturn(role);
+ when(dispatcher.revokePrivilegesFromRole(METALAKE, roleName,
metadataObject, privileges))
+ .thenReturn(role);
+
+ return dispatcher;
+ }
+
+ private AccessControlEventDispatcher mockExceptionRoleDispatcher() {
+ return mock(
+ AccessControlEventDispatcher.class,
+ invocation -> {
+ throw new GravitinoRuntimeException("Exception for all methods");
+ });
+ }
+
+ public SecurableObject getMockSecurableObjects(Privilege.Name... names) {
+ SecurableObject mockSecurableObject = mock(SecurableObject.class);
+ List<Privilege> privileges =
+
Arrays.stream(names).map(Privileges::allow).collect(Collectors.toList());
+ when(mockSecurableObject.privileges()).thenReturn(privileges);
+
+ return mockSecurableObject;
+ }
+
+ public Role getMockRole(
+ String roleName, Map<String, String> properties, List<SecurableObject>
securableObjects) {
+ Role mockRole = mock(Role.class);
+ when(mockRole.name()).thenReturn(roleName);
+ when(mockRole.properties()).thenReturn(properties);
+ when(mockRole.securableObjects()).thenReturn(securableObjects);
+
+ return mockRole;
+ }
+
+ private Group getMockGroup(String name, List<String> roles) {
+ Group mockGroup = mock(Group.class);
+ when(mockGroup.name()).thenReturn(name);
+ when(mockGroup.roles()).thenReturn(roles);
+
+ return mockGroup;
+ }
+
+ private User getMockUser(String name, List<String> roles) {
+ User user = mock(User.class);
+ when(user.name()).thenReturn(name);
+ when(user.roles()).thenReturn(roles);
+
+ return user;
+ }
+
+ private MetadataObject getMockMetadataObject(String name,
MetadataObject.Type type) {
+ MetadataObject mockMetadataObject = mock(MetadataObject.class);
+ when(mockMetadataObject.name()).thenReturn(name);
+ when(mockMetadataObject.type()).thenReturn(type);
+
+ return mockMetadataObject;
+ }
+}
diff --git
a/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
b/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
index fcd7455f7c..e13ec72211 100644
--- a/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
+++ b/core/src/test/java/org/apache/gravitino/utils/TestNameIdentifierUtil.java
@@ -183,4 +183,18 @@ public class TestNameIdentifierUtil {
Assertions.assertThrows(
IllegalArgumentException.class, () -> NameIdentifierUtil.ofGroup(null,
groupName));
}
+
+ @Test
+ void testOfRole() {
+ String roleName = "roleA";
+ String metalake = "demo_metalake";
+
+ NameIdentifier nameIdentifier = NameIdentifierUtil.ofRole(metalake,
roleName);
+ Assertions.assertEquals(
+ Joiner.on(".")
+ .join(metalake, Entity.SYSTEM_CATALOG_RESERVED_NAME,
Entity.ROLE_SCHEMA_NAME, roleName),
+ nameIdentifier.toString());
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> NameIdentifierUtil.ofRole(null,
roleName));
+ }
}
diff --git
a/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
b/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
index 9c26043a12..58703b23b9 100644
--- a/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
+++ b/core/src/test/java/org/apache/gravitino/utils/TestNamespaceUtil.java
@@ -106,4 +106,15 @@ public class TestNamespaceUtil {
namespace.toString());
Assertions.assertThrows(IllegalArgumentException.class, () ->
NamespaceUtil.ofGroup(null));
}
+
+ @Test
+ void testOfRole() {
+ String metalake = "metalake";
+ Namespace namespace = NamespaceUtil.ofRole(metalake);
+
+ Assertions.assertEquals(
+ Joiner.on(".").join(metalake, Entity.SYSTEM_CATALOG_RESERVED_NAME,
Entity.ROLE_SCHEMA_NAME),
+ namespace.toString());
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
NamespaceUtil.ofRole(null));
+ }
}
diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md
index 95d50c117e..db0d5f3132 100644
--- a/docs/gravitino-server-config.md
+++ b/docs/gravitino-server-config.md
@@ -149,6 +149,7 @@ Gravitino triggers a pre-event before the operation, a
post-event after the comp
| Gravitino server tag operation | `ListTagsPreEvent`,
`ListTagsInfoPreEvent`, `CreateTagPreEvent`, `GetTagPreEvent`,
`AlterTagPreEvent`, `DeleteTagPreEvent`, `ListMetadataObjectsForTagPreEvent`,
`ListTagsForMetadataObjectPreEvent`, `ListTagsInfoForMetadataObjectPreEvent`,
`AssociateTagsForMetadataObjectPreEvent`, `GetTagForMetadataObjectPreEvent` |
0.9.0-incubating |
| Gravitino server user operation | `AddUserPreEvent`, `GetUserPreEvent`,
`ListUserNamesPreEvent`, `ListUsersPreEvent`, `RemoveUserPreEvent`,
`GrantUserRolesPreEvent`, `RevokeUserRolesPreEvent`
| 0.9.0-incubating |
| Gravitino server group operation | `AddGroupPreEvent`,
`GetGroupPreEvent`, `ListGroupNamesPreEvent`, `ListGroupsPreEvent`,
`RemoveGroupPreEvent`, `GrantGroupRolesPreEvent`, `RevokeGroupRolesPreEvent`
|
0.9.0-incubating |
+| Gravitino server role operation | `CreateRolePreEvent`,
`DeleteRolePreEvent`, `GetRolePreEvent`, `GrantPrivilegesPreEvent`,
`ListRoleNamesPreEvent`, `RevokePrivilegesPreEvent`
|
0.9.0-incubating |
#### Event listener plugin