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 0911a63cd2 [#6711] feat(core): Support user pre-event to Gravitino
server (#6714)
0911a63cd2 is described below
commit 0911a63cd2503dba96e91aa70bcdf1c87c89c8f2
Author: Lord of Abyss <[email protected]>
AuthorDate: Mon Mar 24 21:21:13 2025 +0800
[#6711] feat(core): Support user pre-event to Gravitino server (#6714)
### What changes were proposed in this pull request?
Support user pre-event to Gravitino server.
<img width="1663" alt="image"
src="https://github.com/user-attachments/assets/ecd81df0-af3a-41d9-8709-1b8d2eae677c"
/>
### Why are the changes needed?
Fix: #6711
### Does this PR introduce _any_ user-facing change?
No
### How was this patch tested?
local test.
---
.../java/org/apache/gravitino/GravitinoEnv.java | 10 +-
.../api/event/AccessControlEventDispatcher.java | 361 +++++++++++++++++++++
.../listener/api/event/AddUserPreEvent.java | 61 ++++
.../listener/api/event/GetUserPreEvent.java | 61 ++++
.../listener/api/event/ListUserNamesPreEvent.java | 48 +++
.../listener/api/event/ListUsersPreEvent.java | 48 +++
.../listener/api/event/OperationType.java | 11 +
.../listener/api/event/RemoveUserPreEvent.java | 61 ++++
.../gravitino/listener/api/event/UserPreEvent.java | 38 +++
.../listener/api/event/TestUserEvent.java | 187 +++++++++++
docs/gravitino-server-config.md | 21 +-
11 files changed, 893 insertions(+), 14 deletions(-)
diff --git a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
index 385bc53392..4e9fc02b49 100644
--- a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
+++ b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
@@ -65,6 +65,7 @@ import org.apache.gravitino.listener.SchemaEventDispatcher;
import org.apache.gravitino.listener.TableEventDispatcher;
import org.apache.gravitino.listener.TagEventDispatcher;
import org.apache.gravitino.listener.TopicEventDispatcher;
+import org.apache.gravitino.listener.api.event.AccessControlEventDispatcher;
import org.apache.gravitino.lock.LockManager;
import org.apache.gravitino.metalake.MetalakeDispatcher;
import org.apache.gravitino.metalake.MetalakeManager;
@@ -492,11 +493,12 @@ public class GravitinoEnv {
// Create and initialize access control related modules
boolean enableAuthorization = config.get(Configs.ENABLE_AUTHORIZATION);
if (enableAuthorization) {
+ AccessControlManager accessControlManager =
+ new AccessControlManager(entityStore, idGenerator, config);
AccessControlHookDispatcher accessControlHookDispatcher =
- new AccessControlHookDispatcher(
- new AccessControlManager(entityStore, idGenerator, config));
-
- this.accessControlDispatcher = accessControlHookDispatcher;
+ new AccessControlHookDispatcher(accessControlManager);
+ this.accessControlDispatcher =
+ new AccessControlEventDispatcher(eventBus,
accessControlHookDispatcher);
this.ownerManager = new OwnerManager(entityStore);
this.futureGrantManager = new FutureGrantManager(entityStore,
ownerManager);
} else {
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
new file mode 100644
index 0000000000..7c658ef942
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/AccessControlEventDispatcher.java
@@ -0,0 +1,361 @@
+/*
+ * 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 java.util.Set;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.authorization.AccessControlDispatcher;
+import org.apache.gravitino.authorization.Group;
+import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.authorization.Role;
+import org.apache.gravitino.authorization.SecurableObject;
+import org.apache.gravitino.authorization.User;
+import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
+import org.apache.gravitino.exceptions.IllegalRoleException;
+import org.apache.gravitino.exceptions.NoSuchGroupException;
+import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
+import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NoSuchRoleException;
+import org.apache.gravitino.exceptions.NoSuchUserException;
+import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
+import org.apache.gravitino.exceptions.UserAlreadyExistsException;
+import org.apache.gravitino.listener.EventBus;
+import org.apache.gravitino.utils.PrincipalUtils;
+
+/**
+ * An implementation of the {@link AccessControlDispatcher} interface that
dispatches events to the
+ * specified event bus.
+ */
+public class AccessControlEventDispatcher implements AccessControlDispatcher {
+ private final EventBus eventBus;
+ private final AccessControlDispatcher dispatcher;
+
+ /**
+ * Construct a new {@link AccessControlEventDispatcher} instance with the
specified event bus and
+ * access control dispatcher.
+ *
+ * @param eventBus The EventBus to which events will be dispatched.
+ * @param dispatcher The underlying {@link AccessControlDispatcher} that
will perform the actual
+ * access control operations.
+ */
+ public AccessControlEventDispatcher(EventBus eventBus,
AccessControlDispatcher dispatcher) {
+ this.eventBus = eventBus;
+ this.dispatcher = dispatcher;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public User addUser(String metalake, String user)
+ throws UserAlreadyExistsException, NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new AddUserPreEvent(initiator,
NameIdentifier.of(metalake), user));
+ try {
+ // TODO add Event
+ return dispatcher.addUser(metalake, user);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean removeUser(String metalake, String user) throws
NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new RemoveUserPreEvent(initiator,
NameIdentifier.of(metalake), user));
+ try {
+ // TODO: add Event
+ return dispatcher.removeUser(metalake, user);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public User getUser(String metalake, String user)
+ throws NoSuchUserException, NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new GetUserPreEvent(initiator,
NameIdentifier.of(metalake), user));
+ try {
+ return dispatcher.getUser(metalake, user);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public User[] listUsers(String metalake) throws NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new ListUsersPreEvent(initiator,
NameIdentifier.of(metalake)));
+ try {
+ // TODO: add Event
+ return dispatcher.listUsers(metalake);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String[] listUserNames(String metalake) throws
NoSuchMetalakeException {
+ String initiator = PrincipalUtils.getCurrentUserName();
+
+ eventBus.dispatchEvent(new ListUserNamesPreEvent(initiator,
NameIdentifier.of(metalake)));
+ try {
+ // TODO: add Event
+ return dispatcher.listUserNames(metalake);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Group addGroup(String metalake, String group)
+ throws GroupAlreadyExistsException, NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.addGroup(metalake, group);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean removeGroup(String metalake, String group) throws
NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.removeGroup(metalake, group);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Group getGroup(String metalake, String group)
+ throws NoSuchGroupException, NoSuchMetalakeException {
+ try {
+ return dispatcher.getGroup(metalake, group);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Group[] listGroups(String metalake) {
+ try {
+ // TODO: add Event
+ return dispatcher.listGroups(metalake);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String[] listGroupNames(String metalake) {
+ try {
+ // TODO: add Event
+ return dispatcher.listGroupNames(metalake);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public User grantRolesToUser(String metalake, List<String> roles, String
user)
+ throws NoSuchUserException, IllegalRoleException,
NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.grantRolesToUser(metalake, roles, user);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Group grantRolesToGroup(String metalake, List<String> roles, String
group)
+ throws NoSuchGroupException, IllegalRoleException,
NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.grantRolesToGroup(metalake, roles, group);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Group revokeRolesFromGroup(String metalake, List<String> roles,
String group)
+ throws NoSuchGroupException, IllegalRoleException,
NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.revokeRolesFromGroup(metalake, roles, group);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public User revokeRolesFromUser(String metalake, List<String> roles, String
user)
+ throws NoSuchUserException, IllegalRoleException,
NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.revokeRolesFromUser(metalake, roles, user);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isServiceAdmin(String user) {
+ try {
+ // TODO: add Event
+ return dispatcher.isServiceAdmin(user);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Role createRole(
+ String metalake,
+ String role,
+ Map<String, String> properties,
+ List<SecurableObject> securableObjects)
+ throws RoleAlreadyExistsException, NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.createRole(metalake, role, properties,
securableObjects);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Role getRole(String metalake, String role)
+ throws NoSuchRoleException, NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.getRole(metalake, role);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean deleteRole(String metalake, String role) throws
NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.deleteRole(metalake, role);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String[] listRoleNames(String metalake) throws
NoSuchMetalakeException {
+ try {
+ // TODO: add Event
+ return dispatcher.listRoleNames(metalake);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String[] listRoleNamesByObject(String metalake, MetadataObject object)
+ throws NoSuchMetalakeException, NoSuchMetadataObjectException {
+ try {
+ // TODO: add Event
+ return dispatcher.listRoleNamesByObject(metalake, object);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Role grantPrivilegeToRole(
+ String metalake, String role, MetadataObject object, Set<Privilege>
privileges)
+ throws NoSuchGroupException, NoSuchRoleException {
+ try {
+ // TODO: add Event
+ return dispatcher.grantPrivilegeToRole(metalake, role, object,
privileges);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Role revokePrivilegesFromRole(
+ String metalake, String role, MetadataObject object, Set<Privilege>
privileges)
+ throws NoSuchMetalakeException, NoSuchRoleException {
+ try {
+ // TODO: add Event
+ return dispatcher.revokePrivilegesFromRole(metalake, role, object,
privileges);
+ } catch (Exception e) {
+ // TODO: add failure event
+ throw e;
+ }
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/AddUserPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/AddUserPreEvent.java
new file mode 100644
index 0000000000..91e1bd61a6
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/AddUserPreEvent.java
@@ -0,0 +1,61 @@
+/*
+ * 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 an event triggered before add a user to a metalake. */
+@DeveloperApi
+public class AddUserPreEvent extends UserPreEvent {
+ private final String userName;
+
+ /**
+ * Construct a new {@link AddUserPreEvent} instance.
+ *
+ * @param initiator the user who initiated the add-user request.
+ * @param identifier the identifier of the metalake which the user is being
added to.
+ * @param userName the username which is requested to be added to the
metalake.
+ */
+ public AddUserPreEvent(String initiator, NameIdentifier identifier, String
userName) {
+ super(initiator, identifier);
+
+ this.userName = userName;
+ }
+
+ /**
+ * Returns the user information which is being added to the metalake.
+ *
+ * @return the username which is requested to be added to the metalake.
+ */
+ public String userName() {
+ return userName;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return the operation type.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.ADD_USER;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/GetUserPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetUserPreEvent.java
new file mode 100644
index 0000000000..1c848fedc2
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetUserPreEvent.java
@@ -0,0 +1,61 @@
+/*
+ * 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 an event triggered before get a user from specific metalake */
+@DeveloperApi
+public class GetUserPreEvent extends UserPreEvent {
+ private final String userName;
+
+ /**
+ * Construct a new {@link GetUserPreEvent} instance with the specified user,
identifier and user
+ * info.
+ *
+ * @param initiator the user who initiated the add-user request.
+ * @param identifier the identifier of the metalake which the user is being
added to.
+ * @param userName the username which is requested to be retrieved.
+ */
+ public GetUserPreEvent(String initiator, NameIdentifier identifier, String
userName) {
+ super(initiator, identifier);
+ this.userName = userName;
+ }
+
+ /**
+ * Returns the user info for the user which is getting retrieved.
+ *
+ * @return the username which is requested to be retrieved.
+ */
+ public String userName() {
+ return userName;
+ }
+
+ /**
+ * Returns the operation type for this event.
+ *
+ * @return the operation type for this event.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.GET_USER;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/ListUserNamesPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListUserNamesPreEvent.java
new file mode 100644
index 0000000000..d5596d06d7
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListUserNamesPreEvent.java
@@ -0,0 +1,48 @@
+/*
+ * 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 an event triggered before list users name from specific
metalake */
+@DeveloperApi
+public class ListUserNamesPreEvent extends UserPreEvent {
+
+ /**
+ * Construct a new {@link ListUserNamesPreEvent} instance with the specified
user and identifier.
+ *
+ * @param initiator the user who initiated the list-user request.
+ * @param identifier the identifier of the metalake which is being listed.
+ */
+ protected ListUserNamesPreEvent(String initiator, NameIdentifier identifier)
{
+ super(initiator, identifier);
+ }
+
+ /**
+ * Returns the operation type for this event.
+ *
+ * @return the operation type for this event.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.LIST_USER_NAMES;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/ListUsersPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListUsersPreEvent.java
new file mode 100644
index 0000000000..975d37d77b
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListUsersPreEvent.java
@@ -0,0 +1,48 @@
+/*
+ * 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 an event triggered before list user from specific metalake */
+@DeveloperApi
+public class ListUsersPreEvent extends UserPreEvent {
+
+ /**
+ * Construct a new {@link ListUsersPreEvent} instance with the specified
user and identifier.
+ *
+ * @param initiator the user who initiated the list-user request.
+ * @param identifier the identifier of the metalake which is being listed.
+ */
+ protected ListUsersPreEvent(String initiator, NameIdentifier identifier) {
+ super(initiator, identifier);
+ }
+
+ /**
+ * Returns the operation type for this event.
+ *
+ * @return the operation type for this event.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.LIST_USERS;
+ }
+}
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 9ad171f10f..8477cd3e8f 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
@@ -112,5 +112,16 @@ public enum OperationType {
LIST_MODEL_VERSIONS,
REGISTER_AND_LINK_MODEL_VERSION,
+ // User
+ ADD_USER,
+ REMOVE_USER,
+ GET_USER,
+ LIST_USERS,
+ LIST_USER_NAMES,
+
+ // TODO GROUP
+
+ // TODO ROLE
+
UNKNOWN,
}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/RemoveUserPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/RemoveUserPreEvent.java
new file mode 100644
index 0000000000..3c39c5a9aa
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/RemoveUserPreEvent.java
@@ -0,0 +1,61 @@
+/*
+ * 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 an event triggered before remove a user from specific metalake.
*/
+@DeveloperApi
+public class RemoveUserPreEvent extends UserPreEvent {
+ private final String userName;
+
+ /**
+ * Construct a new {@link RemoveUserPreEvent} instance with the specified
user and identifier.
+ *
+ * @param initiator the user who initiated the remove user operation.
+ * @param identifier the identifier of the metalake where the user is
removed.
+ * @param userName the username which is requested to be removed from the
metalake.
+ */
+ protected RemoveUserPreEvent(String initiator, NameIdentifier identifier,
String userName) {
+ super(initiator, identifier);
+
+ this.userName = userName;
+ }
+
+ /**
+ * Returns the user information of the user which is to be removed from the
metalake.
+ *
+ * @return the username which is requested to be removed from the metalake.
+ */
+ public String userName() {
+ return userName;
+ }
+
+ /**
+ * Returns the operation type of this event.
+ *
+ * @return The operation type of this event.
+ */
+ @Override
+ public OperationType operationType() {
+ return OperationType.REMOVE_USER;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/UserPreEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/UserPreEvent.java
new file mode 100644
index 0000000000..0316226ab0
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/UserPreEvent.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/** Represent a pre-event for an user operation request. */
+@DeveloperApi
+public abstract class UserPreEvent extends PreEvent {
+
+ /**
+ * Create a new {@link UserPreEvent} instance for an access request.
+ *
+ * @param initiator the user who triggered the event.
+ * @param identifier the identifier of the model being operated on.
+ */
+ protected UserPreEvent(String initiator, NameIdentifier identifier) {
+ super(initiator, identifier);
+ }
+}
diff --git
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestUserEvent.java
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestUserEvent.java
new file mode 100644
index 0000000000..5acf13e1f9
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestUserEvent.java
@@ -0,0 +1,187 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.authorization.User;
+import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.exceptions.NoSuchUserException;
+import org.apache.gravitino.listener.DummyEventListener;
+import org.apache.gravitino.listener.EventBus;
+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 TestUserEvent {
+ private static final String METALAKE = "demo_metalake";
+ private AccessControlEventDispatcher dispatcher;
+ private AccessControlEventDispatcher failureDispatcher;
+ private DummyEventListener dummyEventListener;
+ private String userName;
+ private String otherUserName;
+ private String inExistUserName;
+ private User user;
+ private User otherUser;
+ private NameIdentifier identifier;
+ private NameIdentifier otherIdentifier;
+
+ @BeforeAll
+ void init() {
+ this.userName = "user_test";
+ this.otherUserName = "user_admin";
+ this.inExistUserName = "user_not_exist";
+ this.user = getMockUser(userName, ImmutableList.of("test", "engineer"));
+ this.otherUser = getMockUser(otherUserName, null);
+ this.identifier = NameIdentifier.of(METALAKE);
+ this.otherIdentifier = NameIdentifier.of(otherUserName);
+
+ this.dummyEventListener = new DummyEventListener();
+ EventBus eventBus = new
EventBus(Collections.singletonList(dummyEventListener));
+ this.dispatcher = new AccessControlEventDispatcher(eventBus,
mockUserDispatcher());
+ this.failureDispatcher =
+ new AccessControlEventDispatcher(eventBus,
mockExceptionUserDispatcher());
+
+ System.out.println(dispatcher);
+ System.out.println(failureDispatcher);
+ System.out.println(otherIdentifier);
+ }
+
+ @Test
+ void testAddUserPreEvent() {
+ dispatcher.addUser(METALAKE, otherUserName);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(AddUserPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.ADD_USER, preEvent.operationType());
+
+ AddUserPreEvent addUserPreEvent = (AddUserPreEvent) preEvent;
+ Assertions.assertEquals(identifier, addUserPreEvent.identifier());
+ String userName = addUserPreEvent.userName();
+ Assertions.assertEquals(otherUserName, userName);
+ }
+
+ @Test
+ void testGetUserPreEventWithExistingUser() {
+ dispatcher.getUser(METALAKE, userName);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(GetUserPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.GET_USER, preEvent.operationType());
+
+ GetUserPreEvent getUserPreEvent = (GetUserPreEvent) preEvent;
+ Assertions.assertEquals(identifier, getUserPreEvent.identifier());
+ String requestedUserName = getUserPreEvent.userName();
+ Assertions.assertEquals(userName, requestedUserName);
+ }
+
+ @Test
+ void testGetUserPreEventWithNonExistingUser() {
+ Assertions.assertThrows(
+ NoSuchUserException.class, () -> dispatcher.getUser(METALAKE,
inExistUserName));
+ }
+
+ @Test
+ void testListUserPreEvent() {
+ dispatcher.listUsers(METALAKE);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(ListUsersPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+
+ ListUsersPreEvent listUsersPreEvent = (ListUsersPreEvent) preEvent;
+ Assertions.assertEquals(identifier, listUsersPreEvent.identifier());
+ }
+
+ @Test
+ void testListUserNamesPreEvent() {
+ dispatcher.listUserNames(METALAKE);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(ListUserNamesPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+
+ ListUserNamesPreEvent listUserNamesPreEvent = (ListUserNamesPreEvent)
preEvent;
+ Assertions.assertEquals(identifier, listUserNamesPreEvent.identifier());
+ }
+
+ @Test
+ void testRemoveUserPreEventWithExistingUser() {
+ dispatcher.removeUser(METALAKE, userName);
+
+ // validate pre-event
+ PreEvent preEvent = dummyEventListener.popPreEvent();
+ Assertions.assertEquals(RemoveUserPreEvent.class, preEvent.getClass());
+ Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
+ Assertions.assertEquals(OperationType.REMOVE_USER,
preEvent.operationType());
+
+ RemoveUserPreEvent removeUserPreEvent = (RemoveUserPreEvent) preEvent;
+ Assertions.assertEquals(identifier, removeUserPreEvent.identifier());
+ String removedUserName = removeUserPreEvent.userName();
+ Assertions.assertEquals(userName, removedUserName);
+ }
+
+ private AccessControlEventDispatcher mockUserDispatcher() {
+ AccessControlEventDispatcher dispatcher =
mock(AccessControlEventDispatcher.class);
+ when(dispatcher.addUser(METALAKE, userName)).thenReturn(user);
+ when(dispatcher.addUser(METALAKE, otherUserName)).thenReturn(otherUser);
+
+ when(dispatcher.removeUser(METALAKE, userName)).thenReturn(true);
+ when(dispatcher.removeUser(METALAKE, inExistUserName)).thenReturn(false);
+
+ when(dispatcher.listUsers(METALAKE)).thenReturn(new User[] {user,
otherUser});
+ when(dispatcher.listUserNames(METALAKE)).thenReturn(new String[]
{userName, otherUserName});
+
+ when(dispatcher.getUser(METALAKE, userName)).thenReturn(user);
+ when(dispatcher.getUser(METALAKE, inExistUserName))
+ .thenThrow(new NoSuchUserException("user not found"));
+
+ return dispatcher;
+ }
+
+ private AccessControlEventDispatcher mockExceptionUserDispatcher() {
+ return mock(
+ AccessControlEventDispatcher.class,
+ invocation -> {
+ throw new GravitinoRuntimeException("Exception for all methods");
+ });
+ }
+
+ 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;
+ }
+}
diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md
index 08c9beaec4..afcce0c42c 100644
--- a/docs/gravitino-server-config.md
+++ b/docs/gravitino-server-config.md
@@ -132,17 +132,18 @@ Gravitino triggers a pre-event before the operation, a
post-event after the comp
##### Pre-event
-| Operation type | Pre-event
| Since Version |
-|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|
-| Iceberg REST server table operation | `IcebergCreateTablePreEvent`,
`IcebergUpdateTablePreEvent`, `IcebergDropTablePreEvent`,
`IcebergLoadTablePreEvent`, `IcebergListTablePreEvent`,
`IcebergTableExistsPreEvent`, `IcebergRenameTablePreEvent`
| 0.7.0-incubating |
-| Gravitino server table operation | `CreateTablePreEvent`,
`AlterTablePreEvent`, `DropTablePreEvent`, `PurgeTablePreEvent`,
`LoadTablePreEvent`, `ListTablePreEvent`
|
0.8.0-incubating |
-| Gravitino server schema operation | `CreateSchemaPreEvent`,
`AlterSchemaPreEvent`, `DropSchemaPreEvent`, `LoadSchemaPreEvent`,
`ListSchemaPreEvent`
|
0.8.0-incubating |
-| Gravitino server catalog operation | `CreateCatalogPreEvent`,
`AlterCatalogPreEvent`, `DropCatalogPreEvent`, `LoadCatalogPreEvent`,
`ListCatalogPreEvent`
| 0.8.0-incubating |
-| Gravitino server metalake operation | `CreateMetalakePreEvent`,
`AlterMetalakePreEvent`,`DropMetalakePreEvent`,`LoadMetalakePreEvent`,`ListMetalakePreEvent`
| 0.8.0-incubating |
-| Gravitino server partition operation | `AddPartitionPreEvent`,
`DropPartitionPreEvent`, `GetPartitionPreEvent`,
`PurgePartitionPreEvent`,`ListPartitionPreEvent`,`ListPartitionNamesPreEvent`
| 0.8.0-incubating |
-| Gravitino server fileset operation | `CreateFilesetPreEvent`,
`AlterFilesetPreEvent`, `DropFilesetPreEvent`,
`LoadFilesetPreEvent`,`ListFilesetPreEvent`,`GetFileLocationPreEvent`
| 0.8.0-incubating |
-| Gravitino server model operation | `DeleteModelPreEvent`,
`DeleteModelVersionPreEvent`,
`RegisterAndLinkModelPreEvent`,`GetModelPreEvent`,
`GetModelVersionPreEvent`,`LinkModelVersionPreEvent`,`ListModelPreEvent`,`RegisterModelPreEvent`
|
0.9.0-incubating |
+| Operation type | Pre-event
| Since Version |
+|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|
+| Iceberg REST server table operation | `IcebergCreateTablePreEvent`,
`IcebergUpdateTablePreEvent`, `IcebergDropTablePreEvent`,
`IcebergLoadTablePreEvent`, `IcebergListTablePreEvent`,
`IcebergTableExistsPreEvent`, `IcebergRenameTablePreEvent`
| 0.7.0-incubating |
+| Gravitino server table operation | `CreateTablePreEvent`,
`AlterTablePreEvent`, `DropTablePreEvent`, `PurgeTablePreEvent`,
`LoadTablePreEvent`, `ListTablePreEvent`
|
0.8.0-incubating |
+| Gravitino server schema operation | `CreateSchemaPreEvent`,
`AlterSchemaPreEvent`, `DropSchemaPreEvent`, `LoadSchemaPreEvent`,
`ListSchemaPreEvent`
| 0.8.0-incubating
|
+| Gravitino server catalog operation | `CreateCatalogPreEvent`,
`AlterCatalogPreEvent`, `DropCatalogPreEvent`, `LoadCatalogPreEvent`,
`ListCatalogPreEvent`
| 0.8.0-incubating |
+| Gravitino server metalake operation | `CreateMetalakePreEvent`,
`AlterMetalakePreEvent`,`DropMetalakePreEvent`,`LoadMetalakePreEvent`,`ListMetalakePreEvent`
| 0.8.0-incubating |
+| Gravitino server partition operation | `AddPartitionPreEvent`,
`DropPartitionPreEvent`, `GetPartitionPreEvent`,
`PurgePartitionPreEvent`,`ListPartitionPreEvent`,`ListPartitionNamesPreEvent`
| 0.8.0-incubating |
+| Gravitino server fileset operation | `CreateFilesetPreEvent`,
`AlterFilesetPreEvent`, `DropFilesetPreEvent`,
`LoadFilesetPreEvent`,`ListFilesetPreEvent`,`GetFileLocationPreEvent`
| 0.8.0-incubating |
+| Gravitino server model operation | `DeleteModelPreEvent`,
`DeleteModelVersionPreEvent`,
`RegisterAndLinkModelPreEvent`,`GetModelPreEvent`,
`GetModelVersionPreEvent`,`LinkModelVersionPreEvent`,`ListModelPreEvent`,`RegisterModelPreEvent`
| 0.9.0-incubating
|
| 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`, `ListUserPreEvent`, `RemoveUserPreEvent` |
0.9.0-incubating |
#### Event listener plugin