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 b6a61bafb5 [#6760] test(core): Support role failure event to Gravitino server (#6866) b6a61bafb5 is described below commit b6a61bafb5d1e7f15c8336ac3e7d523dbdfd5831 Author: Lord of Abyss <103809695+abyss-l...@users.noreply.github.com> AuthorDate: Fri Apr 11 08:33:22 2025 +0800 [#6760] test(core): Support role failure event to Gravitino server (#6866) ### What changes were proposed in this pull request? Support role failure event to Gravitino server 1. Support role failure event to Gravitino server 2. Refactor the `EventBus#dispatchEvent` method using the Visitor design pattern to eliminate `instanceof` checks, improve extensibility, and ensure that unhandled event types can be detected at compile time. ### Why are the changes needed? Fix: #6760 ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? local test. --- .../api/event/AccessControlEventDispatcher.java | 26 ++- .../listener/api/event/CreateRoleFailureEvent.java | 66 +++++++ .../listener/api/event/DeleteRoleFailureEvent.java | 66 +++++++ .../listener/api/event/GetRoleFailureEvent.java | 65 +++++++ .../api/event/GrantPrivilegesFailureEvent.java | 100 ++++++++++ .../api/event/ListRoleNamesFailureEvent.java | 82 +++++++++ .../api/event/RevokePrivilegesFailureEvent.java | 99 ++++++++++ .../listener/api/event/RoleFailureEvent.java | 43 +++++ .../gravitino/listener/api/info/RoleInfo.java | 15 ++ .../listener/api/event/TestRoleEvent.java | 202 ++++++++++++++++++++- docs/gravitino-server-config.md | 26 +-- 11 files changed, 762 insertions(+), 28 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 cd5d1613f3..cecf20de50 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 @@ -319,13 +319,7 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { /** {@inheritDoc} */ @Override public boolean isServiceAdmin(String user) { - try { - // TODO: add Event - return dispatcher.isServiceAdmin(user); - } catch (Exception e) { - // TODO: add failure event - throw e; - } + return dispatcher.isServiceAdmin(user); } /** {@inheritDoc} */ @@ -346,7 +340,9 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { return roleObject; } catch (Exception e) { - // TODO: add failure event + eventBus.dispatchEvent( + new CreateRoleFailureEvent( + initiator, metalake, e, new RoleInfo(role, properties, securableObjects))); throw e; } } @@ -364,7 +360,7 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { return roleObject; } catch (Exception e) { - // TODO: add failure event + eventBus.dispatchEvent(new GetRoleFailureEvent(initiator, metalake, e, role)); throw e; } } @@ -381,7 +377,7 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { return isExists; } catch (Exception e) { - // TODO: add failure event + eventBus.dispatchEvent(new DeleteRoleFailureEvent(initiator, metalake, e, role)); throw e; } } @@ -398,7 +394,7 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { return roleNames; } catch (Exception e) { - // TODO: add failure event + eventBus.dispatchEvent(new ListRoleNamesFailureEvent(initiator, metalake, e)); throw e; } } @@ -416,7 +412,7 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { return roleNames; } catch (Exception e) { - // TODO: add failure event + eventBus.dispatchEvent(new ListRoleNamesFailureEvent(initiator, metalake, e, object)); throw e; } } @@ -438,7 +434,8 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { return roleObject; } catch (Exception e) { - // TODO: add failure event + eventBus.dispatchEvent( + new GrantPrivilegesFailureEvent(initiator, metalake, e, role, object, privileges)); throw e; } } @@ -460,7 +457,8 @@ public class AccessControlEventDispatcher implements AccessControlDispatcher { return roleObject; } catch (Exception e) { - // TODO: add failure event + eventBus.dispatchEvent( + new RevokePrivilegesFailureEvent(initiator, metalake, e, role, object, privileges)); throw e; } } diff --git a/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRoleFailureEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRoleFailureEvent.java new file mode 100644 index 0000000000..d3080bed62 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateRoleFailureEvent.java @@ -0,0 +1,66 @@ +/* + * 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 when an attempt to create a role in a metalake fails due to an + * exception. + */ +@DeveloperApi +public class CreateRoleFailureEvent extends RoleFailureEvent { + private final RoleInfo createRoleRequest; + /** + * Constructs a new {@code CreateRoleFailureEvent} instance. + * + * @param initiator the user who initiated the event. + * @param metalake the target metalake context for the role creation + * @param exception the exception that caused the failure + * @param createRoleRequest the role information that failed to be created + */ + public CreateRoleFailureEvent( + String initiator, String metalake, Exception exception, RoleInfo createRoleRequest) { + super(initiator, NameIdentifierUtil.ofRole(metalake, createRoleRequest.roleName()), exception); + + this.createRoleRequest = createRoleRequest; + } + + /** + * Returns the role information that failed to be created. + * + * @return The {@link RoleInfo} instance. + */ + public RoleInfo createRoleRequest() { + return createRoleRequest; + } + + /** + * 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/DeleteRoleFailureEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteRoleFailureEvent.java new file mode 100644 index 0000000000..a020030a2e --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteRoleFailureEvent.java @@ -0,0 +1,66 @@ +/* + * 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 when an attempt to delete a role from a metalake fails due to an + * exception. + */ +@DeveloperApi +public class DeleteRoleFailureEvent extends RoleFailureEvent { + private final String roleName; + + /** + * Constructs a new {@code DeleteRoleFailureEvent} instance. + * + * @param user the user who initiated the deletion attempt + * @param metalake the target metalake from which the role is to be deleted + * @param exception the exception that caused the failure + * @param roleName the name of the role intended for deletion + */ + protected DeleteRoleFailureEvent( + String user, String metalake, Exception exception, String roleName) { + super(user, NameIdentifierUtil.ofRole(metalake, roleName), exception); + + this.roleName = roleName; + } + + /** + * Returns the name of the role that was intended for deletion. + * + * @return the name of the 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/GetRoleFailureEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/GetRoleFailureEvent.java new file mode 100644 index 0000000000..87eb60ff74 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/GetRoleFailureEvent.java @@ -0,0 +1,65 @@ +/* + * 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 when an attempt to retrieve a role from a metalake fails due to an + * exception. + */ +@DeveloperApi +public class GetRoleFailureEvent extends RoleFailureEvent { + private final String roleName; + + /** + * Constructs a new {@code GetRoleFailureEvent} instance. + * + * @param user the user who initiated the retrieval attempt + * @param metalake the metalake from which the role is being retrieved + * @param exception the exception that occurred during role retrieval + * @param roleName the name of the role being retrieved + */ + public GetRoleFailureEvent(String user, String metalake, Exception exception, String roleName) { + super(user, NameIdentifierUtil.ofRole(metalake, roleName), exception); + + this.roleName = roleName; + } + + /** + * Returns the name of the role that was attempted to be retrieved. + * + * @return the role name + */ + 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/GrantPrivilegesFailureEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesFailureEvent.java new file mode 100644 index 0000000000..fab59eb065 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/GrantPrivilegesFailureEvent.java @@ -0,0 +1,100 @@ +/* + * 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 com.google.common.collect.ImmutableSet; +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 triggered when an attempt to grant privileges to a role fails due to an + * exception. + */ +@DeveloperApi +public class GrantPrivilegesFailureEvent extends RoleFailureEvent { + + private final String roleName; + private final MetadataObject metadataObject; + private final Set<Privilege> privileges; + + /** + * Constructs a new {@code GrantPrivilegesFailureEvent} instance. + * + * @param user the name of the user who triggered the event + * @param metalake the name of the metalake + * @param exception the exception that occurred while granting privileges + * @param roleName the name of the role to which privileges were being granted + * @param metadataObject the {@code MetadataObject} instance associated with the role + * @param privileges the set of privileges intended to be granted; if {@code null}, an empty set + * is used + */ + public GrantPrivilegesFailureEvent( + String user, + String metalake, + Exception exception, + String roleName, + MetadataObject metadataObject, + Set<Privilege> privileges) { + super(user, NameIdentifierUtil.ofRole(metalake, roleName), exception); + this.roleName = roleName; + this.metadataObject = metadataObject; + this.privileges = privileges == null ? ImmutableSet.of() : ImmutableSet.copyOf(privileges); + } + + /** + * Returns the name of the role. + * + * @return the name of the role to which privileges were intended to be granted + */ + public String roleName() { + return roleName; + } + + /** + * Returns the associated {@code MetadataObject} instance. + * + * @return the {@code MetadataObject} instance related to the role + */ + public MetadataObject object() { + return metadataObject; + } + + /** + * Returns the set of privileges intended to be granted. + * + * @return an immutable set of privileges + */ + 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/ListRoleNamesFailureEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesFailureEvent.java new file mode 100644 index 0000000000..e95c63ac0b --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/ListRoleNamesFailureEvent.java @@ -0,0 +1,82 @@ +/* + * 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 when an attempt to list role names for a metalake fails due to an + * exception. + */ +@DeveloperApi +public class ListRoleNamesFailureEvent extends RoleFailureEvent { + private final Optional<MetadataObject> metadataObject; + + /** + * Constructs a new {@code ListRoleNamesFailureEvent} instance with no associated {@code + * MetadataObject}. + * + * @param user the user who initiated the operation + * @param metalake the name of the metalake for which role names were being listed + * @param exception the exception that occurred during the operation + */ + public ListRoleNamesFailureEvent(String user, String metalake, Exception exception) { + super(user, NameIdentifierUtil.ofMetalake(metalake), exception); + + this.metadataObject = Optional.empty(); + } + + /** + * Construct a new {@link ListRoleNamesFailureEvent} instance with the specified arguments. + * + * @param user The user who initiated the event. + * @param metalake The name of the metalake for which role names are being listed. + * @param exception The exception that occurred while listing role names. + * @param metadataObject The {@link MetadataObject} instance related to the role names being + * listed, if present. + */ + public ListRoleNamesFailureEvent( + String user, String metalake, Exception exception, MetadataObject metadataObject) { + super(user, NameIdentifierUtil.ofMetalake(metalake), exception); + this.metadataObject = Optional.of(metadataObject); + } + + /** + * Returns the associated {@code MetadataObject} instance. + * + * @return an {@code Optional} containing the {@code MetadataObject} if present, otherwise empty + */ + public Optional<MetadataObject> metadataObject() { + return metadataObject; + } + + /** + * 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/RevokePrivilegesFailureEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesFailureEvent.java new file mode 100644 index 0000000000..d934614d62 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/RevokePrivilegesFailureEvent.java @@ -0,0 +1,99 @@ +/* + * 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 com.google.common.collect.ImmutableSet; +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 triggered when an attempt to revoke privileges from a role fails due to an + * exception. + */ +@DeveloperApi +public class RevokePrivilegesFailureEvent extends RoleFailureEvent { + private final String roleName; + private final MetadataObject metadataObject; + private final Set<Privilege> privileges; + + /** + * Constructs a new {@code RevokePrivilegesFailureEvent} instance. + * + * @param user the user who initiated the event + * @param metalake the name of the metalake + * @param exception the exception that occurred during the revocation attempt + * @param roleName the name of the role from which privileges were attempted to be revoked + * @param metadataObject the {@code MetadataObject} instance associated with the role + * @param privileges the set of privileges intended to be revoked; if {@code null}, an empty set + * is used + */ + public RevokePrivilegesFailureEvent( + String user, + String metalake, + Exception exception, + String roleName, + MetadataObject metadataObject, + Set<Privilege> privileges) { + super(user, NameIdentifierUtil.ofRole(metalake, roleName), exception); + this.roleName = roleName; + this.metadataObject = metadataObject; + this.privileges = privileges == null ? ImmutableSet.of() : ImmutableSet.copyOf(privileges); + } + + /** + * Returns the name of the role from which privileges were attempted to be revoked. + * + * @return the role name + */ + public String roleName() { + return roleName; + } + + /** + * Returns the associated {@code MetadataObject} instance. + * + * @return the {@code MetadataObject} instance linked to the role + */ + public MetadataObject metadataObject() { + return metadataObject; + } + + /** + * Returns the set of privileges intended to be revoked. + * + * @return an immutable set of privileges + */ + 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/RoleFailureEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/RoleFailureEvent.java new file mode 100644 index 0000000000..359e68e435 --- /dev/null +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/RoleFailureEvent.java @@ -0,0 +1,43 @@ +/* + * 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 that occurs when a role operation fails due to an exception. This event + * contains the role information, a unique identifier for the operation, and the exception that + * caused the failure. + */ +@DeveloperApi +public abstract class RoleFailureEvent extends FailureEvent { + + /** + * Constructs a new {@code RoleFailureEvent} instance. + * + * @param user the user who initiated the operation + * @param identifier the unique identifier associated with the operation + * @param exception the exception that caused the failure + */ + protected RoleFailureEvent(String user, NameIdentifier identifier, Exception exception) { + super(user, identifier, exception); + } +} 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 index c89fd5233d..59865dcec6 100644 --- 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 @@ -51,6 +51,21 @@ public class RoleInfo { : ImmutableList.copyOf(roleObject.securableObjects()); } + /** + * Constructs a new {@code RoleInfo} instance using the given arguments. + * + * @param roleName The role name. + * @param properties The properties associated with the role. + * @param securableObjects The securable objects that belong to the role. + */ + public RoleInfo( + String roleName, Map<String, String> properties, List<SecurableObject> securableObjects) { + this.roleName = roleName; + this.properties = properties == null ? ImmutableMap.of() : ImmutableMap.copyOf(properties); + this.securableObjects = + securableObjects == null ? ImmutableList.of() : ImmutableList.copyOf(securableObjects); + } + /** * Returns the role name. * 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 c4d63ffbfa..697c3836bd 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 @@ -101,6 +101,24 @@ public class TestRoleEvent { Assertions.assertEquals(roleName, roleInfo.roleName()); Assertions.assertEquals(properties, roleInfo.properties()); Assertions.assertEquals(securableObjects, roleInfo.securableObjects()); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> roleInfo.securableObjects().add(null)); + + Assertions.assertThrows( + UnsupportedOperationException.class, () -> roleInfo.properties().put("test", "test")); + } + + @Test + void testRoleInfoWithNullSecurableObjects() { + RoleInfo roleInfo = new RoleInfo("test_role", ImmutableMap.of("comment", "test comment"), null); + Assertions.assertEquals("test_role", roleInfo.roleName()); + Assertions.assertEquals(ImmutableMap.of("comment", "test comment"), roleInfo.properties()); + Assertions.assertThrows( + UnsupportedOperationException.class, () -> roleInfo.properties().put("testKey", "testVal")); + Assertions.assertEquals(Collections.emptyList(), roleInfo.securableObjects()); + Assertions.assertThrows( + UnsupportedOperationException.class, () -> roleInfo.securableObjects().add(null)); } @Test @@ -142,6 +160,50 @@ public class TestRoleEvent { validateRoleInfo(roleInfo, roleObject); } + @Test + void testCreateRoleFailureEvent() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, + () -> failureDispatcher.createRole(METALAKE, roleName, properties, securableObjects)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(CreateRoleFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.CREATE_ROLE, event.operationType()); + + CreateRoleFailureEvent createRoleFailureEvent = (CreateRoleFailureEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), createRoleFailureEvent.identifier()); + RoleInfo roleInfo = createRoleFailureEvent.createRoleRequest(); + Assertions.assertEquals(roleName, roleInfo.roleName()); + Assertions.assertEquals(properties, roleInfo.properties()); + Assertions.assertEquals(securableObjects, roleInfo.securableObjects()); + } + + @Test + void testCreateRoleFailureEventWithNullSecurableObjects() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, + () -> failureDispatcher.createRole(METALAKE, roleName, properties, null)); + + // Validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(CreateRoleFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + CreateRoleFailureEvent createRoleFailureEvent = (CreateRoleFailureEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), createRoleFailureEvent.identifier()); + + RoleInfo roleInfo = createRoleFailureEvent.createRoleRequest(); + Assertions.assertEquals(roleName, roleInfo.roleName()); + Assertions.assertEquals(properties, roleInfo.properties()); + Assertions.assertNotNull(roleInfo.securableObjects()); + Assertions.assertTrue(roleInfo.securableObjects().isEmpty()); + Assertions.assertThrows( + UnsupportedOperationException.class, () -> roleInfo.securableObjects().add(null)); + } + @Test void testDeleteRolePreEvent() { dispatcher.deleteRole(METALAKE, roleName); @@ -191,6 +253,23 @@ public class TestRoleEvent { Assertions.assertEquals(otherRoleName, deleteRoleEvent.roleName()); } + @Test + void testDeleteRoleFailureEvent() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, () -> failureDispatcher.deleteRole(METALAKE, roleName)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(DeleteRoleFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.DELETE_ROLE, event.operationType()); + + DeleteRoleFailureEvent deleteRoleFailureEvent = (DeleteRoleFailureEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), deleteRoleFailureEvent.identifier()); + Assertions.assertEquals(roleName, deleteRoleFailureEvent.roleName()); + } + @Test void testGetRolePreEvent() { dispatcher.getRole(METALAKE, roleName); @@ -225,6 +304,23 @@ public class TestRoleEvent { validateRoleInfo(roleInfo, roleName, properties, securableObjects); } + @Test + void testGetRoleFailureEvent() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, () -> failureDispatcher.getRole(METALAKE, roleName)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(GetRoleFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.GET_ROLE, event.operationType()); + + GetRoleFailureEvent getRoleFailureEvent = (GetRoleFailureEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), getRoleFailureEvent.identifier()); + Assertions.assertEquals(roleName, getRoleFailureEvent.roleName()); + } + @Test void testListRolesPreEvent() { dispatcher.listRoleNames(METALAKE); @@ -255,6 +351,22 @@ public class TestRoleEvent { Assertions.assertFalse(listRoleNamesEvent.object().isPresent()); } + @Test + void testListRolesFailureEvent() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, () -> failureDispatcher.listRoleNames(METALAKE)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(ListRoleNamesFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.LIST_ROLE_NAMES, event.operationType()); + + ListRoleNamesFailureEvent listRoleNamesFailureEvent = (ListRoleNamesFailureEvent) event; + Assertions.assertEquals(identifier, listRoleNamesFailureEvent.identifier()); + Assertions.assertFalse(listRoleNamesFailureEvent.metadataObject().isPresent()); + } + @Test void testListRolesFromObjectPreEvent() { dispatcher.listRoleNamesByObject(METALAKE, securableObjects.get(0)); @@ -288,7 +400,26 @@ public class TestRoleEvent { } @Test - void testGrantPrivilegesToRole() { + void testListRoleFromObjectFailureEvent() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, + () -> failureDispatcher.listRoleNamesByObject(METALAKE, securableObjects.get(0))); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(ListRoleNamesFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.LIST_ROLE_NAMES, event.operationType()); + + ListRoleNamesFailureEvent listRoleNamesFailureEvent = (ListRoleNamesFailureEvent) event; + Assertions.assertEquals(identifier, listRoleNamesFailureEvent.identifier()); + Assertions.assertTrue(listRoleNamesFailureEvent.metadataObject().isPresent()); + Assertions.assertEquals( + securableObjects.get(0), listRoleNamesFailureEvent.metadataObject().get()); + } + + @Test + void testGrantPrivilegesToRolePreEvent() { dispatcher.grantPrivilegeToRole(METALAKE, roleName, metadataObject, privileges); // validate pre-event @@ -322,6 +453,52 @@ public class TestRoleEvent { validateRoleInfo(roleInfo, roleName, properties, securableObjects); } + @Test + void testGrantPrivilegesToRoleFailureEvent() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, + () -> + failureDispatcher.grantPrivilegeToRole(METALAKE, roleName, metadataObject, privileges)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(GrantPrivilegesFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.GRANT_PRIVILEGES, event.operationType()); + + GrantPrivilegesFailureEvent grantPrivilegesFailureEvent = (GrantPrivilegesFailureEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), grantPrivilegesFailureEvent.identifier()); + Assertions.assertEquals(roleName, grantPrivilegesFailureEvent.roleName()); + Assertions.assertEquals(metadataObject, grantPrivilegesFailureEvent.object()); + Assertions.assertEquals(privileges, grantPrivilegesFailureEvent.privileges()); + } + + @Test + void testGrantPrivilegesFailureEventWithNullPrivileges() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, + () -> failureDispatcher.grantPrivilegeToRole(METALAKE, roleName, metadataObject, null)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(GrantPrivilegesFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.GRANT_PRIVILEGES, event.operationType()); + + GrantPrivilegesFailureEvent grantPrivilegesFailureEvent = (GrantPrivilegesFailureEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), grantPrivilegesFailureEvent.identifier()); + Assertions.assertEquals(roleName, grantPrivilegesFailureEvent.roleName()); + Assertions.assertEquals(metadataObject, grantPrivilegesFailureEvent.object()); + Assertions.assertNotNull(grantPrivilegesFailureEvent.privileges()); + Assertions.assertTrue(grantPrivilegesFailureEvent.privileges().isEmpty()); + + Assertions.assertThrows( + UnsupportedOperationException.class, + () -> grantPrivilegesFailureEvent.privileges().add(null)); + } + @Test void testRevokePrivilegesFromRolePreEvent() { dispatcher.revokePrivilegesFromRole(METALAKE, roleName, metadataObject, privileges); @@ -357,6 +534,29 @@ public class TestRoleEvent { validateRoleInfo(roleInfo, roleName, properties, securableObjects); } + @Test + void testRevokePrivilegesFromRoleFailureEvent() { + Assertions.assertThrowsExactly( + GravitinoRuntimeException.class, + () -> + failureDispatcher.revokePrivilegesFromRole( + METALAKE, roleName, metadataObject, privileges)); + + // validate event + Event event = dummyEventListener.popPostEvent(); + Assertions.assertEquals(RevokePrivilegesFailureEvent.class, event.getClass()); + Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus()); + Assertions.assertEquals(OperationType.REVOKE_PRIVILEGES, event.operationType()); + + RevokePrivilegesFailureEvent revokePrivilegesFailureEvent = + (RevokePrivilegesFailureEvent) event; + Assertions.assertEquals( + NameIdentifierUtil.ofRole(METALAKE, roleName), revokePrivilegesFailureEvent.identifier()); + Assertions.assertEquals(roleName, revokePrivilegesFailureEvent.roleName()); + Assertions.assertEquals(metadataObject, revokePrivilegesFailureEvent.metadataObject()); + Assertions.assertEquals(privileges, revokePrivilegesFailureEvent.privileges()); + } + private AccessControlEventDispatcher mockRoleDispatcher() { AccessControlEventDispatcher dispatcher = mock(AccessControlEventDispatcher.class); Group mockGroup = getMockGroup(groupName, ImmutableList.of(roleName, otherRoleName)); diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md index de56789997..5c41b8aa3c 100644 --- a/docs/gravitino-server-config.md +++ b/docs/gravitino-server-config.md @@ -118,20 +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` [...] -| role operation | `CreateRoleEvent`, `DeleteRoleEvent`, `GetRoleEvent`, `GrantPrivilegesEvent`, `ListRoleNamesEvent`, `RevokePrivilegesEvent` [...] +| 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`, `CreateRoleFailureEvent`, `DeleteRoleFailureEvent`, `GetRoleFailureEvent`, `GrantPrivilegesFailureEvent`, `ListRoleNamesFailureEvent`, `RevokePrivilegesFailureEvent` [...]