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 914e69b08e [#6759] test(core): Support role event to Gravitino server (#6851) 914e69b08e is described below commit 914e69b08e03509e6c8904189b6a103bd7ac55db Author: Lord of Abyss <103809695+abyss-l...@users.noreply.github.com> AuthorDate: Wed Apr 9 11:41:03 2025 +0800 [#6759] test(core): Support role event to Gravitino server (#6851) ### What changes were proposed in this pull request? Support role event to Gravitino server ### Why are the changes needed? Fix: #6759 ### Does this PR introduce _any_ user-facing change? user can listen to `RoleEvent`. ### How was this patch tested? local ut. --- .../api/event/AccessControlEventDispatcher.java | 47 ++++-- .../listener/api/event/CreateRoleEvent.java | 61 ++++++++ .../listener/api/event/DeleteRoleEvent.java | 73 +++++++++ .../gravitino/listener/api/event/GetRoleEvent.java | 62 ++++++++ ...egesPreEvent.java => GrantPrivilegesEvent.java} | 53 +++---- .../api/event/GrantPrivilegesPreEvent.java | 10 +- .../listener/api/event/ListRoleNamesEvent.java | 74 +++++++++ ...gesPreEvent.java => RevokePrivilegesEvent.java} | 48 +++--- .../api/event/RevokePrivilegesPreEvent.java | 10 +- .../gravitino/listener/api/event/RoleEvent.java | 50 ++++++ .../gravitino/listener/api/info/RoleInfo.java | 80 ++++++++++ .../listener/api/event/TestRoleEvent.java | 169 ++++++++++++++++++++- docs/gravitino-server-config.md | 25 +-- 13 files changed, 670 insertions(+), 92 deletions(-) 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 d6db8bfe89..cd5d1613f3 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 @@ -40,6 +40,7 @@ import org.apache.gravitino.exceptions.RoleAlreadyExistsException; import org.apache.gravitino.exceptions.UserAlreadyExistsException; import org.apache.gravitino.listener.EventBus; import org.apache.gravitino.listener.api.info.GroupInfo; +import org.apache.gravitino.listener.api.info.RoleInfo; import org.apache.gravitino.listener.api.info.UserInfo; import org.apache.gravitino.utils.PrincipalUtils; @@ -340,8 +341,10 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { eventBus.dispatchEvent( new CreateRolePreEvent(initiator, metalake, role, properties, securableObjects)); try { - // TODO: add Event - return dispatcher.createRole(metalake, role, properties, securableObjects); + Role roleObject = dispatcher.createRole(metalake, role, properties, securableObjects); + eventBus.dispatchEvent(new CreateRoleEvent(initiator, metalake, new RoleInfo(roleObject))); + + return roleObject; } catch (Exception e) { // TODO: add failure event throw e; @@ -356,8 +359,10 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { eventBus.dispatchEvent(new GetRolePreEvent(initiator, metalake, role)); try { - // TODO: add Event - return dispatcher.getRole(metalake, role); + Role roleObject = dispatcher.getRole(metalake, role); + eventBus.dispatchEvent(new GetRoleEvent(initiator, metalake, new RoleInfo(roleObject))); + + return roleObject; } catch (Exception e) { // TODO: add failure event throw e; @@ -371,8 +376,10 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { eventBus.dispatchEvent(new DeleteRolePreEvent(initiator, metalake, role)); try { - // TODO: add Event - return dispatcher.deleteRole(metalake, role); + boolean isExists = dispatcher.deleteRole(metalake, role); + eventBus.dispatchEvent(new DeleteRoleEvent(initiator, metalake, role, isExists)); + + return isExists; } catch (Exception e) { // TODO: add failure event throw e; @@ -386,8 +393,10 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { eventBus.dispatchEvent(new ListRoleNamesPreEvent(initiator, metalake)); try { - // TODO: add Event - return dispatcher.listRoleNames(metalake); + String[] roleNames = dispatcher.listRoleNames(metalake); + eventBus.dispatchEvent(new ListRoleNamesEvent(initiator, metalake)); + + return roleNames; } catch (Exception e) { // TODO: add failure event throw e; @@ -402,8 +411,10 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { eventBus.dispatchEvent(new ListRoleNamesPreEvent(initiator, metalake, object)); try { - // TODO: add Event - return dispatcher.listRoleNamesByObject(metalake, object); + String[] roleNames = dispatcher.listRoleNamesByObject(metalake, object); + eventBus.dispatchEvent(new ListRoleNamesEvent(initiator, metalake, object)); + + return roleNames; } catch (Exception e) { // TODO: add failure event throw e; @@ -420,8 +431,12 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { eventBus.dispatchEvent( new GrantPrivilegesPreEvent(initiator, metalake, role, object, privileges)); try { - // TODO: add Event - return dispatcher.grantPrivilegeToRole(metalake, role, object, privileges); + Role roleObject = dispatcher.grantPrivilegeToRole(metalake, role, object, privileges); + eventBus.dispatchEvent( + new GrantPrivilegesEvent( + initiator, metalake, new RoleInfo(roleObject), privileges, object)); + + return roleObject; } catch (Exception e) { // TODO: add failure event throw e; @@ -438,8 +453,12 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { eventBus.dispatchEvent( new RevokePrivilegesPreEvent(initiator, metalake, role, object, privileges)); try { - // TODO: add Event - return dispatcher.revokePrivilegesFromRole(metalake, role, object, privileges); + Role roleObject = dispatcher.revokePrivilegesFromRole(metalake, role, object, privileges); + eventBus.dispatchEvent( + new RevokePrivilegesEvent( + initiator, metalake, new RoleInfo(roleObject), object, privileges)); + + return roleObject; } catch (Exception e) { // TODO: add failure event throw e; diff --git a/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRoleEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRoleEvent.java new file mode 100644 index 0000000000..527e122f6a --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRoleEvent.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.annotation.DeveloperApi; +import org.apache.gravitino.listener.api.info.RoleInfo; +import org.apache.gravitino.utils.NameIdentifierUtil; + +/** Represents an event triggered after a role is successfully created. */ +@DeveloperApi +public class CreateRoleEvent extends RoleEvent { + private final RoleInfo createdRoleInfo; + + /** + * Constructs a new {@code CreateRoleEvent} instance. + * + * @param initiator the user who initiated the event. + * @param metalake the metalake name where the role was created. + * @param createdRoleInfo the information of the created role. + */ + public CreateRoleEvent(String initiator, String metalake, RoleInfo createdRoleInfo) { + super(initiator, NameIdentifierUtil.ofRole(metalake, createdRoleInfo.roleName())); + this.createdRoleInfo = createdRoleInfo; + } + + /** + * Returns the created role information. + * + * @return the {@code RoleInfo} instance containing details of the created role. + */ + public RoleInfo createdRoleInfo() { + return createdRoleInfo; + } + + /** + * 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/DeleteRoleEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteRoleEvent.java new file mode 100644 index 0000000000..839070c495 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteRoleEvent.java @@ -0,0 +1,73 @@ +/* + * 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 after a role is successfully deleted. */ +@DeveloperApi +public class DeleteRoleEvent extends RoleEvent { + private final String roleName; + private final boolean isExists; + + /** + * Constructs a new {@code DeleteRoleEvent} instance. + * + * @param initiator the user who initiated the event. + * @param metalake the metalake name where the role was deleted. + * @param roleName the name of the deleted role. + * @param isExists a flag indicating whether the role existed at the time of deletion. + */ + public DeleteRoleEvent(String initiator, String metalake, String roleName, boolean isExists) { + super(initiator, NameIdentifierUtil.ofRole(metalake, roleName)); + + this.roleName = roleName; + this.isExists = isExists; + } + + /** + * Returns a flag indicating whether the role existed at the time of deletion. + * + * @return {@code true} if the role existed; {@code false} otherwise. + */ + public boolean isExists() { + return isExists; + } + + /** + * Returns the name of the deleted role. + * + * @return the name of the deleted role. + */ + 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/GetRoleEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/GetRoleEvent.java new file mode 100644 index 0000000000..6a30400e6f --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/GetRoleEvent.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.listener.api.info.RoleInfo; +import org.apache.gravitino.utils.NameIdentifierUtil; + +/** Represents an event triggered after a role is successfully loaded. */ +@DeveloperApi +public class GetRoleEvent extends RoleEvent { + private final RoleInfo loadedRoleInfo; + + /** + * Constructs a new {@code GetRoleEvent} instance. + * + * @param initiator the user who initiated the event. + * @param metalake the metalake name from which the role was loaded. + * @param loadedRoleInfo the {@code RoleInfo} instance of the loaded role. + */ + public GetRoleEvent(String initiator, String metalake, RoleInfo loadedRoleInfo) { + super(initiator, NameIdentifierUtil.ofRole(metalake, loadedRoleInfo.roleName())); + + this.loadedRoleInfo = loadedRoleInfo; + } + + /** + * Returns the role information of the loaded role. + * + * @return the {@code RoleInfo} instance of the loaded role. + */ + public RoleInfo roleInfo() { + return loadedRoleInfo; + } + + /** + * 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/GrantPrivilegesEvent.java similarity index 55% copy from core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesPreEvent.java copy to core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesEvent.java index 3f33c8cce4..33ca0b66ea 100644 --- a/core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesPreEvent.java +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesEvent.java @@ -23,63 +23,60 @@ 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.listener.api.info.RoleInfo; 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. - */ +/** Represents an event triggered after privileges are granted to a role. */ @DeveloperApi -public class GrantPrivilegesPreEvent extends RolePreEvent { - private final String roleName; +public class GrantPrivilegesEvent extends RoleEvent { + private final RoleInfo grantedRoleInfo; 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. + * Constructs a new {@code GrantPrivilegesEvent} instance. * - * @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. + * @param initiator the user who initiated the event. + * @param metalake the metalake name where the event occurred. + * @param grantedRoleInfo the {@code RoleInfo} of the role that was granted privileges. + * @param privileges the set of privileges granted to the role. + * @param object the {@code MetadataObject} associated with the role. */ - public GrantPrivilegesPreEvent( + public GrantPrivilegesEvent( String initiator, String metalake, - String roleName, - MetadataObject object, - Set<Privilege> privileges) { - super(initiator, NameIdentifierUtil.ofRole(metalake, roleName)); + RoleInfo grantedRoleInfo, + Set<Privilege> privileges, + MetadataObject object) { + super(initiator, NameIdentifierUtil.ofRole(metalake, grantedRoleInfo.roleName())); - this.roleName = roleName; - this.object = object; + this.grantedRoleInfo = grantedRoleInfo; this.privileges = privileges; + this.object = object; } /** - * Returns the name of the role. + * Returns the role information of the role that was granted privileges. * - * @return The name of the role to which privileges will be granted. + * @return the {@code RoleInfo} instance containing details of the granted role. */ - public String roleName() { - return roleName; + public RoleInfo grantedRoleInfo() { + return grantedRoleInfo; } /** - * Returns the {@link MetadataObject} instance. + * Returns the metadata object associated with the role. * - * @return The {@link MetadataObject} instance related to the role. + * @return the {@code MetadataObject} instance. */ public MetadataObject object() { return object; } /** - * Returns the set of privileges to grant. + * Returns the set of privileges granted to the role. * - * @return The set of privileges to grant to the role. + * @return a set of {@code Privilege} instances. */ public Set<Privilege> privileges() { return privileges; 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 index 3f33c8cce4..262bd80eaf 100644 --- 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 @@ -32,7 +32,7 @@ import org.apache.gravitino.utils.NameIdentifierUtil; @DeveloperApi public class GrantPrivilegesPreEvent extends RolePreEvent { private final String roleName; - private final MetadataObject object; + private final MetadataObject metadataObject; private final Set<Privilege> privileges; /** @@ -42,19 +42,19 @@ public class GrantPrivilegesPreEvent extends RolePreEvent { * @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 metadataObject 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, + MetadataObject metadataObject, Set<Privilege> privileges) { super(initiator, NameIdentifierUtil.ofRole(metalake, roleName)); this.roleName = roleName; - this.object = object; + this.metadataObject = metadataObject; this.privileges = privileges; } @@ -73,7 +73,7 @@ public class GrantPrivilegesPreEvent extends RolePreEvent { * @return The {@link MetadataObject} instance related to the role. */ public MetadataObject object() { - return object; + return metadataObject; } /** diff --git a/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesEvent.java new file mode 100644 index 0000000000..9b59c70fe0 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesEvent.java @@ -0,0 +1,74 @@ +/* + * 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 triggered after role names are listed. */ +@DeveloperApi +public class ListRoleNamesEvent extends RoleEvent { + private final Optional<MetadataObject> object; + + /** + * Constructs a new {@code ListRoleNamesEvent} instance without a related metadata object. + * + * @param initiator the user who triggered the event. + * @param metalake the metalake name where the roles are listed. + */ + public ListRoleNamesEvent(String initiator, String metalake) { + this(initiator, metalake, null); + } + + /** + * Constructs a new {@code ListRoleNamesEvent} instance with the specified metadata object. + * + * @param initiator the user who triggered the event. + * @param metalake the metalake name where the roles are listed. + * @param object the {@code MetadataObject} related to the role names. + */ + protected ListRoleNamesEvent(String initiator, String metalake, MetadataObject object) { + super(initiator, NameIdentifierUtil.ofMetalake(metalake)); + + this.object = Optional.ofNullable(object); + } + + /** + * Returns the {@code MetadataObject} related to the role names. + * + * @return an {@code Optional} containing the {@code MetadataObject} if present, otherwise an + * empty {@code Optional}. + */ + 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/RevokePrivilegesPreEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesEvent.java similarity index 57% copy from core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesPreEvent.java copy to core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesEvent.java index 260eb374f0..e7b37b559f 100644 --- a/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesPreEvent.java +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesEvent.java @@ -23,62 +23,60 @@ 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.listener.api.info.RoleInfo; 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. - */ +/** Represents an event triggered after privileges are revoked from a role. */ @DeveloperApi -public class RevokePrivilegesPreEvent extends RolePreEvent { - private final String roleName; +public class RevokePrivilegesEvent extends RoleEvent { + private final RoleInfo revokedRoleInfo; 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. + * Constructs a new {@code RevokePrivilegesEvent} instance. * - * @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. + * @param initiator the user who initiated the event. + * @param metalake the metalake name where the event occurred. + * @param revokedRoleInfo the {@code RoleInfo} of the role from which privileges were revoked. + * @param object the {@code MetadataObject} associated with the role. + * @param privileges the set of privileges that were revoked. */ - public RevokePrivilegesPreEvent( + public RevokePrivilegesEvent( String initiator, String metalake, - String roleName, + RoleInfo revokedRoleInfo, MetadataObject object, Set<Privilege> privileges) { - super(initiator, NameIdentifierUtil.ofRole(metalake, roleName)); - this.roleName = roleName; + super(initiator, NameIdentifierUtil.ofRole(metalake, revokedRoleInfo.roleName())); + + this.revokedRoleInfo = revokedRoleInfo; this.object = object; this.privileges = privileges; } /** - * Returns the name of the role. + * Returns the role information of the role from which privileges were revoked. * - * @return The name of the role from which privileges will be revoked. + * @return the {@code RoleInfo} instance. */ - public String roleName() { - return roleName; + public RoleInfo revokedRoleInfo() { + return revokedRoleInfo; } /** - * Returns the {@link MetadataObject} instance related to the role. + * Returns the metadata object associated with the role. * - * @return The {@link MetadataObject} instance associated with the role. + * @return the {@code MetadataObject} instance. */ public MetadataObject object() { return object; } /** - * Returns the set of privileges to revoke. + * Returns the set of privileges that were revoked. * - * @return The set of privileges to revoke from the role. + * @return a set of {@code Privilege} instances. */ public Set<Privilege> privileges() { return privileges; 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 index 260eb374f0..adce8ef5ab 100644 --- 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 @@ -32,7 +32,7 @@ import org.apache.gravitino.utils.NameIdentifierUtil; @DeveloperApi public class RevokePrivilegesPreEvent extends RolePreEvent { private final String roleName; - private final MetadataObject object; + private final MetadataObject metadataObject; private final Set<Privilege> privileges; /** @@ -42,18 +42,18 @@ public class RevokePrivilegesPreEvent extends RolePreEvent { * @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 metadataObject 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, + MetadataObject metadataObject, Set<Privilege> privileges) { super(initiator, NameIdentifierUtil.ofRole(metalake, roleName)); this.roleName = roleName; - this.object = object; + this.metadataObject = metadataObject; this.privileges = privileges; } @@ -72,7 +72,7 @@ public class RevokePrivilegesPreEvent extends RolePreEvent { * @return The {@link MetadataObject} instance associated with the role. */ public MetadataObject object() { - return object; + return metadataObject; } /** diff --git a/core/src/main/java/org/apache/gravitino/listener/api/event/RoleEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/RoleEvent.java new file mode 100644 index 0000000000..bdea7e5f74 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/RoleEvent.java @@ -0,0 +1,50 @@ +/* + * 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 abstract base class for events related to role operations. This class extends + * {@link Event} to provide a specific context for operations performed by roles. It captures + * essential information including the user performing the operation and the identifier of the role. + * + * <p>Concrete implementations of this class should provide additional details relevant to the + * specific type of role operation. + */ +@DeveloperApi +public abstract class RoleEvent extends Event { + /** + * Constructs a new {@code RoleEvent} instance with the given initiator and identifier. + * + * @param initiator the user who triggered the event. + * @param identifier the identifier of the role affected by the event. + */ + protected RoleEvent(String initiator, NameIdentifier identifier) { + super(initiator, identifier); + } + + /** {@inheritDoc} */ + @Override + public OperationStatus operationStatus() { + return OperationStatus.SUCCESS; + } +} diff --git a/core/src/main/java/org/apache/gravitino/listener/api/info/RoleInfo.java b/core/src/main/java/org/apache/gravitino/listener/api/info/RoleInfo.java new file mode 100644 index 0000000000..c89fd5233d --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/info/RoleInfo.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.info; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.util.List; +import java.util.Map; +import org.apache.gravitino.annotation.DeveloperApi; +import org.apache.gravitino.authorization.Role; +import org.apache.gravitino.authorization.SecurableObject; + +/** Provides read-only access to role information for event listeners. */ +@DeveloperApi +public class RoleInfo { + private final String roleName; + private final Map<String, String> properties; + private final List<SecurableObject> securableObjects; + + /** + * Constructs a new {@code RoleInfo} instance using the given {@code Role} object. + * + * @param roleObject the {@code Role} object providing role information. + */ + public RoleInfo(Role roleObject) { + this.roleName = roleObject.name(); + this.properties = + roleObject.properties() == null + ? ImmutableMap.of() + : ImmutableMap.copyOf(roleObject.properties()); + this.securableObjects = + roleObject.securableObjects() == null + ? ImmutableList.of() + : ImmutableList.copyOf(roleObject.securableObjects()); + } + + /** + * Returns the role name. + * + * @return the role name. + */ + public String roleName() { + return roleName; + } + + /** + * Returns the properties associated with the role. + * + * @return a map containing the role's properties. + */ + public 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. + */ + public List<SecurableObject> securableObjects() { + return securableObjects; + } +} 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 index e4220e037a..c4d63ffbfa 100644 --- 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 @@ -42,6 +42,7 @@ 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.listener.api.info.RoleInfo; import org.apache.gravitino.utils.NameIdentifierUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -92,6 +93,16 @@ public class TestRoleEvent { System.out.println(failureDispatcher); } + @Test + void testRoleInfo() { + role = getMockRole(roleName, properties, securableObjects); + RoleInfo roleInfo = new RoleInfo(role); + + Assertions.assertEquals(roleName, roleInfo.roleName()); + Assertions.assertEquals(properties, roleInfo.properties()); + Assertions.assertEquals(securableObjects, roleInfo.securableObjects()); + } + @Test void testCreateRole() { dispatcher.createRole( @@ -111,6 +122,26 @@ public class TestRoleEvent { Assertions.assertEquals(securableObjects, createRolePreEvent.securableObjects()); } + @Test + void testCreateRoleEvent() { + dispatcher.createRole( + METALAKE, roleName, ImmutableMap.of("comment", "test comment"), securableObjects); + Role roleObject = getMockRole(roleName, properties, securableObjects); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(CreateRoleEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.CREATE_ROLE, event.operationType()); + + CreateRoleEvent createRoleEvent = (CreateRoleEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), createRoleEvent.identifier()); + RoleInfo roleInfo = createRoleEvent.createdRoleInfo(); + + validateRoleInfo(roleInfo, roleObject); + } + @Test void testDeleteRolePreEvent() { dispatcher.deleteRole(METALAKE, roleName); @@ -127,6 +158,39 @@ public class TestRoleEvent { Assertions.assertEquals(roleName, deleteRolePreEvent.roleName()); } + @Test + void testDeleteRoleEventWithExistIdentifier() { + dispatcher.deleteRole(METALAKE, roleName); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(DeleteRoleEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.DELETE_ROLE, event.operationType()); + + DeleteRoleEvent deleteRoleEvent = (DeleteRoleEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), deleteRoleEvent.identifier()); + Assertions.assertEquals(roleName, deleteRoleEvent.roleName()); + Assertions.assertTrue(deleteRoleEvent.isExists()); + } + + @Test + void testDeleteRoleEventWithNotExistIdentifier() { + dispatcher.deleteRole(METALAKE, otherRoleName); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(DeleteRoleEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.DELETE_ROLE, event.operationType()); + + DeleteRoleEvent deleteRoleEvent = (DeleteRoleEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, otherRoleName), deleteRoleEvent.identifier()); + Assertions.assertEquals(otherRoleName, deleteRoleEvent.roleName()); + } + @Test void testGetRolePreEvent() { dispatcher.getRole(METALAKE, roleName); @@ -144,7 +208,25 @@ public class TestRoleEvent { } @Test - void testListRolesEvent() { + void testGetRoleEvent() { + dispatcher.getRole(METALAKE, roleName); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(GetRoleEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.GET_ROLE, event.operationType()); + + GetRoleEvent getRoleEvent = (GetRoleEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), getRoleEvent.identifier()); + RoleInfo roleInfo = getRoleEvent.roleInfo(); + + validateRoleInfo(roleInfo, roleName, properties, securableObjects); + } + + @Test + void testListRolesPreEvent() { dispatcher.listRoleNames(METALAKE); // validate pre-event @@ -159,7 +241,22 @@ public class TestRoleEvent { } @Test - void testListRolesFromObject() { + void testListRolesEvent() { + dispatcher.listRoleNames(METALAKE); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(ListRoleNamesEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.LIST_ROLE_NAMES, event.operationType()); + + ListRoleNamesEvent listRoleNamesEvent = (ListRoleNamesEvent) event; + Assertions.assertEquals(identifier, listRoleNamesEvent.identifier()); + Assertions.assertFalse(listRoleNamesEvent.object().isPresent()); + } + + @Test + void testListRolesFromObjectPreEvent() { dispatcher.listRoleNamesByObject(METALAKE, securableObjects.get(0)); // validate pre-event @@ -174,6 +271,22 @@ public class TestRoleEvent { Assertions.assertEquals(securableObjects.get(0), listRoleNamesPreEvent.object().get()); } + @Test + void testListRoleFromObjectEvent() { + dispatcher.listRoleNamesByObject(METALAKE, securableObjects.get(0)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(ListRoleNamesEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.LIST_ROLE_NAMES, event.operationType()); + + ListRoleNamesEvent listRoleNamesEvent = (ListRoleNamesEvent) event; + Assertions.assertEquals(identifier, listRoleNamesEvent.identifier()); + Assertions.assertTrue(listRoleNamesEvent.object().isPresent()); + Assertions.assertEquals(securableObjects.get(0), listRoleNamesEvent.object().get()); + } + @Test void testGrantPrivilegesToRole() { dispatcher.grantPrivilegeToRole(METALAKE, roleName, metadataObject, privileges); @@ -193,7 +306,24 @@ public class TestRoleEvent { } @Test - void testRevokePrivilegesFromRole() { + void testGrantPrivilegesToRoleEvent() { + dispatcher.grantPrivilegeToRole(METALAKE, roleName, metadataObject, privileges); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(GrantPrivilegesEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.GRANT_PRIVILEGES, event.operationType()); + + GrantPrivilegesEvent grantPrivilegesEvent = (GrantPrivilegesEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), grantPrivilegesEvent.identifier()); + RoleInfo roleInfo = grantPrivilegesEvent.grantedRoleInfo(); + validateRoleInfo(roleInfo, roleName, properties, securableObjects); + } + + @Test + void testRevokePrivilegesFromRolePreEvent() { dispatcher.revokePrivilegesFromRole(METALAKE, roleName, metadataObject, privileges); // validate pre-event @@ -210,6 +340,23 @@ public class TestRoleEvent { Assertions.assertEquals(privileges, revokePrivilegesPreEvent.privileges()); } + @Test + void testRevokePrivilegesFromRoleEvent() { + dispatcher.revokePrivilegesFromRole(METALAKE, roleName, metadataObject, privileges); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(RevokePrivilegesEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus()); + Assertions.assertEquals(OperationType.REVOKE_PRIVILEGES, event.operationType()); + + RevokePrivilegesEvent revokePrivilegesEvent = (RevokePrivilegesEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), revokePrivilegesEvent.identifier()); + RoleInfo roleInfo = revokePrivilegesEvent.revokedRoleInfo(); + validateRoleInfo(roleInfo, roleName, properties, securableObjects); + } + private AccessControlEventDispatcher mockRoleDispatcher() { AccessControlEventDispatcher dispatcher = mock(AccessControlEventDispatcher.class); Group mockGroup = getMockGroup(groupName, ImmutableList.of(roleName, otherRoleName)); @@ -288,4 +435,20 @@ public class TestRoleEvent { return mockMetadataObject; } + + private void validateRoleInfo(RoleInfo roleinfo, Role roleObject) { + Assertions.assertEquals(roleObject.name(), roleinfo.roleName()); + Assertions.assertEquals(roleObject.properties(), roleinfo.properties()); + Assertions.assertEquals(roleObject.securableObjects(), roleinfo.securableObjects()); + } + + private void validateRoleInfo( + RoleInfo roleinfo, + String roleName, + Map<String, String> properties, + List<SecurableObject> securableObjects) { + Assertions.assertEquals(roleName, roleinfo.roleName()); + Assertions.assertEquals(properties, roleinfo.properties()); + Assertions.assertEquals(securableObjects, roleinfo.securableObjects()); + } } diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md index db0d5f3132..de56789997 100644 --- a/docs/gravitino-server-config.md +++ b/docs/gravitino-server-config.md @@ -118,19 +118,20 @@ Gravitino triggers a pre-event before the operation, a post-event after the comp ##### Post-event -| Operation type | Post-event [...] -|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [...] -| table operation | `CreateTableEvent`, `AlterTableEvent`, `DropTableEvent`, `LoadTableEvent`, `ListTableEvent`, `PurgeTableFailureEvent`, `CreateTableFailureEvent`, `AlterTableFailureEvent`, `DropTableFailureEvent`, `LoadTableFailureEvent`, `ListTableFailureEvent`, `PurgeTableFailureEvent` [...] -| fileset operation | `CreateFileSetEvent`, `AlterFileSetEvent`, `DropFileSetEvent`, `LoadFileSetEvent`, `ListFileSetEvent`, `CreateFileSetFailureEvent`, `AlterFileSetFailureEvent`, `DropFileSetFailureEvent`, `LoadFileSetFailureEvent`, `ListFileSetFailureEvent` [...] -| topic operation | `CreateTopicEvent`, `AlterTopicEvent`, `DropTopicEvent`, `LoadTopicEvent`, `ListTopicEvent`, `CreateTopicFailureEvent`, `AlterTopicFailureEvent`, `DropTopicFailureEvent`, `LoadTopicFailureEvent`, `ListTopicFailureEvent` [...] -| schema operation | `CreateSchemaEvent`, `AlterSchemaEvent`, `DropSchemaEvent`, `LoadSchemaEvent`, `ListSchemaEvent`, `CreateSchemaFailureEvent`, `AlterSchemaFailureEvent`, `DropSchemaFailureEvent`, `LoadSchemaFailureEvent`, `ListSchemaFailureEvent` [...] -| catalog operation | `CreateCatalogEvent`, `AlterCatalogEvent`, `DropCatalogEvent`, `LoadCatalogEvent`, `ListCatalogEvent`, `CreateCatalogFailureEvent`, `AlterCatalogFailureEvent`, `DropCatalogFailureEvent`, `LoadCatalogFailureEvent`, `ListCatalogFailureEvent` [...] -| metalake operation | `CreateMetalakeEvent`, `AlterMetalakeEvent`, `DropMetalakeEvent`, `LoadMetalakeEvent`, `ListMetalakeEvent`, `CreateMetalakeFailureEvent`, `AlterMetalakeFailureEvent`, `DropMetalakeFailureEvent`, `LoadMetalakeFailureEvent`, `ListMetalakeFailureEvent` [...] -| Iceberg REST server table operation | `IcebergCreateTableEvent`, `IcebergUpdateTableEvent`, `IcebergDropTableEvent`, `IcebergLoadTableEvent`, `IcebergListTableEvent`, `IcebergTableExistsEvent`, `IcebergRenameTableEvent`, `IcebergCreateTableFailureEvent`, `IcebergUpdateTableFailureEvent`, `IcebergDropTableFailureEvent`, `IcebergLoadTableFailureEvent`, `IcebergListTableFailureEvent`, `IcebergRenameTableFailureEvent`, `IcebergTableExistsFailureEvent` [...] +| Operation type | Post-event [...] +|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [...] +| table operation | `CreateTableEvent`, `AlterTableEvent`, `DropTableEvent`, `LoadTableEvent`, `ListTableEvent`, `PurgeTableFailureEvent`, `CreateTableFailureEvent`, `AlterTableFailureEvent`, `DropTableFailureEvent`, `LoadTableFailureEvent`, `ListTableFailureEvent`, `PurgeTableFailureEvent` [...] +| fileset operation | `CreateFileSetEvent`, `AlterFileSetEvent`, `DropFileSetEvent`, `LoadFileSetEvent`, `ListFileSetEvent`, `CreateFileSetFailureEvent`, `AlterFileSetFailureEvent`, `DropFileSetFailureEvent`, `LoadFileSetFailureEvent`, `ListFileSetFailureEvent` [...] +| topic operation | `CreateTopicEvent`, `AlterTopicEvent`, `DropTopicEvent`, `LoadTopicEvent`, `ListTopicEvent`, `CreateTopicFailureEvent`, `AlterTopicFailureEvent`, `DropTopicFailureEvent`, `LoadTopicFailureEvent`, `ListTopicFailureEvent` [...] +| schema operation | `CreateSchemaEvent`, `AlterSchemaEvent`, `DropSchemaEvent`, `LoadSchemaEvent`, `ListSchemaEvent`, `CreateSchemaFailureEvent`, `AlterSchemaFailureEvent`, `DropSchemaFailureEvent`, `LoadSchemaFailureEvent`, `ListSchemaFailureEvent` [...] +| catalog operation | `CreateCatalogEvent`, `AlterCatalogEvent`, `DropCatalogEvent`, `LoadCatalogEvent`, `ListCatalogEvent`, `CreateCatalogFailureEvent`, `AlterCatalogFailureEvent`, `DropCatalogFailureEvent`, `LoadCatalogFailureEvent`, `ListCatalogFailureEvent` [...] +| metalake operation | `CreateMetalakeEvent`, `AlterMetalakeEvent`, `DropMetalakeEvent`, `LoadMetalakeEvent`, `ListMetalakeEvent`, `CreateMetalakeFailureEvent`, `AlterMetalakeFailureEvent`, `DropMetalakeFailureEvent`, `LoadMetalakeFailureEvent`, `ListMetalakeFailureEvent` [...] +| Iceberg REST server table operation | `IcebergCreateTableEvent`, `IcebergUpdateTableEvent`, `IcebergDropTableEvent`, `IcebergLoadTableEvent`, `IcebergListTableEvent`, `IcebergTableExistsEvent`, `IcebergRenameTableEvent`, `IcebergCreateTableFailureEvent`, `IcebergUpdateTableFailureEvent`, `IcebergDropTableFailureEvent`, `IcebergLoadTableFailureEvent`, `IcebergListTableFailureEvent`, `IcebergRenameTableFailureEvent`, `IcebergTableExistsFailureEvent` [...] | tag operation | `ListTagsEvent`, `ListTagsInfoEvent`, `CreateTagEvent`, `GetTagEvent`, `AlterTagEvent`, `DeleteTagEvent`, `ListMetadataObjectsForTagEvent`, `ListTagsForMetadataObjectEvent`, `ListTagsInfoForMetadataObjectEvent`, `AssociateTagsForMetadataObjectEvent`, `GetTagForMetadataObjectEvent`, `ListTagsFailureEvent`, `ListTagInfoFailureEvent`, `CreateTagFailureEvent`, `GetTagFailureEvent`, `AlterTagFailureEvent`, `DeleteTagFailureEvent`, `ListMetadataObjectsFo [...] -| model operation | `DeleteModelEvent`, `DeleteModelVersionEvent`, `GetModelEvent`, `GetModelVersionEvent`, `LinkModelVersionEvent`, `ListModelEvent`, `ListModelVersionsEvent`, `RegisterAndLinkModelEvent`, `RegisterModelEvent`, `DeleteModelFailureEvent`, `DeleteModelVersionFailureEvent`, `GetModelFailureEvent`, `GetModelVersionFailureEvent`, `LinkModelVersionFailureEvent`, `ListModelFailureEvent`, `ListModelVersionFailureEvent`, `RegisterAndLinkModelFailureEvent`, [...] -| user operation | `AddUserEvent`, `GetUserEvent`, `ListUserNamesEvent`, `ListUsersEvent`, `RemoveUserEvent`, `GrantUserRolesEvent`, `RevokeUserRolesEvent`, `AddUserFailureEvent`, `GetUserFailureEvent`, `GrantUserRolesFailureEvent`, `ListUserNamesFailureEvent`, `ListUsersFailureEvent`, `RemoveUserFailureEvent`, `RevokeUserRolesFailureEvent` [...] -| group operation | `AddGroupEvent`, `GetGroupEvent`, `ListGroupNamesEvent`, `ListGroupsEvent`, `RemoveGroupEvent`, `GrantGroupRolesEvent`, `RevokeGroupRolesEvent`, `AddGroupFailureEvent`, `GetGroupFailureEvent`, `GrantGroupRolesFailureEvent`, `ListGroupNamesFailureEvent`, `ListGroupsFailureEvent`, `RemoveGroupFailureEvent`, `RevokeGroupRolesFailureEvent` [...] +| model operation | `DeleteModelEvent`, `DeleteModelVersionEvent`, `GetModelEvent`, `GetModelVersionEvent`, `LinkModelVersionEvent`, `ListModelEvent`, `ListModelVersionsEvent`, `RegisterAndLinkModelEvent`, `RegisterModelEvent`, `DeleteModelFailureEvent`, `DeleteModelVersionFailureEvent`, `GetModelFailureEvent`, `GetModelVersionFailureEvent`, `LinkModelVersionFailureEvent`, `ListModelFailureEvent`, `ListModelVersionFailureEvent`, `RegisterAndLinkModelFailureEvent`, [...] +| user operation | `AddUserEvent`, `GetUserEvent`, `ListUserNamesEvent`, `ListUsersEvent`, `RemoveUserEvent`, `GrantUserRolesEvent`, `RevokeUserRolesEvent`, `AddUserFailureEvent`, `GetUserFailureEvent`, `GrantUserRolesFailureEvent`, `ListUserNamesFailureEvent`, `ListUsersFailureEvent`, `RemoveUserFailureEvent`, `RevokeUserRolesFailureEvent` [...] +| group operation | `AddGroupEvent`, `GetGroupEvent`, `ListGroupNamesEvent`, `ListGroupsEvent`, `RemoveGroupEvent`, `GrantGroupRolesEvent`, `RevokeGroupRolesEvent`, `AddGroupFailureEvent`, `GetGroupFailureEvent`, `GrantGroupRolesFailureEvent`, `ListGroupNamesFailureEvent`, `ListGroupsFailureEvent`, `RemoveGroupFailureEvent`, `RevokeGroupRolesFailureEvent` [...] +| role operation | `CreateRoleEvent`, `DeleteRoleEvent`, `GetRoleEvent`, `GrantPrivilegesEvent`, `ListRoleNamesEvent`, `RevokePrivilegesEvent` [...]