Skip to content

Commit

Permalink
Add ListRoles
Browse files Browse the repository at this point in the history
  • Loading branch information
jerqi committed Sep 9, 2024
1 parent 6c0ecf5 commit b2d206d
Show file tree
Hide file tree
Showing 19 changed files with 505 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,26 @@ public User getUser(String user) throws NoSuchUserException, NoSuchMetalakeExcep
return getMetalake().getUser(user);
}

/**
* Lists the users.
*
* @return The User list.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
*/
public User[] listUsers() {
return getMetalake().listUsers();
}

/**
* Lists the usernames.
*
* @return The username list.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
*/
public String[] listUserNames() {
return getMetalake().listUserNames();
}

/**
* Adds a new Group.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import org.apache.gravitino.dto.responses.SetResponse;
import org.apache.gravitino.dto.responses.TagListResponse;
import org.apache.gravitino.dto.responses.TagResponse;
import org.apache.gravitino.dto.responses.UserListResponse;
import org.apache.gravitino.dto.responses.UserResponse;
import org.apache.gravitino.exceptions.CatalogAlreadyExistsException;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
Expand Down Expand Up @@ -515,6 +516,46 @@ public User getUser(String user) throws NoSuchUserException, NoSuchMetalakeExcep
return resp.getUser();
}

/**
* Lists the users.
*
* @return The User list.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
*/
public User[] listUsers() throws NoSuchMetalakeException {
Map<String, String> params = new HashMap<>();
params.put("details", "true");

UserListResponse resp =
restClient.get(
String.format(API_METALAKES_USERS_PATH, name(), BLANK_PLACE_HOLDER),
params,
UserListResponse.class,
Collections.emptyMap(),
ErrorHandlers.userErrorHandler());
resp.validate();

return resp.getUsers();
}

/**
* Lists the usernames.
*
* @return The username list.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
*/
public String[] listUserNames() throws NoSuchMetalakeException {
NameListResponse resp =
restClient.get(
String.format(API_METALAKES_USERS_PATH, name(), BLANK_PLACE_HOLDER),
NameListResponse.class,
Collections.emptyMap(),
ErrorHandlers.userErrorHandler());
resp.validate();

return resp.getNames();
}

/**
* Adds a new Group.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import static org.apache.hc.core5.http.HttpStatus.SC_SERVER_ERROR;

import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import org.apache.gravitino.authorization.Group;
import org.apache.gravitino.authorization.User;
import org.apache.gravitino.dto.AuditDTO;
Expand All @@ -35,7 +37,9 @@
import org.apache.gravitino.dto.responses.ErrorResponse;
import org.apache.gravitino.dto.responses.GroupResponse;
import org.apache.gravitino.dto.responses.MetalakeResponse;
import org.apache.gravitino.dto.responses.NameListResponse;
import org.apache.gravitino.dto.responses.RemoveResponse;
import org.apache.gravitino.dto.responses.UserListResponse;
import org.apache.gravitino.dto.responses.UserResponse;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
Expand Down Expand Up @@ -175,6 +179,56 @@ public void testRemoveUsers() throws Exception {
Assertions.assertThrows(RuntimeException.class, () -> gravitinoClient.removeUser(username));
}

@Test
public void testListUserNames() throws Exception {
String userPath = withSlash(String.format(API_METALAKES_USERS_PATH, metalakeName, ""));

NameListResponse listResponse = new NameListResponse(new String[] {"user1", "user2"});
buildMockResource(Method.GET, userPath, null, listResponse, SC_OK);

Assertions.assertArrayEquals(new String[] {"user1", "user2"}, gravitinoClient.listUserNames());

ErrorResponse errRespNoMetalake =
ErrorResponse.notFound(NoSuchMetalakeException.class.getSimpleName(), "metalake not found");
buildMockResource(Method.GET, userPath, null, errRespNoMetalake, SC_NOT_FOUND);
Exception ex =
Assertions.assertThrows(
NoSuchMetalakeException.class, () -> gravitinoClient.listUserNames());
Assertions.assertEquals("metalake not found", ex.getMessage());

// Test RuntimeException
ErrorResponse errResp = ErrorResponse.internalError("internal error");
buildMockResource(Method.GET, userPath, null, errResp, SC_SERVER_ERROR);
Assertions.assertThrows(RuntimeException.class, () -> gravitinoClient.listUserNames());
}

@Test
public void testListUsers() throws Exception {
String userPath = withSlash(String.format(API_METALAKES_USERS_PATH, metalakeName, ""));
UserDTO user1 = mockUserDTO("user1");
UserDTO user2 = mockUserDTO("user2");
Map<String, String> params = Collections.singletonMap("details", "true");
UserListResponse listResponse = new UserListResponse(new UserDTO[] {user1, user2});
buildMockResource(Method.GET, userPath, params, null, listResponse, SC_OK);

User[] users = gravitinoClient.listUsers();
Assertions.assertEquals(2, users.length);
assertUser(user1, users[0]);
assertUser(user2, users[1]);

ErrorResponse errRespNoMetalake =
ErrorResponse.notFound(NoSuchMetalakeException.class.getSimpleName(), "metalake not found");
buildMockResource(Method.GET, userPath, params, null, errRespNoMetalake, SC_NOT_FOUND);
Exception ex =
Assertions.assertThrows(NoSuchMetalakeException.class, () -> gravitinoClient.listUsers());
Assertions.assertEquals("metalake not found", ex.getMessage());

// Test RuntimeException
ErrorResponse errResp = ErrorResponse.internalError("internal error");
buildMockResource(Method.GET, userPath, params, null, errResp, SC_SERVER_ERROR);
Assertions.assertThrows(RuntimeException.class, () -> gravitinoClient.listUsers());
}

@Test
public void testAddGroups() throws Exception {
String groupName = "group";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.gravitino.Configs;
import org.apache.gravitino.auth.AuthConstants;
import org.apache.gravitino.authorization.Group;
Expand Down Expand Up @@ -73,11 +75,27 @@ void testManageUsers() {
Assertions.assertEquals(username, user.name());
Assertions.assertTrue(user.roles().isEmpty());

// List users
String anotherUser = "another-user";
metalake.addUser(anotherUser);
String[] usernames = metalake.listUserNames();
Assertions.assertEquals(
Lists.newArrayList(AuthConstants.ANONYMOUS_USER, anotherUser, username),
Arrays.asList(usernames));
User[] users = metalake.listUsers();
Assertions.assertEquals(
Lists.newArrayList(AuthConstants.ANONYMOUS_USER, anotherUser, username),
Arrays.stream(users).map(User::name).collect(Collectors.toList()));

// Get a not-existed user
Assertions.assertThrows(NoSuchUserException.class, () -> metalake.getUser("not-existed"));

Assertions.assertTrue(metalake.removeUser(username));

Assertions.assertFalse(metalake.removeUser(username));

// clean up
metalake.removeUser(anotherUser);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -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.dto.responses;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.apache.gravitino.dto.authorization.UserDTO;

/** Represents a response containing a list of users. */
@Getter
@ToString
@EqualsAndHashCode(callSuper = true)
public class UserListResponse extends BaseResponse {

@JsonProperty("users")
private final UserDTO[] users;

/**
* Constructor for UserListResponse.
*
* @param users The array of users.
*/
public UserListResponse(UserDTO[] users) {
super(0);
this.users = users;
}

/**
* This is the constructor that is used by Jackson deserializer to create an instance of
* UserListResponse.
*/
public UserListResponse() {
super(0);
this.users = null;
}

/**
* Validates the response data.
*
* @throws IllegalArgumentException if users are not set.
*/
@Override
public void validate() throws IllegalArgumentException {
super.validate();
Preconditions.checkArgument(users != null, "users must not be null");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,19 @@ public static CatalogDTO[] toDTOs(Catalog[] catalogs) {
return Arrays.stream(catalogs).map(DTOConverters::toDTO).toArray(CatalogDTO[]::new);
}

/**
* Converts an array of Users to an array of UserDTOs.
*
* @param users The users to be converted.
* @return The array of UserDTOs.
*/
public static UserDTO[] toDTOs(User[] users) {
if (ArrayUtils.isEmpty(users)) {
return new UserDTO[0];
}
return Arrays.stream(users).map(DTOConverters::toDTO).toArray(UserDTO[]::new);
}

/**
* Converts a DistributionDTO to a Distribution.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,24 @@ User addUser(String metalake, String user)
*/
User getUser(String metalake, String user) throws NoSuchUserException, NoSuchMetalakeException;

/**
* Lists the users.
*
* @param metalake The Metalake of the User.
* @return The User list.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
*/
User[] listUsers(String metalake) throws NoSuchMetalakeException;

/**
* Lists the usernames.
*
* @param metalake The Metalake of the User.
* @return The username list.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
*/
String[] listUserNames(String metalake) throws NoSuchMetalakeException;

/**
* Adds a new Group.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ public User getUser(String metalake, String user)
}

@Override
public String[] listUserNames(String metalake) throws NoSuchMetalakeException {
return userGroupManager.listUserNames(metalake);
}

@Override
public User[] listUsers(String metalake) throws NoSuchMetalakeException {
return userGroupManager.listUsers(metalake);
}

public Group addGroup(String metalake, String group)
throws GroupAlreadyExistsException, NoSuchMetalakeException {
return userGroupManager.addGroup(metalake, group);
Expand Down Expand Up @@ -130,16 +139,6 @@ public Role getRole(String metalake, String role)
return roleManager.getRole(metalake, role);
}

/**
* Deletes a Role.
*
* @param metalake The Metalake of the Role.
* @param role The name of the Role.
* @return True if the Role was successfully deleted, false only when there's no such role,
* otherwise it will throw an exception.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
* @throws RuntimeException If deleting the Role encounters storage issues.
*/
public boolean deleteRole(String metalake, String role) throws NoSuchMetalakeException {
return roleManager.deleteRole(metalake, role);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
import com.google.common.collect.Lists;
import java.io.IOException;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import org.apache.gravitino.Entity;
import org.apache.gravitino.EntityAlreadyExistsException;
import org.apache.gravitino.EntityStore;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchEntityException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchUserException;
import org.apache.gravitino.exceptions.UserAlreadyExistsException;
import org.apache.gravitino.meta.AuditInfo;
Expand All @@ -46,6 +49,7 @@
class UserGroupManager {

private static final Logger LOG = LoggerFactory.getLogger(UserGroupManager.class);
private static final String METALAKE_DOES_NOT_EXIST_MSG = "Metalake %s does not exist";

private final EntityStore store;
private final IdGenerator idGenerator;
Expand Down Expand Up @@ -109,6 +113,25 @@ User getUser(String metalake, String user) throws NoSuchUserException {
}
}

String[] listUserNames(String metalake) {
return Arrays.stream(listUsers(metalake)).map(User::name).toArray(String[]::new);
}

User[] listUsers(String metalake) {
try {
Namespace namespace = AuthorizationUtils.ofUserNamespace(metalake);
return store.list(namespace, UserEntity.class, Entity.EntityType.USER).stream()
.map(entity -> (User) entity)
.toArray(User[]::new);
} catch (NoSuchEntityException e) {
LOG.warn("Metalake {} does not exist", metalake, e);
throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, metalake);
} catch (IOException ioe) {
LOG.error("Listing user under metalake {} failed due to storage issues", metalake, ioe);
throw new RuntimeException(ioe);
}
}

Group addGroup(String metalake, String group) throws GroupAlreadyExistsException {
try {
AuthorizationUtils.checkMetalakeExists(metalake);
Expand Down
Loading

0 comments on commit b2d206d

Please sign in to comment.