This is an automated email from the ASF dual-hosted git repository. jshao 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 627e88ffb [#4126] improvement(core): Remove MetalakeAdmin API (#4127) 627e88ffb is described below commit 627e88ffb9cbb8f5efe1b6e8af3a69f8e051d3e0 Author: roryqi <ror...@apache.org> AuthorDate: Tue Jul 16 18:21:18 2024 +0800 [#4126] improvement(core): Remove MetalakeAdmin API (#4127) ### What changes were proposed in this pull request? Remove MetalakeAdmin API. ### Why are the changes needed? Fix: #4126 ### Does this PR introduce _any_ user-facing change? Remove API. But this API isn't released. ### How was this patch tested? Existing tests. --- .../gravitino/client/GravitinoAdminClient.java | 45 ----- .../apache/gravitino/client/TestMetalakeAdmin.java | 110 ----------- .../authorization/AccessControlManager.java | 91 ++------- .../gravitino/authorization/AdminManager.java | 122 ------------ .../authorization/TestAccessControlManager.java | 21 -- .../gravitino/server/web/rest/GroupOperations.java | 33 +++- .../server/web/rest/MetalakeAdminOperations.java | 98 ---------- .../server/web/rest/PermissionOperations.java | 76 ++++++-- .../gravitino/server/web/rest/RoleOperations.java | 37 ++-- .../gravitino/server/web/rest/UserOperations.java | 32 +++- .../web/rest/TestMetalakeAdminOperations.java | 213 --------------------- 11 files changed, 149 insertions(+), 729 deletions(-) diff --git a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java index 67d32289f..b70839733 100644 --- a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java +++ b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java @@ -67,7 +67,6 @@ public class GravitinoAdminClient extends GravitinoClientBase implements Support private static final String API_METALAKES_USERS_PATH = "api/metalakes/%s/users/%s"; private static final String API_METALAKES_GROUPS_PATH = "api/metalakes/%s/groups/%s"; private static final String API_METALAKES_ROLES_PATH = "api/metalakes/%s/roles/%s"; - private static final String API_ADMIN_PATH = "api/admins/%s"; private static final String API_PERMISSION_PATH = "api/metalakes/%s/permissions/%s"; private static final String BLANK_PLACE_HOLDER = ""; @@ -334,50 +333,6 @@ public class GravitinoAdminClient extends GravitinoClientBase implements Support return resp.getGroup(); } - /** - * Adds a new metalake admin. - * - * @param user The name of the User. - * @return The added User instance. - * @throws UserAlreadyExistsException If a metalake admin with the same name already exists. - * @throws RuntimeException If adding the User encounters storage issues. - */ - public User addMetalakeAdmin(String user) throws UserAlreadyExistsException { - UserAddRequest req = new UserAddRequest(user); - req.validate(); - - UserResponse resp = - restClient.post( - String.format(API_ADMIN_PATH, BLANK_PLACE_HOLDER), - req, - UserResponse.class, - Collections.emptyMap(), - ErrorHandlers.userErrorHandler()); - resp.validate(); - - return resp.getUser(); - } - - /** - * Removes a metalake admin. - * - * @param user The name of the User. - * @return True if the User was successfully removed, false only when there's no such metalake - * admin, otherwise it will throw an exception. - * @throws RuntimeException If removing the User encounters storage issues. - */ - public boolean removeMetalakeAdmin(String user) { - RemoveResponse resp = - restClient.delete( - String.format(API_ADMIN_PATH, user), - RemoveResponse.class, - Collections.emptyMap(), - ErrorHandlers.userErrorHandler()); - resp.validate(); - - return resp.removed(); - } - /** * Gets a Role. * diff --git a/clients/client-java/src/test/java/org/apache/gravitino/client/TestMetalakeAdmin.java b/clients/client-java/src/test/java/org/apache/gravitino/client/TestMetalakeAdmin.java deleted file mode 100644 index 6dd10965d..000000000 --- a/clients/client-java/src/test/java/org/apache/gravitino/client/TestMetalakeAdmin.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.client; - -import static javax.servlet.http.HttpServletResponse.SC_CONFLICT; -import static javax.servlet.http.HttpServletResponse.SC_OK; -import static org.apache.hc.core5.http.HttpStatus.SC_SERVER_ERROR; - -import java.time.Instant; -import org.apache.gravitino.authorization.User; -import org.apache.gravitino.dto.AuditDTO; -import org.apache.gravitino.dto.authorization.UserDTO; -import org.apache.gravitino.dto.requests.UserAddRequest; -import org.apache.gravitino.dto.responses.ErrorResponse; -import org.apache.gravitino.dto.responses.RemoveResponse; -import org.apache.gravitino.dto.responses.UserResponse; -import org.apache.gravitino.exceptions.UserAlreadyExistsException; -import org.apache.hc.core5.http.Method; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TestMetalakeAdmin extends TestBase { - - private static final String API_ADMINS_PATH = "api/admins/%s"; - - @BeforeAll - public static void setUp() throws Exception { - TestBase.setUp(); - } - - @Test - public void testAddMetalakeAdmin() throws Exception { - String username = "user"; - String userPath = withSlash(String.format(API_ADMINS_PATH, "")); - UserAddRequest request = new UserAddRequest(username); - - UserDTO mockUser = mockUserDTO(username); - UserResponse userResponse = new UserResponse(mockUser); - buildMockResource(Method.POST, userPath, request, userResponse, SC_OK); - - User addedUser = client.addMetalakeAdmin(username); - Assertions.assertNotNull(addedUser); - assertUser(addedUser, mockUser); - - // test UserAlreadyExistsException - ErrorResponse errResp1 = - ErrorResponse.alreadyExists( - UserAlreadyExistsException.class.getSimpleName(), "user already exists"); - buildMockResource(Method.POST, userPath, request, errResp1, SC_CONFLICT); - Exception ex = - Assertions.assertThrows( - UserAlreadyExistsException.class, () -> client.addMetalakeAdmin(username)); - Assertions.assertEquals("user already exists", ex.getMessage()); - - // test RuntimeException - ErrorResponse errResp3 = ErrorResponse.internalError("internal error"); - buildMockResource(Method.POST, userPath, request, errResp3, SC_SERVER_ERROR); - Assertions.assertThrows( - RuntimeException.class, () -> client.addMetalakeAdmin(username), "internal error"); - } - - @Test - public void testRemoveMetalakeAdmin() throws Exception { - String username = "user"; - String rolePath = withSlash(String.format(API_ADMINS_PATH, username)); - - RemoveResponse removeResponse = new RemoveResponse(true); - buildMockResource(Method.DELETE, rolePath, null, removeResponse, SC_OK); - - Assertions.assertTrue(client.removeMetalakeAdmin(username)); - - removeResponse = new RemoveResponse(false); - buildMockResource(Method.DELETE, rolePath, null, removeResponse, SC_OK); - Assertions.assertFalse(client.removeMetalakeAdmin(username)); - - // test RuntimeException - ErrorResponse errResp = ErrorResponse.internalError("internal error"); - buildMockResource(Method.DELETE, rolePath, null, errResp, SC_SERVER_ERROR); - Assertions.assertThrows(RuntimeException.class, () -> client.removeMetalakeAdmin(username)); - } - - private UserDTO mockUserDTO(String name) { - return UserDTO.builder() - .withName(name) - .withAudit(AuditDTO.builder().withCreator("creator").withCreateTime(Instant.now()).build()) - .build(); - } - - private void assertUser(User expected, User actual) { - Assertions.assertEquals(expected.name(), actual.name()); - Assertions.assertEquals(expected.roles(), actual.roles()); - } -} diff --git a/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java b/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java index 2425f6d58..69ca26bb7 100644 --- a/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java +++ b/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java @@ -22,6 +22,7 @@ import com.google.common.annotations.VisibleForTesting; import java.util.List; import java.util.Map; import org.apache.gravitino.Config; +import org.apache.gravitino.Configs; import org.apache.gravitino.EntityStore; import org.apache.gravitino.exceptions.GroupAlreadyExistsException; import org.apache.gravitino.exceptions.NoSuchGroupException; @@ -31,30 +32,23 @@ import org.apache.gravitino.exceptions.NoSuchUserException; import org.apache.gravitino.exceptions.RoleAlreadyExistsException; import org.apache.gravitino.exceptions.UserAlreadyExistsException; import org.apache.gravitino.storage.IdGenerator; -import org.apache.gravitino.utils.Executable; /** - * AccessControlManager is used for manage users, roles, admin, grant information, this class is an - * entrance class for tenant management. This lock policy about this is as follows: First, admin - * operations are prevented by one lock. Then, other operations are prevented by the other lock. For - * non-admin operations, Apache Gravitino doesn't choose metalake level lock. There are some reasons - * mainly: First, the metalake can be renamed by users. It's hard to maintain a map with metalake as - * the key. Second, the lock will be couped with life cycle of the metalake. + * AccessControlManager is used for manage users, roles, grant information, this class is an + * entrance class for tenant management. The operations will be protected by one lock. */ public class AccessControlManager { private final UserGroupManager userGroupManager; - private final AdminManager adminManager; private final RoleManager roleManager; private final PermissionManager permissionManager; - private final Object adminOperationLock = new Object(); - private final Object nonAdminOperationLock = new Object(); + private final List<String> serviceAdmins; public AccessControlManager(EntityStore store, IdGenerator idGenerator, Config config) { - this.adminManager = new AdminManager(store, idGenerator, config); this.roleManager = new RoleManager(store, idGenerator, config); this.userGroupManager = new UserGroupManager(store, idGenerator); this.permissionManager = new PermissionManager(store, roleManager); + this.serviceAdmins = config.get(Configs.SERVICE_ADMINS); } /** @@ -69,7 +63,7 @@ public class AccessControlManager { */ public User addUser(String metalake, String user) throws UserAlreadyExistsException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> userGroupManager.addUser(metalake, user)); + return userGroupManager.addUser(metalake, user); } /** @@ -83,7 +77,7 @@ public class AccessControlManager { * @throws RuntimeException If removing the User encounters storage issues. */ public boolean removeUser(String metalake, String user) throws NoSuchMetalakeException { - return doWithNonAdminLock(() -> userGroupManager.removeUser(metalake, user)); + return userGroupManager.removeUser(metalake, user); } /** @@ -98,7 +92,7 @@ public class AccessControlManager { */ public User getUser(String metalake, String user) throws NoSuchUserException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> userGroupManager.getUser(metalake, user)); + return userGroupManager.getUser(metalake, user); } /** @@ -113,7 +107,7 @@ public class AccessControlManager { */ public Group addGroup(String metalake, String group) throws GroupAlreadyExistsException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> userGroupManager.addGroup(metalake, group)); + return userGroupManager.addGroup(metalake, group); } /** @@ -127,7 +121,7 @@ public class AccessControlManager { * @throws RuntimeException If removing the Group encounters storage issues. */ public boolean removeGroup(String metalake, String group) throws NoSuchMetalakeException { - return doWithNonAdminLock(() -> userGroupManager.removeGroup(metalake, group)); + return userGroupManager.removeGroup(metalake, group); } /** @@ -142,7 +136,7 @@ public class AccessControlManager { */ public Group getGroup(String metalake, String group) throws NoSuchGroupException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> userGroupManager.getGroup(metalake, group)); + return userGroupManager.getGroup(metalake, group); } /** @@ -159,7 +153,7 @@ public class AccessControlManager { */ public User grantRolesToUser(String metalake, List<String> roles, String user) throws NoSuchUserException, NoSuchRoleException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> permissionManager.grantRolesToUser(metalake, roles, user)); + return permissionManager.grantRolesToUser(metalake, roles, user); } /** @@ -176,7 +170,7 @@ public class AccessControlManager { */ public Group grantRolesToGroup(String metalake, List<String> roles, String group) throws NoSuchGroupException, NoSuchRoleException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> permissionManager.grantRolesToGroup(metalake, roles, group)); + return permissionManager.grantRolesToGroup(metalake, roles, group); } /** @@ -193,7 +187,7 @@ public class AccessControlManager { */ public Group revokeRolesFromGroup(String metalake, List<String> roles, String group) throws NoSuchGroupException, NoSuchRoleException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> permissionManager.revokeRolesFromGroup(metalake, roles, group)); + return permissionManager.revokeRolesFromGroup(metalake, roles, group); } /** @@ -210,31 +204,7 @@ public class AccessControlManager { */ public User revokeRolesFromUser(String metalake, List<String> roles, String user) throws NoSuchUserException, NoSuchRoleException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> permissionManager.revokeRolesFromUser(metalake, roles, user)); - } - - /** - * Adds a new metalake admin. - * - * @param user The name of the User. - * @return The added User instance. - * @throws UserAlreadyExistsException If a metalake admin with the same name already exists. - * @throws RuntimeException If adding the User encounters storage issues. - */ - public User addMetalakeAdmin(String user) throws UserAlreadyExistsException { - return doWithAdminLock(() -> adminManager.addMetalakeAdmin(user)); - } - - /** - * Removes a metalake admin. - * - * @param user The name of the User. - * @return True if the User was successfully removed, false only when there's no such metalake - * admin, otherwise it will throw an exception. - * @throws RuntimeException If removing the User encounters storage issues. - */ - public boolean removeMetalakeAdmin(String user) { - return doWithAdminLock(() -> adminManager.removeMetalakeAdmin(user)); + return permissionManager.revokeRolesFromUser(metalake, roles, user); } /** @@ -244,17 +214,7 @@ public class AccessControlManager { * @return True if the user is service admin, otherwise false. */ public boolean isServiceAdmin(String user) { - return adminManager.isServiceAdmin(user); - } - - /** - * Judges whether the user is the metalake admin. - * - * @param user the name of the user - * @return True if the user is metalake admin, otherwise false. - */ - public boolean isMetalakeAdmin(String user) { - return doWithAdminLock(() -> adminManager.isMetalakeAdmin(user)); + return serviceAdmins.contains(user); } /** @@ -275,8 +235,7 @@ public class AccessControlManager { Map<String, String> properties, List<SecurableObject> securableObjects) throws RoleAlreadyExistsException, NoSuchMetalakeException { - return doWithNonAdminLock( - () -> roleManager.createRole(metalake, role, properties, securableObjects)); + return roleManager.createRole(metalake, role, properties, securableObjects); } /** @@ -291,7 +250,7 @@ public class AccessControlManager { */ public Role getRole(String metalake, String role) throws NoSuchRoleException, NoSuchMetalakeException { - return doWithNonAdminLock(() -> roleManager.getRole(metalake, role)); + return roleManager.getRole(metalake, role); } /** @@ -305,23 +264,11 @@ public class AccessControlManager { * @throws RuntimeException If deleting the Role encounters storage issues. */ public boolean deleteRole(String metalake, String role) throws NoSuchMetalakeException { - return doWithNonAdminLock(() -> roleManager.deleteRole(metalake, role)); + return roleManager.deleteRole(metalake, role); } @VisibleForTesting RoleManager getRoleManager() { return roleManager; } - - private <R, E extends Exception> R doWithNonAdminLock(Executable<R, E> executable) throws E { - synchronized (nonAdminOperationLock) { - return executable.execute(); - } - } - - private <R, E extends Exception> R doWithAdminLock(Executable<R, E> executable) throws E { - synchronized (adminOperationLock) { - return executable.execute(); - } - } } diff --git a/core/src/main/java/org/apache/gravitino/authorization/AdminManager.java b/core/src/main/java/org/apache/gravitino/authorization/AdminManager.java deleted file mode 100644 index f1a6f7036..000000000 --- a/core/src/main/java/org/apache/gravitino/authorization/AdminManager.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.authorization; - -import com.google.common.collect.Lists; -import java.io.IOException; -import java.time.Instant; -import java.util.List; -import org.apache.gravitino.Config; -import org.apache.gravitino.Configs; -import org.apache.gravitino.Entity; -import org.apache.gravitino.EntityAlreadyExistsException; -import org.apache.gravitino.EntityStore; -import org.apache.gravitino.NameIdentifier; -import org.apache.gravitino.Namespace; -import org.apache.gravitino.exceptions.UserAlreadyExistsException; -import org.apache.gravitino.meta.AuditInfo; -import org.apache.gravitino.meta.UserEntity; -import org.apache.gravitino.storage.IdGenerator; -import org.apache.gravitino.utils.PrincipalUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * There are two kinds of admin roles in the system: service admin and metalake admin. The service - * admin is configured instead of managing by APIs. It is responsible for creating metalake admin. - * If Gravitino enables authorization, service admin is required. Metalake admin can create a - * metalake or drops its metalake. The metalake admin will be responsible for managing the access - * control. AdminManager operates underlying store using the lock because kv storage needs the lock. - */ -class AdminManager { - - private static final Logger LOG = LoggerFactory.getLogger(AdminManager.class); - - private final EntityStore store; - private final IdGenerator idGenerator; - private final List<String> serviceAdmins; - - AdminManager(EntityStore store, IdGenerator idGenerator, Config config) { - this.store = store; - this.idGenerator = idGenerator; - this.serviceAdmins = config.get(Configs.SERVICE_ADMINS); - } - - User addMetalakeAdmin(String user) { - - UserEntity userEntity = - UserEntity.builder() - .withId(idGenerator.nextId()) - .withName(user) - .withNamespace( - Namespace.of( - Entity.SYSTEM_METALAKE_RESERVED_NAME, - Entity.AUTHORIZATION_CATALOG_NAME, - Entity.ADMIN_SCHEMA_NAME)) - .withRoleNames(Lists.newArrayList()) - .withAuditInfo( - AuditInfo.builder() - .withCreator(PrincipalUtils.getCurrentPrincipal().getName()) - .withCreateTime(Instant.now()) - .build()) - .build(); - try { - store.put(userEntity, false /* overwritten */); - return userEntity; - } catch (EntityAlreadyExistsException e) { - LOG.warn("User {} in the metalake admin already exists", user, e); - throw new UserAlreadyExistsException("User %s in the metalake admin already exists", user); - } catch (IOException ioe) { - LOG.error("Adding user {} failed to the metalake admin due to storage issues", user, ioe); - throw new RuntimeException(ioe); - } - } - - boolean removeMetalakeAdmin(String user) { - try { - return store.delete(ofMetalakeAdmin(user), Entity.EntityType.USER); - } catch (IOException ioe) { - LOG.error( - "Removing user {} from the metalake admin {} failed due to storage issues", user, ioe); - throw new RuntimeException(ioe); - } - } - - boolean isServiceAdmin(String user) { - return serviceAdmins.contains(user); - } - - boolean isMetalakeAdmin(String user) { - try { - return store.exists(ofMetalakeAdmin(user), Entity.EntityType.USER); - } catch (IOException ioe) { - LOG.error( - "Fail to check whether {} is the metalake admin {} due to storage issues", user, ioe); - throw new RuntimeException(ioe); - } - } - - private NameIdentifier ofMetalakeAdmin(String user) { - return NameIdentifier.of( - Entity.SYSTEM_METALAKE_RESERVED_NAME, - Entity.AUTHORIZATION_CATALOG_NAME, - Entity.ADMIN_SCHEMA_NAME, - user); - } -} diff --git a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java index e0a6e3835..8035f303f 100644 --- a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java +++ b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java @@ -204,27 +204,6 @@ public class TestAccessControlManager { Assertions.assertFalse(removed1); } - @Test - public void testMetalakeAdmin() { - User user = accessControlManager.addMetalakeAdmin("test"); - Assertions.assertEquals("test", user.name()); - Assertions.assertTrue(user.roles().isEmpty()); - Assertions.assertTrue(accessControlManager.isMetalakeAdmin("test")); - - // Test with UserAlreadyExistsException - Assertions.assertThrows( - UserAlreadyExistsException.class, () -> accessControlManager.addMetalakeAdmin("test")); - - // Test to remove admin - boolean removed = accessControlManager.removeMetalakeAdmin("test"); - Assertions.assertTrue(removed); - Assertions.assertFalse(accessControlManager.isMetalakeAdmin("test")); - - // Test to remove non-existed admin - boolean removed1 = accessControlManager.removeMetalakeAdmin("no-exist"); - Assertions.assertFalse(removed1); - } - @Test public void testServiceAdmin() { Assertions.assertTrue(accessControlManager.isServiceAdmin("admin1")); diff --git a/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java b/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java index fb12c184b..d1ec13c7e 100644 --- a/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java +++ b/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java @@ -30,11 +30,15 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import org.apache.gravitino.GravitinoEnv; +import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.authorization.AccessControlManager; +import org.apache.gravitino.authorization.AuthorizationUtils; import org.apache.gravitino.dto.requests.GroupAddRequest; import org.apache.gravitino.dto.responses.GroupResponse; import org.apache.gravitino.dto.responses.RemoveResponse; import org.apache.gravitino.dto.util.DTOConverters; +import org.apache.gravitino.lock.LockType; +import org.apache.gravitino.lock.TreeLockUtils; import org.apache.gravitino.metrics.MetricNames; import org.apache.gravitino.server.authorization.NameBindings; import org.apache.gravitino.server.web.Utils; @@ -69,9 +73,14 @@ public class GroupOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new GroupResponse( - DTOConverters.toDTO(accessControlManager.getGroup(metalake, group))))); + TreeLockUtils.doWithTreeLock( + AuthorizationUtils.ofGroup(metalake, group), + LockType.READ, + () -> + Utils.ok( + new GroupResponse( + DTOConverters.toDTO( + accessControlManager.getGroup(metalake, group)))))); } catch (Exception e) { return ExceptionHandlers.handleGroupException(OperationType.GET, group, metalake, e); } @@ -86,10 +95,14 @@ public class GroupOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new GroupResponse( - DTOConverters.toDTO( - accessControlManager.addGroup(metalake, request.getName()))))); + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofGroupNamespace(metalake).levels()), + LockType.WRITE, + () -> + Utils.ok( + new GroupResponse( + DTOConverters.toDTO( + accessControlManager.addGroup(metalake, request.getName())))))); } catch (Exception e) { return ExceptionHandlers.handleGroupException( OperationType.ADD, request.getName(), metalake, e); @@ -107,7 +120,11 @@ public class GroupOperations { return Utils.doAs( httpRequest, () -> { - boolean removed = accessControlManager.removeGroup(metalake, group); + boolean removed = + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofGroupNamespace(metalake).levels()), + LockType.WRITE, + () -> accessControlManager.removeGroup(metalake, group)); if (!removed) { LOG.warn("Failed to remove group {} under metalake {}", group, metalake); } diff --git a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeAdminOperations.java b/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeAdminOperations.java deleted file mode 100644 index 0b4aa1547..000000000 --- a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeAdminOperations.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.gravitino.server.web.rest; - -import com.codahale.metrics.annotation.ResponseMetered; -import com.codahale.metrics.annotation.Timed; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import org.apache.gravitino.GravitinoEnv; -import org.apache.gravitino.authorization.AccessControlManager; -import org.apache.gravitino.dto.requests.UserAddRequest; -import org.apache.gravitino.dto.responses.RemoveResponse; -import org.apache.gravitino.dto.responses.UserResponse; -import org.apache.gravitino.dto.util.DTOConverters; -import org.apache.gravitino.metrics.MetricNames; -import org.apache.gravitino.server.authorization.NameBindings; -import org.apache.gravitino.server.web.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@NameBindings.AccessControlInterfaces -@Path("/admins") -public class MetalakeAdminOperations { - private static final Logger LOG = LoggerFactory.getLogger(MetalakeAdminOperations.class); - - private final AccessControlManager accessControlManager; - - @Context private HttpServletRequest httpRequest; - - public MetalakeAdminOperations() { - // Because accessManager may be null when Gravitino doesn't enable authorization, - // and Jersey injection doesn't support null value. So MetalakeAdminOperations chooses to - // retrieve accessControlManager from GravitinoEnv instead of injection here. - this.accessControlManager = GravitinoEnv.getInstance().accessControlManager(); - } - - @POST - @Produces("application/vnd.gravitino.v1+json") - @Timed(name = "add-admin." + MetricNames.HTTP_PROCESS_DURATION, absolute = true) - @ResponseMetered(name = "add-admin", absolute = true) - public Response addAdmin(UserAddRequest request) { - - try { - return Utils.doAs( - httpRequest, - () -> - Utils.ok( - new UserResponse( - DTOConverters.toDTO( - accessControlManager.addMetalakeAdmin(request.getName()))))); - } catch (Exception e) { - return ExceptionHandlers.handleUserException(OperationType.ADD, request.getName(), null, e); - } - } - - @DELETE - @Path("{user}") - @Produces("application/vnd.gravitino.v1+json") - @Timed(name = "remove-admin." + MetricNames.HTTP_PROCESS_DURATION, absolute = true) - @ResponseMetered(name = "remove-admin", absolute = true) - public Response removeAdmin(@PathParam("user") String user) { - try { - return Utils.doAs( - httpRequest, - () -> { - boolean removed = accessControlManager.removeMetalakeAdmin(user); - if (!removed) { - LOG.warn("Failed to remove metalake admin user {}", user); - } - return Utils.ok(new RemoveResponse(removed)); - }); - } catch (Exception e) { - return ExceptionHandlers.handleUserException(OperationType.REMOVE, user, null, e); - } - } -} diff --git a/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java b/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java index bc0decee9..c27791be5 100644 --- a/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java +++ b/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java @@ -29,12 +29,16 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; import org.apache.gravitino.GravitinoEnv; +import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.authorization.AccessControlManager; +import org.apache.gravitino.authorization.AuthorizationUtils; import org.apache.gravitino.dto.requests.RoleGrantRequest; import org.apache.gravitino.dto.requests.RoleRevokeRequest; import org.apache.gravitino.dto.responses.GroupResponse; import org.apache.gravitino.dto.responses.UserResponse; import org.apache.gravitino.dto.util.DTOConverters; +import org.apache.gravitino.lock.LockType; +import org.apache.gravitino.lock.TreeLockUtils; import org.apache.gravitino.metrics.MetricNames; import org.apache.gravitino.server.web.Utils; @@ -65,11 +69,19 @@ public class PermissionOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new UserResponse( - DTOConverters.toDTO( - accessControlManager.grantRolesToUser( - metalake, request.getRoleNames(), user))))); + TreeLockUtils.doWithTreeLock( + AuthorizationUtils.ofUser(metalake, user), + LockType.WRITE, + () -> + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofRoleNamespace(metalake).levels()), + LockType.READ, + () -> + Utils.ok( + new UserResponse( + DTOConverters.toDTO( + accessControlManager.grantRolesToUser( + metalake, request.getRoleNames(), user))))))); } catch (Exception e) { return ExceptionHandlers.handleUserPermissionOperationException( OperationType.GRANT, StringUtils.join(request.getRoleNames(), ","), user, e); @@ -89,11 +101,19 @@ public class PermissionOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new GroupResponse( - DTOConverters.toDTO( - accessControlManager.grantRolesToGroup( - metalake, request.getRoleNames(), group))))); + TreeLockUtils.doWithTreeLock( + AuthorizationUtils.ofGroup(metalake, group), + LockType.WRITE, + () -> + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofRoleNamespace(metalake).levels()), + LockType.READ, + () -> + Utils.ok( + new GroupResponse( + DTOConverters.toDTO( + accessControlManager.grantRolesToGroup( + metalake, request.getRoleNames(), group))))))); } catch (Exception e) { return ExceptionHandlers.handleGroupPermissionOperationException( OperationType.GRANT, StringUtils.join(request.getRoleNames(), ","), group, e); @@ -113,11 +133,19 @@ public class PermissionOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new UserResponse( - DTOConverters.toDTO( - accessControlManager.revokeRolesFromUser( - metalake, request.getRoleNames(), user))))); + TreeLockUtils.doWithTreeLock( + AuthorizationUtils.ofUser(metalake, user), + LockType.WRITE, + () -> + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofRoleNamespace(metalake).levels()), + LockType.READ, + () -> + Utils.ok( + new UserResponse( + DTOConverters.toDTO( + accessControlManager.revokeRolesFromUser( + metalake, request.getRoleNames(), user))))))); } catch (Exception e) { return ExceptionHandlers.handleUserPermissionOperationException( OperationType.REVOKE, StringUtils.join(request.getRoleNames(), ","), user, e); @@ -137,11 +165,19 @@ public class PermissionOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new GroupResponse( - DTOConverters.toDTO( - accessControlManager.revokeRolesFromGroup( - metalake, request.getRoleNames(), group))))); + TreeLockUtils.doWithTreeLock( + AuthorizationUtils.ofGroup(metalake, group), + LockType.WRITE, + () -> + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofRoleNamespace(metalake).levels()), + LockType.READ, + () -> + Utils.ok( + new GroupResponse( + DTOConverters.toDTO( + accessControlManager.revokeRolesFromGroup( + metalake, request.getRoleNames(), group))))))); } catch (Exception e) { return ExceptionHandlers.handleGroupPermissionOperationException( OperationType.REVOKE, StringUtils.join(request.getRoleNames()), group, e); diff --git a/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java b/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java index fdd4a0afb..66393a0ea 100644 --- a/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java +++ b/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java @@ -37,6 +37,7 @@ import org.apache.gravitino.MetadataObject; import org.apache.gravitino.MetadataObjects; import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.authorization.AccessControlManager; +import org.apache.gravitino.authorization.AuthorizationUtils; import org.apache.gravitino.authorization.Privilege; import org.apache.gravitino.authorization.Privileges; import org.apache.gravitino.authorization.SecurableObject; @@ -75,9 +76,13 @@ public class RoleOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new RoleResponse( - DTOConverters.toDTO(accessControlManager.getRole(metalake, role))))); + TreeLockUtils.doWithTreeLock( + AuthorizationUtils.ofRole(metalake, role), + LockType.READ, + () -> + Utils.ok( + new RoleResponse( + DTOConverters.toDTO(accessControlManager.getRole(metalake, role)))))); } catch (Exception e) { return ExceptionHandlers.handleRoleException(OperationType.GET, role, metalake, e); } @@ -118,14 +123,18 @@ public class RoleOperations { .collect(Collectors.toList()))) .collect(Collectors.toList()); - return Utils.ok( - new RoleResponse( - DTOConverters.toDTO( - accessControlManager.createRole( - metalake, - request.getName(), - request.getProperties(), - securableObjects)))); + return TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofRoleNamespace(metalake).levels()), + LockType.WRITE, + () -> + Utils.ok( + new RoleResponse( + DTOConverters.toDTO( + accessControlManager.createRole( + metalake, + request.getName(), + request.getProperties(), + securableObjects))))); }); } catch (Exception e) { @@ -145,7 +154,11 @@ public class RoleOperations { return Utils.doAs( httpRequest, () -> { - boolean deleted = accessControlManager.deleteRole(metalake, role); + boolean deleted = + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofRoleNamespace(metalake).levels()), + LockType.WRITE, + () -> accessControlManager.deleteRole(metalake, role)); if (!deleted) { LOG.warn("Failed to delete role {} under metalake {}", role, metalake); } diff --git a/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java b/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java index 4203cd510..7b63082f4 100644 --- a/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java +++ b/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java @@ -30,11 +30,15 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import org.apache.gravitino.GravitinoEnv; +import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.authorization.AccessControlManager; +import org.apache.gravitino.authorization.AuthorizationUtils; import org.apache.gravitino.dto.requests.UserAddRequest; import org.apache.gravitino.dto.responses.RemoveResponse; import org.apache.gravitino.dto.responses.UserResponse; import org.apache.gravitino.dto.util.DTOConverters; +import org.apache.gravitino.lock.LockType; +import org.apache.gravitino.lock.TreeLockUtils; import org.apache.gravitino.metrics.MetricNames; import org.apache.gravitino.server.authorization.NameBindings; import org.apache.gravitino.server.web.Utils; @@ -68,9 +72,13 @@ public class UserOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new UserResponse( - DTOConverters.toDTO(accessControlManager.getUser(metalake, user))))); + TreeLockUtils.doWithTreeLock( + AuthorizationUtils.ofGroup(metalake, user), + LockType.READ, + () -> + Utils.ok( + new UserResponse( + DTOConverters.toDTO(accessControlManager.getUser(metalake, user)))))); } catch (Exception e) { return ExceptionHandlers.handleUserException(OperationType.GET, user, metalake, e); } @@ -85,10 +93,14 @@ public class UserOperations { return Utils.doAs( httpRequest, () -> - Utils.ok( - new UserResponse( - DTOConverters.toDTO( - accessControlManager.addUser(metalake, request.getName()))))); + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofGroupNamespace(metalake).levels()), + LockType.WRITE, + () -> + Utils.ok( + new UserResponse( + DTOConverters.toDTO( + accessControlManager.addUser(metalake, request.getName())))))); } catch (Exception e) { return ExceptionHandlers.handleUserException( OperationType.ADD, request.getName(), metalake, e); @@ -106,7 +118,11 @@ public class UserOperations { return Utils.doAs( httpRequest, () -> { - boolean removed = accessControlManager.removeUser(metalake, user); + boolean removed = + TreeLockUtils.doWithTreeLock( + NameIdentifier.of(AuthorizationUtils.ofGroupNamespace(metalake).levels()), + LockType.WRITE, + () -> accessControlManager.removeUser(metalake, user)); if (!removed) { LOG.warn("Failed to remove user {} under metalake {}", user, metalake); } diff --git a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeAdminOperations.java b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeAdminOperations.java deleted file mode 100644 index c7b074e26..000000000 --- a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeAdminOperations.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.gravitino.server.web.rest; - -import static org.apache.gravitino.Configs.TREE_LOCK_CLEAN_INTERVAL; -import static org.apache.gravitino.Configs.TREE_LOCK_MAX_NODE_IN_MEMORY; -import static org.apache.gravitino.Configs.TREE_LOCK_MIN_NODE_IN_MEMORY; -import static org.mockito.ArgumentMatchers.any; - -import java.io.IOException; -import java.time.Instant; -import java.util.Collections; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.client.Entity; -import javax.ws.rs.core.Application; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import org.apache.commons.lang3.reflect.FieldUtils; -import org.apache.gravitino.Config; -import org.apache.gravitino.GravitinoEnv; -import org.apache.gravitino.authorization.AccessControlManager; -import org.apache.gravitino.authorization.User; -import org.apache.gravitino.dto.authorization.UserDTO; -import org.apache.gravitino.dto.requests.UserAddRequest; -import org.apache.gravitino.dto.responses.ErrorConstants; -import org.apache.gravitino.dto.responses.ErrorResponse; -import org.apache.gravitino.dto.responses.RemoveResponse; -import org.apache.gravitino.dto.responses.UserResponse; -import org.apache.gravitino.exceptions.UserAlreadyExistsException; -import org.apache.gravitino.lock.LockManager; -import org.apache.gravitino.meta.AuditInfo; -import org.apache.gravitino.meta.UserEntity; -import org.apache.gravitino.rest.RESTUtils; -import org.glassfish.hk2.utilities.binding.AbstractBinder; -import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.test.JerseyTest; -import org.glassfish.jersey.test.TestProperties; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestMetalakeAdminOperations extends JerseyTest { - - private static final AccessControlManager manager = Mockito.mock(AccessControlManager.class); - - private static class MockServletRequestFactory extends ServletRequestFactoryBase { - @Override - public HttpServletRequest get() { - HttpServletRequest request = Mockito.mock(HttpServletRequest.class); - Mockito.when(request.getRemoteUser()).thenReturn(null); - return request; - } - } - - @BeforeAll - public static void setup() throws IllegalAccessException { - Config config = Mockito.mock(Config.class); - Mockito.doReturn(100000L).when(config).get(TREE_LOCK_MAX_NODE_IN_MEMORY); - Mockito.doReturn(1000L).when(config).get(TREE_LOCK_MIN_NODE_IN_MEMORY); - Mockito.doReturn(36000L).when(config).get(TREE_LOCK_CLEAN_INTERVAL); - FieldUtils.writeField(GravitinoEnv.getInstance(), "lockManager", new LockManager(config), true); - FieldUtils.writeField(GravitinoEnv.getInstance(), "accessControlManager", manager, true); - } - - @Override - protected Application configure() { - try { - forceSet( - TestProperties.CONTAINER_PORT, String.valueOf(RESTUtils.findAvailablePort(2000, 3000))); - } catch (IOException e) { - throw new RuntimeException(e); - } - - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.register(MetalakeAdminOperations.class); - resourceConfig.register( - new AbstractBinder() { - @Override - protected void configure() { - bindFactory(MockServletRequestFactory.class).to(HttpServletRequest.class); - } - }); - - return resourceConfig; - } - - @Test - public void testAddMetalakeAdmin() { - UserAddRequest req = new UserAddRequest("user1"); - User user = buildUser("user1"); - - Mockito.when(manager.addMetalakeAdmin(any())).thenReturn(user); - - Response resp = - target("/admins") - .request(MediaType.APPLICATION_JSON_TYPE) - .accept("application/vnd.gravitino.v1+json") - .post(Entity.entity(req, MediaType.APPLICATION_JSON_TYPE)); - - Assertions.assertEquals(Response.Status.OK.getStatusCode(), resp.getStatus()); - Assertions.assertEquals(MediaType.APPLICATION_JSON_TYPE, resp.getMediaType()); - - UserResponse userResponse = resp.readEntity(UserResponse.class); - Assertions.assertEquals(0, userResponse.getCode()); - - UserDTO userDTO = userResponse.getUser(); - Assertions.assertEquals("user1", userDTO.name()); - Assertions.assertNotNull(userDTO.roles()); - Assertions.assertTrue(userDTO.roles().isEmpty()); - - // Test to throw UserAlreadyExistsException - Mockito.doThrow(new UserAlreadyExistsException("mock error")) - .when(manager) - .addMetalakeAdmin(any()); - Response resp2 = - target("/admins") - .request(MediaType.APPLICATION_JSON_TYPE) - .accept("application/vnd.gravitino.v1+json") - .post(Entity.entity(req, MediaType.APPLICATION_JSON_TYPE)); - - Assertions.assertEquals(Response.Status.CONFLICT.getStatusCode(), resp2.getStatus()); - - ErrorResponse errorResponse1 = resp2.readEntity(ErrorResponse.class); - Assertions.assertEquals(ErrorConstants.ALREADY_EXISTS_CODE, errorResponse1.getCode()); - Assertions.assertEquals( - UserAlreadyExistsException.class.getSimpleName(), errorResponse1.getType()); - - // Test to throw internal RuntimeException - Mockito.doThrow(new RuntimeException("mock error")).when(manager).addMetalakeAdmin(any()); - Response resp3 = - target("/admins") - .request(MediaType.APPLICATION_JSON_TYPE) - .accept("application/vnd.gravitino.v1+json") - .post(Entity.entity(req, MediaType.APPLICATION_JSON_TYPE)); - - Assertions.assertEquals( - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), resp3.getStatus()); - - ErrorResponse errorResponse2 = resp3.readEntity(ErrorResponse.class); - Assertions.assertEquals(ErrorConstants.INTERNAL_ERROR_CODE, errorResponse2.getCode()); - Assertions.assertEquals(RuntimeException.class.getSimpleName(), errorResponse2.getType()); - } - - private User buildUser(String user) { - return UserEntity.builder() - .withId(1L) - .withName(user) - .withRoleNames(Collections.emptyList()) - .withAuditInfo( - AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build()) - .build(); - } - - @Test - public void testRemoveMetalakeAdmin() { - Mockito.when(manager.removeMetalakeAdmin(any())).thenReturn(true); - - Response resp = - target("/admins/user1") - .request(MediaType.APPLICATION_JSON_TYPE) - .accept("application/vnd.gravitino.v1+json") - .delete(); - - Assertions.assertEquals(Response.Status.OK.getStatusCode(), resp.getStatus()); - RemoveResponse removeResponse = resp.readEntity(RemoveResponse.class); - Assertions.assertEquals(0, removeResponse.getCode()); - Assertions.assertTrue(removeResponse.removed()); - - // Test when failed to remove user - Mockito.when(manager.removeMetalakeAdmin(any())).thenReturn(false); - Response resp2 = - target("/admins/user1") - .request(MediaType.APPLICATION_JSON_TYPE) - .accept("application/vnd.gravitino.v1+json") - .delete(); - - Assertions.assertEquals(Response.Status.OK.getStatusCode(), resp2.getStatus()); - RemoveResponse removeResponse2 = resp2.readEntity(RemoveResponse.class); - Assertions.assertEquals(0, removeResponse2.getCode()); - Assertions.assertFalse(removeResponse2.removed()); - - Mockito.doThrow(new RuntimeException("mock error")).when(manager).removeMetalakeAdmin(any()); - Response resp3 = - target("/admins/user1") - .request(MediaType.APPLICATION_JSON_TYPE) - .accept("application/vnd.gravitino.v1+json") - .delete(); - - Assertions.assertEquals( - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), resp3.getStatus()); - - ErrorResponse errorResponse = resp3.readEntity(ErrorResponse.class); - Assertions.assertEquals(ErrorConstants.INTERNAL_ERROR_CODE, errorResponse.getCode()); - Assertions.assertEquals(RuntimeException.class.getSimpleName(), errorResponse.getType()); - } -}