From ad763dd8456e6d8c7d755d74e4a17775bc257f5a Mon Sep 17 00:00:00 2001 From: pfurio Date: Thu, 17 Oct 2024 15:13:38 +0200 Subject: [PATCH 1/2] catalog: allow study administrators to sync external users, #TASK-5688 --- .../app/cli/main/OpenCgaCompleter.java | 2 +- .../app/cli/main/OpencgaCliOptionsParser.java | 1 + .../main/executors/UsersCommandExecutor.java | 35 ++++ .../cli/main/options/AdminCommandOptions.java | 2 +- .../cli/main/options/UsersCommandOptions.java | 34 ++++ .../opencga/catalog/managers/UserManager.java | 184 ++++++++++++++++-- opencga-client/src/main/R/R/Admin-methods.R | 2 +- opencga-client/src/main/R/R/User-methods.R | 7 + .../client/rest/clients/AdminClient.java | 2 +- .../client/rest/clients/UserClient.java | 14 ++ opencga-client/src/main/javascript/Admin.js | 2 +- opencga-client/src/main/javascript/User.js | 8 + .../pyopencga/rest_clients/admin_client.py | 4 +- .../pyopencga/rest_clients/user_client.py | 11 ++ .../opencga/server/rest/UserWSServer.java | 35 ++++ .../server/rest/admin/AdminWSServer.java | 3 +- 16 files changed, 325 insertions(+), 21 deletions(-) diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java index fa5b3482284..35dc8ce5ccd 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpenCgaCompleter.java @@ -69,7 +69,7 @@ public abstract class OpenCgaCompleter implements Completer { .map(Candidate::new) .collect(toList()); - private List usersList = asList( "anonymous","create","login","password","search","info","configs","configs-update","filters","password-reset","update") + private List usersList = asList( "anonymous","create","login","password","search","sync","info","configs","configs-update","filters","password-reset","update") .stream() .map(Candidate::new) .collect(toList()); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java index cfd4cee9919..16e277a9e67 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/OpencgaCliOptionsParser.java @@ -215,6 +215,7 @@ public OpencgaCliOptionsParser() { usersSubCommands.addCommand("login", usersCommandOptions.loginCommandOptions); usersSubCommands.addCommand("password", usersCommandOptions.passwordCommandOptions); usersSubCommands.addCommand("search", usersCommandOptions.searchCommandOptions); + usersSubCommands.addCommand("sync", usersCommandOptions.syncCommandOptions); usersSubCommands.addCommand("info", usersCommandOptions.infoCommandOptions); usersSubCommands.addCommand("configs", usersCommandOptions.configsCommandOptions); usersSubCommands.addCommand("configs-update", usersCommandOptions.updateConfigsCommandOptions); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/UsersCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/UsersCommandExecutor.java index 5f8dfce148c..a30531d4ae8 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/UsersCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/UsersCommandExecutor.java @@ -18,7 +18,9 @@ import org.opencb.opencga.catalog.utils.ParamUtils.AddRemoveAction; import org.opencb.opencga.client.exceptions.ClientException; import org.opencb.opencga.core.common.JacksonUtils; +import org.opencb.opencga.core.models.admin.GroupSyncParams; import org.opencb.opencga.core.models.common.Enums; +import org.opencb.opencga.core.models.study.Group; import org.opencb.opencga.core.models.user.AuthenticationResponse; import org.opencb.opencga.core.models.user.ConfigUpdateParams; import org.opencb.opencga.core.models.user.FilterUpdateParams; @@ -80,6 +82,9 @@ public void execute() throws Exception { case "search": queryResponse = search(); break; + case "sync": + queryResponse = sync(); + break; case "info": queryResponse = info(); break; @@ -209,6 +214,36 @@ private RestResponse search() throws Exception { return openCGAClient.getUserClient().search(queryParams); } + private RestResponse sync() throws Exception { + logger.debug("Executing sync in Users command line"); + + UsersCommandOptions.SyncCommandOptions commandOptions = usersCommandOptions.syncCommandOptions; + + GroupSyncParams groupSyncParams = null; + if (commandOptions.jsonDataModel) { + RestResponse res = new RestResponse<>(); + res.setType(QueryType.VOID); + PrintUtils.println(getObjectAsJSON(categoryName,"/{apiVersion}/users/sync")); + return res; + } else if (commandOptions.jsonFile != null) { + groupSyncParams = JacksonUtils.getDefaultObjectMapper() + .readValue(new java.io.File(commandOptions.jsonFile), GroupSyncParams.class); + } else { + ObjectMap beanParams = new ObjectMap(); + putNestedIfNotEmpty(beanParams, "authenticationOriginId", commandOptions.authenticationOriginId, true); + putNestedIfNotEmpty(beanParams, "from", commandOptions.from, true); + putNestedIfNotEmpty(beanParams, "to", commandOptions.to, true); + putNestedIfNotEmpty(beanParams, "study", commandOptions.study, true); + putNestedIfNotNull(beanParams, "syncAll", commandOptions.syncAll, true); + putNestedIfNotNull(beanParams, "force", commandOptions.force, true); + + groupSyncParams = JacksonUtils.getDefaultObjectMapper().copy() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .readValue(beanParams.toJson(), GroupSyncParams.class); + } + return openCGAClient.getUserClient().sync(groupSyncParams); + } + private RestResponse info() throws Exception { logger.debug("Executing info in Users command line"); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java index 60684639ca0..d0ab19ef505 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java @@ -248,7 +248,7 @@ public class SearchUsersCommandOptions { } - @Parameters(commandNames = {"users-sync"}, commandDescription ="Synchronise a group of users from an authentication origin with a group in a study from catalog") + @Parameters(commandNames = {"users-sync"}, commandDescription ="[DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog") public class SyncUsersCommandOptions { @ParametersDelegate diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/UsersCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/UsersCommandOptions.java index 66d7cee55a8..3eecd96ccd2 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/UsersCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/UsersCommandOptions.java @@ -38,6 +38,7 @@ public class UsersCommandOptions extends CustomUsersCommandOptions { public LoginCommandOptions loginCommandOptions; public PasswordCommandOptions passwordCommandOptions; public SearchCommandOptions searchCommandOptions; + public SyncCommandOptions syncCommandOptions; public InfoCommandOptions infoCommandOptions; public ConfigsCommandOptions configsCommandOptions; public UpdateConfigsCommandOptions updateConfigsCommandOptions; @@ -54,6 +55,7 @@ public UsersCommandOptions(CommonCommandOptions commonCommandOptions, JCommander this.loginCommandOptions = new LoginCommandOptions(); this.passwordCommandOptions = new PasswordCommandOptions(); this.searchCommandOptions = new SearchCommandOptions(); + this.syncCommandOptions = new SyncCommandOptions(); this.infoCommandOptions = new InfoCommandOptions(); this.configsCommandOptions = new ConfigsCommandOptions(); this.updateConfigsCommandOptions = new UpdateConfigsCommandOptions(); @@ -170,6 +172,38 @@ public class SearchCommandOptions { } + @Parameters(commandNames = {"sync"}, commandDescription ="Synchronise a group of users from an authentication origin with a group in a study from catalog") + public class SyncCommandOptions { + + @ParametersDelegate + public CommonCommandOptions commonOptions = commonCommandOptions; + + @Parameter(names = {"--json-file"}, description = "File with the body data in JSON format. Note, that using this parameter will ignore all the other parameters.", required = false, arity = 1) + public String jsonFile; + + @Parameter(names = {"--json-data-model"}, description = "Show example of file structure for body data.", help = true, arity = 0) + public Boolean jsonDataModel = false; + + @Parameter(names = {"--authentication-origin-id"}, description = "The body web service authenticationOriginId parameter", required = false, arity = 1) + public String authenticationOriginId; + + @Parameter(names = {"--from"}, description = "The body web service from parameter", required = false, arity = 1) + public String from; + + @Parameter(names = {"--to"}, description = "The body web service to parameter", required = false, arity = 1) + public String to; + + @Parameter(names = {"--study", "-s"}, description = "The body web service study parameter", required = false, arity = 1) + public String study; + + @Parameter(names = {"--sync-all"}, description = "The body web service syncAll parameter", required = false, help = true, arity = 0) + public boolean syncAll = false; + + @Parameter(names = {"--force"}, description = "The body web service force parameter", required = false, help = true, arity = 0) + public boolean force = false; + + } + @Parameters(commandNames = {"info"}, commandDescription ="Return the user information including its projects and studies") public class InfoCommandOptions { diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/UserManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/UserManager.java index 7e8e8cdffeb..9eae9383b33 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/UserManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/UserManager.java @@ -46,6 +46,7 @@ import org.opencb.opencga.core.models.organizations.Organization; import org.opencb.opencga.core.models.study.Group; import org.opencb.opencga.core.models.study.GroupUpdateParams; +import org.opencb.opencga.core.models.study.Study; import org.opencb.opencga.core.models.user.*; import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.Logger; @@ -302,6 +303,7 @@ public JwtPayload validateToken(String token) throws CatalogException { return jwtPayload; } + @Deprecated public void syncAllUsersOfExternalGroup(String organizationId, String study, String authOrigin, String token) throws CatalogException { if (!OPENCGA.equals(authenticationFactory.getUserId(organizationId, authOrigin, token))) { throw new CatalogAuthorizationException("Only the root user can perform this action"); @@ -365,19 +367,74 @@ public void syncAllUsersOfExternalGroup(String organizationId, String study, Str } } - /** - * Register all the users belonging to a remote group. If internalGroup and study are not null, it will also associate the remote group - * to the internalGroup defined. - * - * @param organizationId Organization id. - * @param authOrigin Authentication origin. - * @param remoteGroup Group name of the remote authentication origin. - * @param internalGroup Group name in Catalog that will be associated to the remote group. - * @param study Study where the internal group will be associated. - * @param sync Boolean indicating whether the remote group will be synced with the internal group or not. - * @param token JWT token. The token should belong to the root user. - * @throws CatalogException If any of the parameters is wrong or there is any internal error. - */ + public void syncAllUsersOfExternalGroup(String studyStr, String authOrigin, String token) throws CatalogException { + JwtPayload tokenPayload = validateToken(token); + CatalogFqn studyFqn = CatalogFqn.extractFqnFromStudy(studyStr, tokenPayload); + Study study = catalogManager.getStudyManager().resolveId(studyFqn, null, tokenPayload); + String organizationId = studyFqn.getOrganizationId(); + String userId = tokenPayload.getUserId(organizationId); + + authorizationManager.checkIsAtLeastStudyAdministrator(organizationId, study.getUid(), userId); + + OpenCGAResult allGroups = catalogManager.getStudyManager().getGroup(studyStr, null, token); + + boolean foundAny = false; + for (Group group : allGroups.getResults()) { + if (group.getSyncedFrom() != null && group.getSyncedFrom().getAuthOrigin().equals(authOrigin)) { + logger.info("Fetching users of group '{}' from authentication origin '{}'", group.getSyncedFrom().getRemoteGroup(), + group.getSyncedFrom().getAuthOrigin()); + foundAny = true; + + List userList; + try { + userList = authenticationFactory.getUsersFromRemoteGroup(organizationId, group.getSyncedFrom().getAuthOrigin(), + group.getSyncedFrom().getRemoteGroup()); + } catch (CatalogException e) { + // There was some kind of issue for which we could not retrieve the group information. + logger.info("Removing all users from group '{}' belonging to group '{}' in the external authentication origin", + group.getId(), group.getSyncedFrom().getAuthOrigin()); + logger.info("Please, manually remove group '{}' if external group '{}' was removed from the authentication origin", + group.getId(), group.getSyncedFrom().getAuthOrigin()); + catalogManager.getStudyManager().updateGroup(studyStr, group.getId(), ParamUtils.BasicUpdateAction.SET, + new GroupUpdateParams(Collections.emptyList()), token); + continue; + } + Iterator iterator = userList.iterator(); + while (iterator.hasNext()) { + User user = iterator.next(); + user.setOrganization(organizationId); + try { + create(user, null, token); + logger.info("User '{}' ({}) successfully created", user.getId(), user.getName()); + } catch (CatalogParameterException e) { + logger.warn("Could not create user '{}' ({}). {}", user.getId(), user.getName(), e.getMessage()); + iterator.remove(); + } catch (CatalogException e) { + if (!e.getMessage().contains("already exists")) { + logger.warn("Could not create user '{}' ({}). {}", user.getId(), user.getName(), e.getMessage()); + iterator.remove(); + } + } + } + + GroupUpdateParams updateParams; + if (ListUtils.isEmpty(userList)) { + logger.info("No members associated to the external group"); + updateParams = new GroupUpdateParams(Collections.emptyList()); + } else { + logger.info("Associating members to the internal OpenCGA group"); + updateParams = new GroupUpdateParams(new ArrayList<>(userList.stream().map(User::getId).collect(Collectors.toSet()))); + } + catalogManager.getStudyManager().updateGroup(studyStr, group.getId(), ParamUtils.BasicUpdateAction.SET, + updateParams, token); + } + } + if (!foundAny) { + logger.info("No synced groups found in study '{}' from authentication origin '{}'", studyStr, authOrigin); + } + } + + @Deprecated public void importRemoteGroupOfUsers(String organizationId, String authOrigin, String remoteGroup, @Nullable String internalGroup, @Nullable String study, boolean sync, String token) throws CatalogException { JwtPayload payload = validateToken(token); @@ -454,6 +511,107 @@ public void importRemoteGroupOfUsers(String organizationId, String authOrigin, S } } + /** + * Register all the users belonging to a remote group. If internalGroup and study are not null, it will also associate the remote group + * to the internalGroup defined. + * + * @param authOrigin Authentication origin. + * @param remoteGroup Group name of the remote authentication origin. + * @param internalGroup Group name in Catalog that will be associated to the remote group. + * @param studyStr Study where the internal group will be associated. + * @param sync Boolean indicating whether the remote group will be synced with the internal group or not. + * @param token JWT token. The token should belong to the root user. + * @throws CatalogException If any of the parameters is wrong or there is any internal error. + */ + public void importRemoteGroupOfUsers(String authOrigin, String remoteGroup, @Nullable String internalGroup, + @Nullable String studyStr, boolean sync, String token) throws CatalogException { + JwtPayload tokenPayload = validateToken(token); + String organizationId; + String userId; + Study study = null; + if (StringUtils.isNotEmpty(studyStr)) { + CatalogFqn studyFqn = CatalogFqn.extractFqnFromStudy(studyStr, tokenPayload); + study = catalogManager.getStudyManager().resolveId(studyFqn, null, tokenPayload); + organizationId = studyFqn.getOrganizationId(); + userId = tokenPayload.getUserId(organizationId); + } else { + organizationId = tokenPayload.getOrganization(); + userId = tokenPayload.getUserId(); + } + + ObjectMap auditParams = new ObjectMap() + .append("organizationId", organizationId) + .append("authOrigin", authOrigin) + .append("remoteGroup", remoteGroup) + .append("internalGroup", internalGroup) + .append("study", studyStr) + .append("sync", sync) + .append("token", token); + try { + if (studyStr != null) { + authorizationManager.checkIsAtLeastStudyAdministrator(organizationId, study.getUid(), userId); + } else { + authorizationManager.checkIsAtLeastOrganizationOwnerOrAdmin(organizationId, userId); + } + + ParamUtils.checkParameter(authOrigin, "Authentication origin"); + ParamUtils.checkParameter(remoteGroup, "Remote group"); + + List userList; + if (sync) { + // We don't create any user as they will be automatically populated during login + userList = Collections.emptyList(); + } else { + logger.info("Fetching users from authentication origin '{}'", authOrigin); + + // Register the users + userList = authenticationFactory.getUsersFromRemoteGroup(organizationId, authOrigin, remoteGroup); + for (User user : userList) { + user.setOrganization(organizationId); + try { + create(user, null, token); + logger.info("User '{}' successfully created", user.getId()); + } catch (CatalogException e) { + logger.warn("{}", e.getMessage()); + } + } + } + + if (StringUtils.isNotEmpty(internalGroup) && StringUtils.isNotEmpty(studyStr)) { + // Check if the group already exists + OpenCGAResult groupResult = catalogManager.getStudyManager().getGroup(studyStr, internalGroup, token); + if (groupResult.getNumResults() == 1) { + logger.error("Cannot synchronise with group {}. The group already exists and is already in use.", internalGroup); + throw new CatalogException("Cannot synchronise with group " + internalGroup + + ". The group already exists and is already in use."); + } + + // Create new group associating it to the remote group + try { + logger.info("Attempting to register group '{}' in study '{}'", internalGroup, studyStr); + Group.Sync groupSync = null; + if (sync) { + groupSync = new Group.Sync(authOrigin, remoteGroup); + } + Group group = new Group(internalGroup, userList.stream().map(User::getId).collect(Collectors.toList())) + .setSyncedFrom(groupSync); + catalogManager.getStudyManager().createGroup(studyStr, group, token); + logger.info("Group '{}' created and synchronised with external group", internalGroup); + auditManager.audit(organizationId, userId, Enums.Action.IMPORT_EXTERNAL_GROUP_OF_USERS, Enums.Resource.USER, + group.getId(), "", studyStr, "", auditParams, new AuditRecord.Status(AuditRecord.Status.Result.SUCCESS)); + } catch (CatalogException e) { + logger.error("Could not register group '{}' in study '{}'\n{}", internalGroup, studyStr, e.getMessage(), e); + throw new CatalogException("Could not register group '" + internalGroup + "' in study '" + studyStr + "': " + + e.getMessage(), e); + } + } + } catch (CatalogException e) { + auditManager.audit(organizationId, userId, Enums.Action.IMPORT_EXTERNAL_GROUP_OF_USERS, Enums.Resource.USER, "", "", "", "", + auditParams, new AuditRecord.Status(AuditRecord.Status.Result.ERROR, e.getError())); + throw e; + } + } + /** * Register all the ids. If internalGroup and study are not null, it will also associate the users to the internalGroup defined. * diff --git a/opencga-client/src/main/R/R/Admin-methods.R b/opencga-client/src/main/R/R/Admin-methods.R index d63a1ad3478..1e0d83ec53d 100644 --- a/opencga-client/src/main/R/R/Admin-methods.R +++ b/opencga-client/src/main/R/R/Admin-methods.R @@ -101,7 +101,7 @@ setMethod("adminClient", "OpencgaR", function(OpencgaR, user, endpointName, para subcategoryId=NULL, action="search", params=params, httpMethod="GET", as.queryParam=NULL, ...), #' @section Endpoint /{apiVersion}/admin/users/sync: - #' Synchronise a group of users from an authentication origin with a group in a study from catalog. + #' [DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog. #' @param organization Organization id. #' @param data JSON containing the parameters. syncUsers=fetchOpenCGA(object=OpencgaR, category="admin", categoryId=NULL, subcategory="users", diff --git a/opencga-client/src/main/R/R/User-methods.R b/opencga-client/src/main/R/R/User-methods.R index ffc1c599ca9..54dc587c346 100644 --- a/opencga-client/src/main/R/R/User-methods.R +++ b/opencga-client/src/main/R/R/User-methods.R @@ -24,6 +24,7 @@ #' | login | /{apiVersion}/users/login | body | #' | password | /{apiVersion}/users/password | body[*] | #' | search | /{apiVersion}/users/search | include, exclude, limit, skip, count, organization, id, authenticationId | +#' | sync | /{apiVersion}/users/sync | body[*] | #' | info | /{apiVersion}/users/{users}/info | include, exclude, organization, users[*] | #' | configs | /{apiVersion}/users/{user}/configs | user[*], name | #' | updateConfigs | /{apiVersion}/users/{user}/configs/update | user[*], action, body[*] | @@ -80,6 +81,12 @@ setMethod("userClient", "OpencgaR", function(OpencgaR, filterId, user, users, en search=fetchOpenCGA(object=OpencgaR, category="users", categoryId=NULL, subcategory=NULL, subcategoryId=NULL, action="search", params=params, httpMethod="GET", as.queryParam=NULL, ...), + #' @section Endpoint /{apiVersion}/users/sync: + #' Synchronise a group of users from an authentication origin with a group in a study from catalog. + #' @param data JSON containing the parameters. + sync=fetchOpenCGA(object=OpencgaR, category="users", categoryId=NULL, subcategory=NULL, subcategoryId=NULL, + action="sync", params=params, httpMethod="POST", as.queryParam=NULL, ...), + #' @section Endpoint /{apiVersion}/users/{users}/info: #' Return the user information including its projects and studies. #' @param include Fields included in the response, whole JSON path must be provided. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java index 928fd361341..efbd7283fea 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java @@ -161,7 +161,7 @@ public RestResponse searchUsers(ObjectMap params) throws ClientException } /** - * Synchronise a group of users from an authentication origin with a group in a study from catalog. + * [DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog. * @param data JSON containing the parameters. * @param params Map containing any of the following optional parameters. * organization: Organization id. diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java index ee290b40d21..d80dd1a5d3e 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/UserClient.java @@ -20,6 +20,8 @@ import org.opencb.opencga.client.config.ClientConfiguration; import org.opencb.opencga.client.exceptions.ClientException; import org.opencb.opencga.client.rest.*; +import org.opencb.opencga.core.models.admin.GroupSyncParams; +import org.opencb.opencga.core.models.study.Group; import org.opencb.opencga.core.models.user.AuthenticationResponse; import org.opencb.opencga.core.models.user.ConfigUpdateParams; import org.opencb.opencga.core.models.user.FilterUpdateParams; @@ -121,6 +123,18 @@ public RestResponse search(ObjectMap params) throws ClientException { return execute("users", null, null, null, "search", params, GET, User.class); } + /** + * Synchronise a group of users from an authentication origin with a group in a study from catalog. + * @param data JSON containing the parameters. + * @return a RestResponse object. + * @throws ClientException ClientException if there is any server error. + */ + public RestResponse sync(GroupSyncParams data) throws ClientException { + ObjectMap params = new ObjectMap(); + params.put("body", data); + return execute("users", null, null, null, "sync", params, POST, Group.class); + } + /** * Return the user information including its projects and studies. * @param users Comma separated list of user IDs. diff --git a/opencga-client/src/main/javascript/Admin.js b/opencga-client/src/main/javascript/Admin.js index 3f6204d4165..a616597343e 100644 --- a/opencga-client/src/main/javascript/Admin.js +++ b/opencga-client/src/main/javascript/Admin.js @@ -116,7 +116,7 @@ export default class Admin extends OpenCGAParentClass { return this._get("admin", null, "users", null, "search", params); } - /** Synchronise a group of users from an authentication origin with a group in a study from catalog + /** [DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog * @param {Object} data - JSON containing the parameters. * @param {Object} [params] - The Object containing the following optional parameters: * @param {String} [params.organization] - Organization id. diff --git a/opencga-client/src/main/javascript/User.js b/opencga-client/src/main/javascript/User.js index 32b28182c79..366a84e6254 100644 --- a/opencga-client/src/main/javascript/User.js +++ b/opencga-client/src/main/javascript/User.js @@ -84,6 +84,14 @@ export default class User extends OpenCGAParentClass { return this._get("users", null, null, null, "search", params); } + /** Synchronise a group of users from an authentication origin with a group in a study from catalog + * @param {Object} data - JSON containing the parameters. + * @returns {Promise} Promise object in the form of RestResponse instance. + */ + sync(data) { + return this._post("users", null, null, null, "sync", data); + } + /** Return the user information including its projects and studies * @param {String} users - Comma separated list of user IDs. * @param {Object} [params] - The Object containing the following optional parameters: diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py index 7311fa7e5dd..1bc291c6e89 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py @@ -122,8 +122,8 @@ def search_users(self, **options): def sync_users(self, data=None, **options): """ - Synchronise a group of users from an authentication origin with a - group in a study from catalog. + [DEPRECATED] Synchronise a group of users from an authentication + origin with a group in a study from catalog. PATH: /{apiVersion}/admin/users/sync :param dict data: JSON containing the parameters. (REQUIRED) diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py index 93451319d3d..7848b47e358 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/user_client.py @@ -84,6 +84,17 @@ def search(self, **options): return self._get(category='users', resource='search', **options) + def sync(self, data=None, **options): + """ + Synchronise a group of users from an authentication origin with a + group in a study from catalog. + PATH: /{apiVersion}/users/sync + + :param dict data: JSON containing the parameters. (REQUIRED) + """ + + return self._post(category='users', resource='sync', data=data, **options) + def info(self, users, **options): """ Return the user information including its projects and studies. diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/UserWSServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/UserWSServer.java index 31fcd3a9720..b61a50f1d5a 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/UserWSServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/UserWSServer.java @@ -25,6 +25,8 @@ import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.exceptions.VersionException; +import org.opencb.opencga.core.models.admin.GroupSyncParams; +import org.opencb.opencga.core.models.study.Group; import org.opencb.opencga.core.models.user.*; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.tools.annotations.*; @@ -115,6 +117,39 @@ public Response getInfo( } } + @POST + @Path("/sync") + @ApiOperation(value = "Synchronise a group of users from an authentication origin with a group in a study from catalog", response = Group.class, + notes = "Mandatory fields: authOriginId, study
" + + "
    " + + "
  • authOriginId: Authentication origin id defined in the main Catalog configuration.
  • " + + "
  • study: Study [[organization@]project:]study where the list of users will be associated to.
  • " + + "
  • from: Group defined in the authenticated origin to be synchronised.
  • " + + "
  • to: Group in a study that will be synchronised.
  • " + + "
  • syncAll: Flag indicating whether to synchronise all the groups present in the study with" + + " their corresponding authenticated groups automatically. --from and --to parameters will not be needed when the flag" + + "is active..
  • " + + "
  • force: Boolean to force the synchronisation with already existing Catalog groups that are not yet " + + "synchronised with any other group.
  • " + + "
" + ) + public Response externalSync( + @ApiParam(value = "JSON containing the parameters", required = true) GroupSyncParams syncParams + ) { + try { + // TODO: These two methods should return an OpenCGAResult containing at least the number of changes + if (syncParams.isSyncAll()) { + catalogManager.getUserManager().syncAllUsersOfExternalGroup(syncParams.getStudy(), syncParams.getAuthenticationOriginId(), token); + } else { + catalogManager.getUserManager().importRemoteGroupOfUsers(syncParams.getAuthenticationOriginId(), syncParams.getFrom(), + syncParams.getTo(), syncParams.getStudy(), true, token); + } + return createOkResponse(OpenCGAResult.empty(Group.class)); + } catch (Exception e) { + return createErrorResponse(e); + } + } + @POST @Path("/login") @Consumes(MediaType.APPLICATION_JSON) diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java index c0345d421f5..2c2e962751a 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java @@ -180,9 +180,10 @@ public Response updateGroups( } } + @Deprecated @POST @Path("/users/sync") - @ApiOperation(value = "Synchronise a group of users from an authentication origin with a group in a study from catalog", response = Group.class, + @ApiOperation(value = "[DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog", response = Group.class, notes = "Mandatory fields: authOriginId, study
" + "
    " + "
  • authOriginId: Authentication origin id defined in the main Catalog configuration.
  • " From c45bbe8b660d99079bc17f24d179c17cf5febdac Mon Sep 17 00:00:00 2001 From: pfurio Date: Thu, 17 Oct 2024 15:56:10 +0200 Subject: [PATCH 2/2] server: add where the deprecated code was moved, #TASK-5688 --- .../opencga/app/cli/main/options/AdminCommandOptions.java | 2 +- opencga-client/src/main/R/R/Admin-methods.R | 2 +- .../org/opencb/opencga/client/rest/clients/AdminClient.java | 2 +- opencga-client/src/main/javascript/Admin.js | 2 +- .../src/main/python/pyopencga/rest_clients/admin_client.py | 3 +-- .../org/opencb/opencga/server/rest/admin/AdminWSServer.java | 5 +++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java index d0ab19ef505..8b41e55359c 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/AdminCommandOptions.java @@ -248,7 +248,7 @@ public class SearchUsersCommandOptions { } - @Parameters(commandNames = {"users-sync"}, commandDescription ="[DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog") + @Parameters(commandNames = {"users-sync"}, commandDescription ="[DEPRECATED] Moved to /users/sync") public class SyncUsersCommandOptions { @ParametersDelegate diff --git a/opencga-client/src/main/R/R/Admin-methods.R b/opencga-client/src/main/R/R/Admin-methods.R index 1e0d83ec53d..c6f87df5923 100644 --- a/opencga-client/src/main/R/R/Admin-methods.R +++ b/opencga-client/src/main/R/R/Admin-methods.R @@ -101,7 +101,7 @@ setMethod("adminClient", "OpencgaR", function(OpencgaR, user, endpointName, para subcategoryId=NULL, action="search", params=params, httpMethod="GET", as.queryParam=NULL, ...), #' @section Endpoint /{apiVersion}/admin/users/sync: - #' [DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog. + #' [DEPRECATED] Moved to /users/sync. #' @param organization Organization id. #' @param data JSON containing the parameters. syncUsers=fetchOpenCGA(object=OpencgaR, category="admin", categoryId=NULL, subcategory="users", diff --git a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java index efbd7283fea..2e2127bc499 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/AdminClient.java @@ -161,7 +161,7 @@ public RestResponse searchUsers(ObjectMap params) throws ClientException } /** - * [DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog. + * [DEPRECATED] Moved to /users/sync. * @param data JSON containing the parameters. * @param params Map containing any of the following optional parameters. * organization: Organization id. diff --git a/opencga-client/src/main/javascript/Admin.js b/opencga-client/src/main/javascript/Admin.js index a616597343e..97cad09bd3d 100644 --- a/opencga-client/src/main/javascript/Admin.js +++ b/opencga-client/src/main/javascript/Admin.js @@ -116,7 +116,7 @@ export default class Admin extends OpenCGAParentClass { return this._get("admin", null, "users", null, "search", params); } - /** [DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog + /** [DEPRECATED] Moved to /users/sync * @param {Object} data - JSON containing the parameters. * @param {Object} [params] - The Object containing the following optional parameters: * @param {String} [params.organization] - Organization id. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py index 1bc291c6e89..1f4d81be29b 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/admin_client.py @@ -122,8 +122,7 @@ def search_users(self, **options): def sync_users(self, data=None, **options): """ - [DEPRECATED] Synchronise a group of users from an authentication - origin with a group in a study from catalog. + [DEPRECATED] Moved to /users/sync. PATH: /{apiVersion}/admin/users/sync :param dict data: JSON containing the parameters. (REQUIRED) diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java index 2c2e962751a..bc0b23b20c0 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/admin/AdminWSServer.java @@ -183,8 +183,9 @@ public Response updateGroups( @Deprecated @POST @Path("/users/sync") - @ApiOperation(value = "[DEPRECATED] Synchronise a group of users from an authentication origin with a group in a study from catalog", response = Group.class, - notes = "Mandatory fields: authOriginId, study
    " + @ApiOperation(value = "[DEPRECATED] Moved to /users/sync", response = Group.class, + notes = "Synchronise a group of users from an authentication origin with a group in a study from catalog.
    " + + "Mandatory fields: authOriginId, study
    " + "
      " + "
    • authOriginId: Authentication origin id defined in the main Catalog configuration.
    • " + "
    • study: Study [[organization@]project:]study where the list of users will be associated to.
    • "