diff options
4 files changed, 49 insertions, 12 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockUserManagement.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockUserManagement.java index 468e03bab13..6d579980846 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockUserManagement.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockUserManagement.java @@ -98,4 +98,9 @@ public class MockUserManagement extends AbstractComponent implements UserManagem .filter(user -> user.email().equals(email)) .findFirst(); } + + @Override + public List<User> findUsers(String query) { + return List.of(); + } } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java index db0b66bc9db..60c717d989b 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java @@ -44,4 +44,7 @@ public interface UserManagement { /** Find a user with all attributes */ Optional<User> findUser(String email); + + /** Find all users from the database given query */ + List<User> findUsers(String query); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java index 5bdb84bb6dd..dddc3a47d69 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java @@ -101,7 +101,7 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { private HttpResponse handleGET(Path path, HttpRequest request) { if (path.matches("/user/v1/user")) return userMetadata(request); - if (path.matches("/user/v1/find")) return userMetadataFromUserId(request.getProperty("email")); + if (path.matches("/user/v1/find")) return findUser(request); if (path.matches("/user/v1/tenant/{tenant}")) return listTenantRoleMembers(path.get("tenant")); if (path.matches("/user/v1/tenant/{tenant}/application/{application}")) return listApplicationRoleMembers(path.get("tenant"), path.get("application")); @@ -134,18 +134,44 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { RoleDefinition.hostedSupporter, RoleDefinition.hostedAccountant); + private HttpResponse findUser(HttpRequest request) { + var email = request.getProperty("email"); + var query = request.getProperty("query"); + if (email != null) return userMetadataFromUserId(email); + if (query != null) return userMetadataQuery(query); + return ErrorResponse.badRequest("Need 'email' or 'query' parameter"); + } + private HttpResponse userMetadataFromUserId(String email) { var maybeUser = users.findUser(email); if (maybeUser.isPresent()) { var user = maybeUser.get(); var roles = users.listRoles(new UserId(user.email())); - return renderUserMetaData(user, Set.copyOf(roles)); + var slime = new Slime(); + var root = slime.setObject(); + var usersRoot = root.setArray("users"); + renderUserMetaData(usersRoot.addObject(), user, Set.copyOf(roles)); + return new SlimeJsonResponse(slime); } return ErrorResponse.notFoundError("Could not find user: " + email); } + private HttpResponse userMetadataQuery(String query) { + var userList = users.findUsers(query); + var slime = new Slime(); + var root = slime.setObject(); + var userSlime = root.setArray("users"); + + for (var user : userList) { + var roles = users.listRoles(new UserId((user.email()))); + renderUserMetaData(userSlime.addObject(), user, Set.copyOf(roles)); + } + + return new SlimeJsonResponse(slime); + } + private HttpResponse userMetadata(HttpRequest request) { User user; if (request.getJDiscRequest().context().get(User.ATTRIBUTE_NAME) instanceof User) { @@ -159,10 +185,12 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { Set<Role> roles = getAttribute(request, SecurityContext.ATTRIBUTE_NAME, SecurityContext.class).roles(); - return renderUserMetaData(user, roles); + var slime = new Slime(); + renderUserMetaData(slime.setObject(), user, roles); + return new SlimeJsonResponse(slime); } - private HttpResponse renderUserMetaData(User user, Set<Role> roles) { + private void renderUserMetaData(Cursor root, User user, Set<Role> roles) { Map<TenantName, List<TenantRole>> tenantRolesByTenantName = roles.stream() .flatMap(role -> filterTenantRoles(role).stream()) .distinct() @@ -175,9 +203,6 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { .sorted(Comparator.comparing(Role::definition)) .toList(); - Slime slime = new Slime(); - Cursor root = slime.setObject(); - root.setBool("isPublic", controller.system().isPublic()); root.setBool("isCd", controller.system().isCd()); root.setBool("hasTrialCapacity", hasTrialCapacity()); @@ -202,8 +227,6 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { } UserFlagsSerializer.toSlime(root, flagsDb.getAllFlagData(), tenantRolesByTenantName.keySet(), !operatorRoles.isEmpty(), user.email()); - - return new SlimeJsonResponse(slime); } private HttpResponse listTenantRoleMembers(String tenantName) { @@ -273,7 +296,7 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { var user = new UserId(require("user", Inspector::asString, requestObject)); var roles = SlimeStream.fromArray(requestObject.field("roles"), Inspector::asString) .map(roleName -> Roles.toRole(tenant, roleName)) - .collect(Collectors.toUnmodifiableList()); + .toList(); users.addToRoles(user, roles); return new MessageResponse(user + " is now a member of " + roles.stream().map(Role::toString).collect(Collectors.joining(", "))); @@ -285,7 +308,7 @@ public class UserApiHandler extends ThreadedHttpRequestHandler { var user = new UserId(require("user", Inspector::asString, requestObject)); var roles = SlimeStream.fromArray(requestObject.field("roles"), Inspector::asString) .map(roleName -> Roles.toRole(tenant, roleName)) - .collect(Collectors.toUnmodifiableList()); + .toList(); enforceLastAdminOfTenant(tenant, user, roles); removeDeveloperKey(tenant, user, roles); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java index efb06dd5c7d..b573940d150 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java @@ -271,7 +271,13 @@ public class UserApiTest extends ControllerContainerCloudTest { .roles(operator) .user(user), """ - {"isPublic":true,"isCd":false,"hasTrialCapacity":true,"user":{"name":"dev@domail","email":"dev@domail","verified":false},"tenants":{"scoober":{"supported":false,"roles":["developer"]}},"flags":[{"id":"enable-public-signup-flow","rules":[{"value":false}]}]}"""); + {"users":[{"isPublic":true,"isCd":false,"hasTrialCapacity":true,"user":{"name":"dev@domail","email":"dev@domail","verified":false},"tenants":{"scoober":{"supported":false,"roles":["developer"]}},"flags":[{"id":"enable-public-signup-flow","rules":[{"value":false}]}]}]}"""); + + tester.assertResponse(request("/user/v1/find?query=email:dev@domail") + .roles(operator) + .user(user), + """ + {"users":[]}"""); } } |