diff --git a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/OpenCGATestExternalResource.java b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/OpenCGATestExternalResource.java index 1f05dfc5dd3..5c5956d7cf7 100644 --- a/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/OpenCGATestExternalResource.java +++ b/opencga-analysis/src/test/java/org/opencb/opencga/analysis/variant/OpenCGATestExternalResource.java @@ -214,8 +214,6 @@ public Path isolateOpenCGA() throws IOException { Files.createDirectories(conf); Files.createDirectories(userHome); - catalogManagerExternalResource.getConfiguration().getAdmin().setSecretKey(null); - catalogManagerExternalResource.getConfiguration().getAdmin().setAlgorithm(null); catalogManagerExternalResource.getConfiguration().serialize( new FileOutputStream(conf.resolve("configuration.yml").toFile())); InputStream inputStream = StorageManager.class.getClassLoader().getResourceAsStream("storage-configuration.yml"); diff --git a/opencga-app/app/cloud/docker/opencga-init/override_yaml.py b/opencga-app/app/cloud/docker/opencga-init/override_yaml.py index 1993f85232b..04bd8139e82 100644 --- a/opencga-app/app/cloud/docker/opencga-init/override_yaml.py +++ b/opencga-app/app/cloud/docker/opencga-init/override_yaml.py @@ -19,9 +19,6 @@ parser.add_argument("--catalog-database-authentication-database", required=False, default="admin") parser.add_argument("--catalog-database-authentication-mechanism", required=False) parser.add_argument("--catalog-database-replica-set", required=False) -parser.add_argument("--catalog-search-hosts", required=True) -parser.add_argument("--catalog-search-user", required=False) -parser.add_argument("--catalog-search-password", required=False) parser.add_argument("--rest-host", required=True) parser.add_argument("--grpc-host", required=True) parser.add_argument("--analysis-execution-mode", required=False) @@ -138,13 +135,6 @@ def hostOverride(conf,hosts_var): if args.catalog_database_authentication_mechanism is not None: config["catalog"]["database"]["options"]["authenticationMechanism"] = args.catalog_database_authentication_mechanism -# Inject search database -hostOverride(config["catalog"]["searchEngine"], args.catalog_search_hosts) - -if args.catalog_search_user is not None: - config["catalog"]["searchEngine"]["user"] = args.catalog_search_user - config["catalog"]["searchEngine"]["password"] = args.catalog_search_password - # Inject execution settings config["analysis"]["scratchDir"] = "/tmp/opencga_scratch" if args.max_concurrent_jobs is not None: diff --git a/opencga-app/app/cloud/docker/opencga-init/test/test_override_yaml.py b/opencga-app/app/cloud/docker/opencga-init/test/test_override_yaml.py index 9d28765d399..ee6a547ea2b 100644 --- a/opencga-app/app/cloud/docker/opencga-init/test/test_override_yaml.py +++ b/opencga-app/app/cloud/docker/opencga-init/test/test_override_yaml.py @@ -1,11 +1,10 @@ +import os import subprocess -from shutil import copyfile +import sys import unittest import yaml from io import StringIO -import sys -import os - +from shutil import copyfile os.chdir(sys.path[0]) @@ -40,9 +39,6 @@ def test_end_2_end(self): "--catalog-database-hosts", "test-catalog-database-host1,test-catalog-database-host2,test-catalog-database-host3", "--catalog-database-user", "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", "test-catalog-search-user", - "--catalog-search-password", "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", "test-grpc-host", "--max-concurrent-jobs", "25", @@ -170,18 +166,6 @@ def test_end_2_end(self): self.assertEqual(config["catalog"]["database"]["options"]["sslEnabled"], True) self.assertEqual(config["catalog"]["database"]["options"]["sslInvalidCertificatesAllowed"], True) self.assertEqual(config["catalog"]["database"]["options"]["authenticationDatabase"], "admin") - self.assertEqual( - config["catalog"]["searchEngine"]["hosts"][0], "test-catalog-search-host1" - ) - self.assertEqual( - config["catalog"]["searchEngine"]["hosts"][1], "test-catalog-search-host2" - ) - self.assertEqual( - config["catalog"]["searchEngine"]["user"], "test-catalog-search-user" - ) - self.assertEqual( - config["catalog"]["searchEngine"]["password"], "test-catalog-search-password" - ) self.assertEqual(config["analysis"]["execution"]["id"], "test-analysis-execution-mode") self.assertEqual(config["analysis"]["execution"]["maxConcurrentJobs"]["variant-index"], 25) self.assertEqual(client_config["rest"]["hosts"][0]["url"], "test-rest-host") @@ -200,9 +184,6 @@ def test_azure_batch_execution(self): "--catalog-database-hosts", "test-catalog-database-host1,test-catalog-database-host2,test-catalog-database-host3", "--catalog-database-user", "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", "test-catalog-search-user", - "--catalog-search-password", "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", "test-grpc-host", "--analysis-execution-mode", "AZURE", @@ -273,9 +254,6 @@ def test_kubernetes_execution(self): "--catalog-database-hosts", "test-catalog-database-host1,test-catalog-database-host2,test-catalog-database-host3", "--catalog-database-user", "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", "test-catalog-search-user", - "--catalog-search-password", "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", "test-grpc-host", "--analysis-execution-mode", "k8s", @@ -357,12 +335,6 @@ def test_cellbasedb_with_empty_hosts(self): "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", - "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", - "test-catalog-search-user", - "--catalog-search-password", - "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", @@ -437,12 +409,6 @@ def test_cellbasedb_with_no_db_hosts(self): "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", - "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", - "test-catalog-search-user", - "--catalog-search-password", - "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", @@ -519,12 +485,6 @@ def test_cellbase_rest_set(self): "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", - "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", - "test-catalog-search-user", - "--catalog-search-password", - "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", @@ -604,12 +564,6 @@ def test_cellbase_rest_empty_set(self): "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", - "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", - "test-catalog-search-user", - "--catalog-search-password", - "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", @@ -688,12 +642,6 @@ def test_cellbase_rest_not_set(self): "test-catalog-database-user", "--catalog-database-password", "test-catalog-database-password", - "--catalog-search-hosts", - "test-catalog-search-host1,test-catalog-search-host2", - "--catalog-search-user", - "test-catalog-search-user", - "--catalog-search-password", - "test-catalog-search-password", "--rest-host", "test-rest-host", "--grpc-host", diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/AdminCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/AdminCommandExecutor.java index 9dd3c922c14..f4106569f24 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/AdminCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/AdminCommandExecutor.java @@ -22,7 +22,6 @@ import org.opencb.opencga.app.cli.CommandExecutor; import org.opencb.opencga.app.cli.admin.AdminCliOptionsParser; import org.opencb.opencga.catalog.exceptions.CatalogException; -import org.opencb.opencga.core.config.Admin; import java.util.Collections; @@ -82,9 +81,6 @@ protected void setCatalogDatabaseCredentials(String host, String prefix, String configuration.getCatalog().getDatabase().setUser(user); } - if (configuration.getAdmin() == null) { - configuration.setAdmin(new Admin()); - } if (StringUtils.isNotEmpty(password)) { configuration.getCatalog().getDatabase().setPassword(password); } diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/CatalogCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/CatalogCommandExecutor.java index 5520de81126..090802f3741 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/CatalogCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/admin/executors/CatalogCommandExecutor.java @@ -23,13 +23,11 @@ import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.mongodb.MongoDataStore; import org.opencb.opencga.app.cli.admin.AdminCliOptionsParser; -import org.opencb.opencga.catalog.auth.authentication.JwtManager; import org.opencb.opencga.catalog.db.mongodb.MongoDBAdaptorFactory; import org.opencb.opencga.catalog.db.mongodb.MongoDBUtils; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.managers.CatalogManager; import org.opencb.opencga.core.common.JacksonUtils; -import org.opencb.opencga.core.common.PasswordUtils; import org.opencb.opencga.master.monitor.MonitorService; import javax.ws.rs.client.Client; @@ -173,19 +171,12 @@ private void install() throws CatalogException { validateConfiguration(commandOptions); - this.configuration.getAdmin().setAlgorithm("HS256"); - - this.configuration.getAdmin().setSecretKey(commandOptions.secretKey); - if (StringUtils.isEmpty(configuration.getAdmin().getSecretKey())) { - configuration.getAdmin().setSecretKey(PasswordUtils.getStrongRandomPassword(JwtManager.SECRET_KEY_MIN_LENGTH)); - } - if (StringUtils.isEmpty(commandOptions.commonOptions.adminPassword)) { throw new CatalogException("No admin password found. Please, insert your password."); } try (CatalogManager catalogManager = new CatalogManager(configuration)) { - catalogManager.installCatalogDB("HS256", configuration.getAdmin().getSecretKey(), commandOptions.commonOptions.adminPassword, + catalogManager.installCatalogDB("HS256", commandOptions.secretKey, commandOptions.commonOptions.adminPassword, commandOptions.email, commandOptions.force); } } 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 d4ff662a7c3..217d898fcba 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 @@ -89,7 +89,7 @@ public abstract class OpenCgaCompleter implements Completer { .map(Candidate::new) .collect(toList()); - private List organizationsList = asList( "create","notes-create","notes-search","notes-delete","notes-update","configuration-update","info","update") + private List organizationsList = asList( "create","notes-create","notes-search","notes-delete","notes-update","update-status-user","user-update","configuration-update","info","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 e0b9f455343..a171446b03a 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 @@ -272,6 +272,8 @@ public OpencgaCliOptionsParser() { organizationsSubCommands.addCommand("notes-search", organizationsCommandOptions.searchNotesCommandOptions); organizationsSubCommands.addCommand("notes-delete", organizationsCommandOptions.deleteNotesCommandOptions); organizationsSubCommands.addCommand("notes-update", organizationsCommandOptions.updateNotesCommandOptions); + organizationsSubCommands.addCommand("update-status-user", organizationsCommandOptions.userUpdateStatusCommandOptions); + organizationsSubCommands.addCommand("user-update", organizationsCommandOptions.updateUserCommandOptions); organizationsSubCommands.addCommand("configuration-update", organizationsCommandOptions.updateConfigurationCommandOptions); organizationsSubCommands.addCommand("info", organizationsCommandOptions.infoCommandOptions); organizationsSubCommands.addCommand("update", organizationsCommandOptions.updateCommandOptions); diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java index a74a27358a5..c0ce6156d34 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/executors/OrganizationsCommandExecutor.java @@ -24,6 +24,10 @@ import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.models.organizations.TokenConfiguration; +import org.opencb.opencga.core.models.user.OrganizationUserUpdateParams; +import org.opencb.opencga.core.models.user.User; +import org.opencb.opencga.core.models.user.UserQuota; +import org.opencb.opencga.core.models.user.UserStatusUpdateParams; import org.opencb.opencga.core.response.QueryType; import org.opencb.opencga.core.response.RestResponse; @@ -76,6 +80,12 @@ public void execute() throws Exception { case "notes-update": queryResponse = updateNotes(); break; + case "update-status-user": + queryResponse = userUpdateStatus(); + break; + case "user-update": + queryResponse = updateUser(); + break; case "configuration-update": queryResponse = updateConfiguration(); break; @@ -120,6 +130,7 @@ private RestResponse create() throws Exception { putNestedIfNotEmpty(beanParams, "name",commandOptions.name, true); putNestedIfNotEmpty(beanParams, "creationDate",commandOptions.creationDate, true); putNestedIfNotEmpty(beanParams, "modificationDate",commandOptions.modificationDate, true); + putNestedIfNotEmpty(beanParams, "configuration.defaultUserExpirationDate",commandOptions.configurationDefaultUserExpirationDate, true); putNestedIfNotNull(beanParams, "attributes",commandOptions.attributes, true); organizationCreateParams = JacksonUtils.getDefaultObjectMapper().copy() @@ -227,6 +238,77 @@ private RestResponse updateNotes() throws Exception { return openCGAClient.getOrganizationClient().updateNotes(commandOptions.id, noteUpdateParams, queryParams); } + private RestResponse userUpdateStatus() throws Exception { + logger.debug("Executing userUpdateStatus in Organizations command line"); + + OrganizationsCommandOptions.UserUpdateStatusCommandOptions commandOptions = organizationsCommandOptions.userUpdateStatusCommandOptions; + + ObjectMap queryParams = new ObjectMap(); + queryParams.putIfNotEmpty("include", commandOptions.include); + queryParams.putIfNotEmpty("exclude", commandOptions.exclude); + queryParams.putIfNotEmpty("organization", commandOptions.organization); + queryParams.putIfNotNull("includeResult", commandOptions.includeResult); + + + UserStatusUpdateParams userStatusUpdateParams = null; + if (commandOptions.jsonDataModel) { + RestResponse res = new RestResponse<>(); + res.setType(QueryType.VOID); + PrintUtils.println(getObjectAsJSON(categoryName,"/{apiVersion}/organizations/user/{user}/status/update")); + return res; + } else if (commandOptions.jsonFile != null) { + userStatusUpdateParams = JacksonUtils.getDefaultObjectMapper() + .readValue(new java.io.File(commandOptions.jsonFile), UserStatusUpdateParams.class); + } else { + ObjectMap beanParams = new ObjectMap(); + putNestedIfNotEmpty(beanParams, "status",commandOptions.status, true); + + userStatusUpdateParams = JacksonUtils.getDefaultObjectMapper().copy() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .readValue(beanParams.toJson(), UserStatusUpdateParams.class); + } + return openCGAClient.getOrganizationClient().userUpdateStatus(commandOptions.user, userStatusUpdateParams, queryParams); + } + + private RestResponse updateUser() throws Exception { + logger.debug("Executing updateUser in Organizations command line"); + + OrganizationsCommandOptions.UpdateUserCommandOptions commandOptions = organizationsCommandOptions.updateUserCommandOptions; + + ObjectMap queryParams = new ObjectMap(); + queryParams.putIfNotEmpty("include", commandOptions.include); + queryParams.putIfNotEmpty("exclude", commandOptions.exclude); + queryParams.putIfNotEmpty("organization", commandOptions.organization); + queryParams.putIfNotNull("includeResult", commandOptions.includeResult); + + + OrganizationUserUpdateParams organizationUserUpdateParams = null; + if (commandOptions.jsonDataModel) { + RestResponse res = new RestResponse<>(); + res.setType(QueryType.VOID); + PrintUtils.println(getObjectAsJSON(categoryName,"/{apiVersion}/organizations/user/{user}/update")); + return res; + } else if (commandOptions.jsonFile != null) { + organizationUserUpdateParams = JacksonUtils.getDefaultObjectMapper() + .readValue(new java.io.File(commandOptions.jsonFile), OrganizationUserUpdateParams.class); + } else { + ObjectMap beanParams = new ObjectMap(); + putNestedIfNotEmpty(beanParams, "name",commandOptions.name, true); + putNestedIfNotEmpty(beanParams, "email",commandOptions.email, true); + putNestedIfNotNull(beanParams, "quota.diskUsage",commandOptions.quotaDiskUsage, true); + putNestedIfNotNull(beanParams, "quota.cpuUsage",commandOptions.quotaCpuUsage, true); + putNestedIfNotNull(beanParams, "quota.maxDisk",commandOptions.quotaMaxDisk, true); + putNestedIfNotNull(beanParams, "quota.maxCpu",commandOptions.quotaMaxCpu, true); + putNestedIfNotEmpty(beanParams, "account.expirationDate",commandOptions.accountExpirationDate, true); + putNestedIfNotNull(beanParams, "attributes",commandOptions.attributes, true); + + organizationUserUpdateParams = JacksonUtils.getDefaultObjectMapper().copy() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .readValue(beanParams.toJson(), OrganizationUserUpdateParams.class); + } + return openCGAClient.getOrganizationClient().updateUser(commandOptions.user, organizationUserUpdateParams, queryParams); + } + private RestResponse updateConfiguration() throws Exception { logger.debug("Executing updateConfiguration in Organizations command line"); @@ -250,6 +332,7 @@ private RestResponse updateConfiguration() throws Exc .readValue(new java.io.File(commandOptions.jsonFile), OrganizationConfiguration.class); } else { ObjectMap beanParams = new ObjectMap(); + putNestedIfNotEmpty(beanParams, "defaultUserExpirationDate",commandOptions.defaultUserExpirationDate, true); putNestedIfNotNull(beanParams, "optimizations.simplifyPermissions",commandOptions.optimizationsSimplifyPermissions, true); putNestedIfNotEmpty(beanParams, "token.algorithm",commandOptions.tokenAlgorithm, true); putNestedIfNotEmpty(beanParams, "token.secretKey",commandOptions.tokenSecretKey, true); 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 6eeeae17408..4883cedef5c 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 @@ -305,7 +305,6 @@ private RestResponse update() throws Exception { ObjectMap beanParams = new ObjectMap(); putNestedIfNotEmpty(beanParams, "name",commandOptions.name, true); putNestedIfNotEmpty(beanParams, "email",commandOptions.email, true); - putNestedIfNotNull(beanParams, "attributes",commandOptions.attributes, true); userUpdateParams = JacksonUtils.getDefaultObjectMapper().copy() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java index 08120624c87..68be35b6cac 100644 --- a/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java +++ b/opencga-app/src/main/java/org/opencb/opencga/app/cli/main/options/OrganizationsCommandOptions.java @@ -38,6 +38,8 @@ public class OrganizationsCommandOptions { public SearchNotesCommandOptions searchNotesCommandOptions; public DeleteNotesCommandOptions deleteNotesCommandOptions; public UpdateNotesCommandOptions updateNotesCommandOptions; + public UserUpdateStatusCommandOptions userUpdateStatusCommandOptions; + public UpdateUserCommandOptions updateUserCommandOptions; public UpdateConfigurationCommandOptions updateConfigurationCommandOptions; public InfoCommandOptions infoCommandOptions; public UpdateCommandOptions updateCommandOptions; @@ -52,6 +54,8 @@ public OrganizationsCommandOptions(CommonCommandOptions commonCommandOptions, JC this.searchNotesCommandOptions = new SearchNotesCommandOptions(); this.deleteNotesCommandOptions = new DeleteNotesCommandOptions(); this.updateNotesCommandOptions = new UpdateNotesCommandOptions(); + this.userUpdateStatusCommandOptions = new UserUpdateStatusCommandOptions(); + this.updateUserCommandOptions = new UpdateUserCommandOptions(); this.updateConfigurationCommandOptions = new UpdateConfigurationCommandOptions(); this.infoCommandOptions = new InfoCommandOptions(); this.updateCommandOptions = new UpdateCommandOptions(); @@ -91,6 +95,9 @@ public class CreateCommandOptions { @Parameter(names = {"--modification-date", "--md"}, description = "Autogenerated date following the format YYYYMMDDhhmmss containing the date when the entry was last modified.", required = false, arity = 1) public String modificationDate; + @Parameter(names = {"--configuration-default-user-expiration-date"}, description = "The body web service defaultUserExpirationDate parameter", required = false, arity = 1) + public String configurationDefaultUserExpirationDate; + @DynamicParameter(names = {"--attributes"}, description = "You can use this field to store any other information, keep in mind this is not indexed so you cannot search by attributes.. Use: --attributes key=value", required = false) public java.util.Map attributes = new HashMap<>(); //Dynamic parameters must be initialized; @@ -218,6 +225,91 @@ public class UpdateNotesCommandOptions { } + @Parameters(commandNames = {"update-status-user"}, commandDescription ="Update the user status") + public class UserUpdateStatusCommandOptions { + + @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 = {"--include", "-I"}, description = "Fields included in the response, whole JSON path must be provided", required = false, arity = 1) + public String include; + + @Parameter(names = {"--exclude", "-E"}, description = "Fields excluded in the response, whole JSON path must be provided", required = false, arity = 1) + public String exclude; + + @Parameter(names = {"--user", "-u"}, description = "User ID", required = true, arity = 1) + public String user; + + @Parameter(names = {"--organization"}, description = "Organization id", required = false, arity = 1) + public String organization; + + @Parameter(names = {"--include-result"}, description = "Flag indicating to include the created or updated document result in the response", required = false, help = true, arity = 0) + public boolean includeResult = false; + + @Parameter(names = {"--status"}, description = "The body web service status parameter", required = false, arity = 1) + public String status; + + } + + @Parameters(commandNames = {"user-update"}, commandDescription ="Update the user information") + public class UpdateUserCommandOptions { + + @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 = {"--include", "-I"}, description = "Fields included in the response, whole JSON path must be provided", required = false, arity = 1) + public String include; + + @Parameter(names = {"--exclude", "-E"}, description = "Fields excluded in the response, whole JSON path must be provided", required = false, arity = 1) + public String exclude; + + @Parameter(names = {"--user", "-u"}, description = "User ID", required = true, arity = 1) + public String user; + + @Parameter(names = {"--organization"}, description = "Organization id", required = false, arity = 1) + public String organization; + + @Parameter(names = {"--include-result"}, description = "Flag indicating to include the created or updated document result in the response", required = false, help = true, arity = 0) + public boolean includeResult = false; + + @Parameter(names = {"--name", "-n"}, description = "The body web service name parameter", required = false, arity = 1) + public String name; + + @Parameter(names = {"--email"}, description = "The body web service email parameter", required = false, arity = 1) + public String email; + + @Parameter(names = {"--quota-disk-usage"}, description = "The body web service diskUsage parameter", required = false, arity = 1) + public Long quotaDiskUsage; + + @Parameter(names = {"--quota-cpu-usage"}, description = "The body web service cpuUsage parameter", required = false, arity = 1) + public Integer quotaCpuUsage; + + @Parameter(names = {"--quota-max-disk"}, description = "The body web service maxDisk parameter", required = false, arity = 1) + public Long quotaMaxDisk; + + @Parameter(names = {"--quota-max-cpu"}, description = "The body web service maxCpu parameter", required = false, arity = 1) + public Integer quotaMaxCpu; + + @Parameter(names = {"--account-expiration-date"}, description = "The body web service expirationDate parameter", required = false, arity = 1) + public String accountExpirationDate; + + @DynamicParameter(names = {"--attributes"}, description = "The body web service attributes parameter. Use: --attributes key=value", required = false) + public java.util.Map attributes = new HashMap<>(); //Dynamic parameters must be initialized; + + } + @Parameters(commandNames = {"configuration-update"}, commandDescription ="Update the Organization configuration attributes") public class UpdateConfigurationCommandOptions { @@ -245,6 +337,9 @@ public class UpdateConfigurationCommandOptions { @Parameter(names = {"--authentication-origins-action"}, description = "Action to be performed if the array of authenticationOrigins is being updated.", required = false, arity = 1) public String authenticationOriginsAction = "ADD"; + @Parameter(names = {"--default-user-expiration-date"}, description = "The body web service defaultUserExpirationDate parameter", required = false, arity = 1) + public String defaultUserExpirationDate; + @Parameter(names = {"--optimizations-simplify-permissions"}, description = "The body web service simplifyPermissions parameter", required = false, help = true, arity = 0) public boolean optimizationsSimplifyPermissions = false; 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 885a2ffaa70..66d7cee55a8 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 @@ -285,9 +285,6 @@ public class UpdateCommandOptions { @Parameter(names = {"--email"}, description = "The body web service email parameter", required = false, arity = 1) public String email; - @DynamicParameter(names = {"--attributes"}, description = "The body web service attributes parameter. Use: --attributes key=value", required = false) - public java.util.Map attributes = new HashMap<>(); //Dynamic parameters must be initialized; - } } \ No newline at end of file diff --git a/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v3/v3_1_0/UserBanMigration.java b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v3/v3_1_0/UserBanMigration.java new file mode 100644 index 00000000000..b7d79b73914 --- /dev/null +++ b/opencga-app/src/main/java/org/opencb/opencga/app/migrations/v3/v3_1_0/UserBanMigration.java @@ -0,0 +1,41 @@ +package org.opencb.opencga.app.migrations.v3.v3_1_0; + +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Projections; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.Updates; +import org.bson.Document; +import org.opencb.opencga.catalog.db.mongodb.OrganizationMongoDBAdaptorFactory; +import org.opencb.opencga.catalog.migration.Migration; +import org.opencb.opencga.catalog.migration.MigrationTool; +import org.opencb.opencga.catalog.utils.Constants; +import org.opencb.opencga.core.common.TimeUtils; + +@Migration(id = "addFailedLoginAttemptsMigration", description = "Add failedAttempts to User #TASK-6013", version = "3.2.0", + language = Migration.MigrationLanguage.JAVA, domain = Migration.MigrationDomain.CATALOG, date = 20240419) +public class UserBanMigration extends MigrationTool { + + @Override + protected void run() throws Exception { + String lastModified = TimeUtils.getTime(); + migrateCollection(OrganizationMongoDBAdaptorFactory.USER_COLLECTION, + Filters.exists("internal.registrationDate", false), + Projections.include("_id", "internal", "account"), + (document, bulk) -> { + Document internal = document.get("internal", Document.class); + Document account = document.get("account", Document.class); + internal.put("failedAttempts", 0); + internal.put("registrationDate", account.get("creationDate")); + internal.put("lastModified", lastModified); + account.put("expirationDate", Constants.DEFAULT_USER_EXPIRATION_DATE); + + bulk.add(new UpdateOneModel<>(Filters.eq("_id", document.get("_id")), + Updates.combine( + Updates.set("internal", internal), + Updates.set("account", account)) + ) + ); + }); + } + +} diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java index 3acc8124f97..7172401fda7 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/auth/authentication/azure/AuthenticationFactory.java @@ -9,7 +9,7 @@ import org.opencb.opencga.catalog.managers.OrganizationManager; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.config.AuthenticationOrigin; -import org.opencb.opencga.core.config.Email; +import org.opencb.opencga.core.config.Configuration; import org.opencb.opencga.core.models.organizations.Organization; import org.opencb.opencga.core.models.user.AuthenticationResponse; import org.opencb.opencga.core.models.user.User; @@ -29,16 +29,15 @@ public final class AuthenticationFactory { private final Map> authenticationManagerMap; private final Logger logger = LoggerFactory.getLogger(AuthenticationFactory.class); private final DBAdaptorFactory catalogDBAdaptorFactory; + private final Configuration configuration; - public AuthenticationFactory(DBAdaptorFactory catalogDBAdaptorFactory) { + public AuthenticationFactory(DBAdaptorFactory catalogDBAdaptorFactory, Configuration configuration) { this.catalogDBAdaptorFactory = catalogDBAdaptorFactory; + this.configuration = configuration; authenticationManagerMap = new ConcurrentHashMap<>(); } public void configureOrganizationAuthenticationManager(Organization organization) throws CatalogException { - // TODO: Pass proper email values - Email email = new Email(); - Map tmpAuthenticationManagerMap = new HashMap<>(); long expiration = organization.getConfiguration().getToken().getExpiration(); @@ -59,7 +58,8 @@ public void configureOrganizationAuthenticationManager(Organization organization break; case OPENCGA: CatalogAuthenticationManager catalogAuthenticationManager = - new CatalogAuthenticationManager(catalogDBAdaptorFactory, email, algorithm, secretKey, expiration); + new CatalogAuthenticationManager(catalogDBAdaptorFactory, configuration.getEmail(), algorithm, + secretKey, expiration); tmpAuthenticationManagerMap.put(CatalogAuthenticationManager.INTERNAL, catalogAuthenticationManager); tmpAuthenticationManagerMap.put(CatalogAuthenticationManager.OPENCGA, catalogAuthenticationManager); break; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/UserDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/UserDBAdaptor.java index 8a6c7207968..3099262e917 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/UserDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/api/UserDBAdaptor.java @@ -118,13 +118,15 @@ enum QueryParams implements QueryParam { NAME("name", TEXT_ARRAY, ""), EMAIL("email", TEXT_ARRAY, ""), ORGANIZATION("organization", TEXT_ARRAY, ""), + INTERNAL("internal", OBJECT, ""), + INTERNAL_FAILED_ATTEMPTS("internal.failedAttempts", INTEGER, ""), INTERNAL_STATUS_ID("internal.status.id", TEXT, ""), INTERNAL_STATUS_DATE("internal.status.date", TEXT, ""), ACCOUNT("account", TEXT_ARRAY, ""), ACCOUNT_AUTHENTICATION_ID("account.authentication.id", TEXT, ""), ACCOUNT_CREATION_DATE("account.creationDate", TEXT, ""), - SIZE("size", INTEGER_ARRAY, ""), - QUOTA("quota", INTEGER_ARRAY, ""), + ACCOUNT_EXPIRATION_DATE("account.expirationDate", TEXT, ""), + QUOTA("quota", OBJECT, ""), ATTRIBUTES("attributes", TEXT, ""), // "Format: where is [<|<=|>|>=|==|!=|~|!~]" PROJECTS("projects", TEXT_ARRAY, ""), diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java index f7896320d10..445c1c6047a 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBAdaptor.java @@ -259,8 +259,9 @@ public List effectivePermissions(long studyUid, List resourceIdList } Document studyDocument = studyResult.first(); + boolean simplifyPermissions = simplifyPermissions(); Map> groupsMap = getGroupUsersMap(studyDocument); - Map> studyUserPermissionsMap = extractUserPermissionsMap(groupsMap, studyDocument); + Map> studyUserPermissionsMap = extractUserPermissionsMap(groupsMap, studyDocument, simplifyPermissions); // Retrieve ACL list for the resources requested MongoDBCollection collection = getMainCollection(entry); @@ -284,7 +285,8 @@ public List effectivePermissions(long studyUid, List resourceIdList throw new CatalogDBException("Resource id '" + resourceId + "' not found."); } Document resourceDocument = dataResultMap.get(resourceId); - Map> resourceUserPermissionsMap = extractUserPermissionsMap(groupsMap, resourceDocument); + Map> resourceUserPermissionsMap = extractUserPermissionsMap(groupsMap, resourceDocument, + simplifyPermissions); Acl acl = convertPermissionsToAcl(groupsMap, studyUserPermissionsMap, resourceUserPermissionsMap, resourceId, entry); aclList.add(acl); } @@ -357,7 +359,8 @@ private Acl convertPermissionsToAcl(Map> groupsMap, Map> extractUserPermissionsMap(Map> groupsMap, Document document) { + private Map> extractUserPermissionsMap(Map> groupsMap, Document document, + boolean simplifyPermissions) { Set allUsers = groupsMap.get(ParamConstants.MEMBERS_GROUP); // Map of userId - List of permissions @@ -383,8 +386,6 @@ private Map> extractUserPermissionsMap(Map userIdsWithPermissions = new HashSet<>(); // Personal ACLs diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBUtils.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBUtils.java index 987a493fd38..fb8a3952f50 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBUtils.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/AuthorizationMongoDBUtils.java @@ -25,7 +25,6 @@ import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.exceptions.CatalogParameterException; import org.opencb.opencga.core.api.ParamConstants; -import org.opencb.opencga.core.config.Configuration; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.study.StudyPermissions; @@ -200,35 +199,18 @@ private static int getPermissionType(Enums.Resource resource) throws CatalogPara /** * If query contains {@link ParamConstants#ACL_PARAM}, it will parse the value to generate the corresponding mongo query documents. * - * @param study Queried study document. - * @param query Original query. - * @param resource Affected resource. - * @param user User performing the query. - * @return A list of documents to satisfy the ACL query. - * @throws CatalogDBException when there is a DB error. - * @throws CatalogParameterException if there is any formatting error. - * @throws CatalogAuthorizationException if the user is not authorised to perform the query. - */ - public static List parseAclQuery(Document study, Query query, Enums.Resource resource, String user) - throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { - return parseAclQuery(study, query, resource, user, null); - } - - /** - * If query contains {@link ParamConstants#ACL_PARAM}, it will parse the value to generate the corresponding mongo query documents. - * - * @param study Queried study document. - * @param query Original query. - * @param resource Affected resource. - * @param user User performing the query. - * @param configuration Configuration object. + * @param study Queried study document. + * @param query Original query. + * @param resource Affected resource. + * @param user User performing the query. + * @param simplifyPermissions Boolean indicating whether permission check can be simplified. * @return A list of documents to satisfy the ACL query. * @throws CatalogDBException when there is a DB error. * @throws CatalogParameterException if there is any formatting error. * @throws CatalogAuthorizationException if the user is not authorised to perform the query. */ public static List parseAclQuery(Document study, Query query, Enums.Resource resource, String user, - Configuration configuration) + boolean simplifyPermissions) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { List aclDocuments = new LinkedList<>(); if (!query.containsKey(ParamConstants.ACL_PARAM)) { @@ -264,11 +246,6 @@ public static List parseAclQuery(Document study, Query query, Enums.Re + study.getString(StudyDBAdaptor.QueryParams.ID.key())); } - boolean simplifyPermissionCheck = false; - if (configuration != null && configuration.getOptimizations() != null) { - simplifyPermissionCheck = configuration.getOptimizations().isSimplifyPermissions(); - } - boolean isAnonymousPresent = false; boolean isRegisteredUsersPresent = false; List groups; @@ -302,7 +279,7 @@ public static List parseAclQuery(Document study, Query query, Enums.Re } Document queryDocument = getAuthorisedEntries(affectedUser, groups, permission, isRegisteredUsersPresent, isAnonymousPresent, - simplifyPermissionCheck); + simplifyPermissions); if (hasStudyPermissions) { // The user has permissions defined globally, so we also have to check the entries where the user/groups/members/* have no // permissions defined as the user will also be allowed to see them @@ -317,13 +294,8 @@ public static List parseAclQuery(Document study, Query query, Enums.Re return aclDocuments; } - public static Document getQueryForAuthorisedEntries(Document study, String user, String permission, Enums.Resource resource) - throws CatalogAuthorizationException, CatalogParameterException { - return getQueryForAuthorisedEntries(study, user, permission, resource, null); - } - public static Document getQueryForAuthorisedEntries(Document study, String user, String permission, Enums.Resource resource, - Configuration configuration) + boolean simplifyPermissions) throws CatalogAuthorizationException, CatalogParameterException { if (StringUtils.isEmpty(user)) { return new Document(); @@ -342,11 +314,6 @@ public static Document getQueryForAuthorisedEntries(Document study, String user, + study.getString(StudyDBAdaptor.QueryParams.ID.key())); } - boolean simplifyPermissionCheck = false; - if (configuration != null && configuration.getOptimizations() != null) { - simplifyPermissionCheck = configuration.getOptimizations().isSimplifyPermissions(); - } - String studyPermission = StudyPermissions.Permissions.getStudyPermission(permission, getPermissionType(resource)).name(); // 0. Check if anonymous has any permission defined (just for performance) @@ -375,8 +342,8 @@ public static Document getQueryForAuthorisedEntries(Document study, String user, } Document queryDocument = getAuthorisedEntries(user, groups, permission, isRegisteredUsersPresent, isAnonymousPresent, - simplifyPermissionCheck); - if (hasStudyPermissions && !simplifyPermissionCheck) { + simplifyPermissions); + if (hasStudyPermissions && !simplifyPermissions) { // The user has permissions defined globally, so we also have to check the entries where the user/groups/members/* have no // permissions defined as the user will also be allowed to see them queryDocument = new Document("$or", Arrays.asList( diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java index ce2f0d138ee..2b9d84cd876 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/ClinicalAnalysisMongoDBAdaptor.java @@ -31,7 +31,10 @@ import org.opencb.commons.datastore.core.*; import org.opencb.commons.datastore.mongodb.MongoDBCollection; import org.opencb.commons.datastore.mongodb.MongoDBIterator; -import org.opencb.opencga.catalog.db.api.*; +import org.opencb.opencga.catalog.db.api.ClinicalAnalysisDBAdaptor; +import org.opencb.opencga.catalog.db.api.DBIterator; +import org.opencb.opencga.catalog.db.api.FileDBAdaptor; +import org.opencb.opencga.catalog.db.api.InterpretationDBAdaptor; import org.opencb.opencga.catalog.db.mongodb.converters.ClinicalAnalysisConverter; import org.opencb.opencga.catalog.db.mongodb.iterators.ClinicalAnalysisCatalogMongoDBIterator; import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; @@ -1286,17 +1289,18 @@ private Bson parseQuery(Query query, Document extraQuery, String user) if (queryCopy.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || queryCopy.containsKey(ParamConstants.ACL_PARAM))) { Document studyDocument = getStudyDocument(null, queryCopy.getLong(QueryParams.STUDY_UID.key())); + boolean simplifyPermissions = simplifyPermissions(); if (queryCopy.containsKey(ParamConstants.ACL_PARAM)) { andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, queryCopy, Enums.Resource.CLINICAL_ANALYSIS, user, - configuration)); + simplifyPermissions)); } else { if (containsAnnotationQuery(query)) { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, - ClinicalAnalysisPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.CLINICAL_ANALYSIS, configuration)); + ClinicalAnalysisPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.CLINICAL_ANALYSIS, simplifyPermissions)); } else { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, ClinicalAnalysisPermissions.VIEW.name(), - Enums.Resource.CLINICAL_ANALYSIS, configuration)); + Enums.Resource.CLINICAL_ANALYSIS, simplifyPermissions)); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/CohortMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/CohortMongoDBAdaptor.java index c6dd04863de..f34f5ced849 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/CohortMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/CohortMongoDBAdaptor.java @@ -879,18 +879,19 @@ private Bson parseQuery(Query query, Document extraQuery, String user) if (query.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || query.containsKey(ParamConstants.ACL_PARAM))) { + boolean simplifyPermissions = simplifyPermissions(); Document studyDocument = getStudyDocument(null, query.getLong(QueryParams.STUDY_UID.key())); if (query.containsKey(ParamConstants.ACL_PARAM)) { andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.COHORT, user, - configuration)); + simplifyPermissions)); } else { if (containsAnnotationQuery(query)) { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, - CohortPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.COHORT, configuration)); + CohortPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.COHORT, simplifyPermissions)); } else { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, CohortPermissions.VIEW.name(), - Enums.Resource.COHORT, configuration)); + Enums.Resource.COHORT, simplifyPermissions)); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FamilyMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FamilyMongoDBAdaptor.java index 2b7ed748772..9e8c35d00ab 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FamilyMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FamilyMongoDBAdaptor.java @@ -1191,17 +1191,18 @@ protected Bson parseQuery(Query query, Document extraQuery, String user) if (query.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || query.containsKey(ParamConstants.ACL_PARAM))) { Document studyDocument = getStudyDocument(null, query.getLong(QueryParams.STUDY_UID.key())); + boolean simplifyPermissions = simplifyPermissions(); if (query.containsKey(ParamConstants.ACL_PARAM)) { andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.FAMILY, user, - configuration)); + simplifyPermissions)); } else { if (containsAnnotationQuery(query)) { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, - FamilyPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.FAMILY, configuration)); + FamilyPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.FAMILY, simplifyPermissions)); } else { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, FamilyPermissions.VIEW.name(), - Enums.Resource.FAMILY, configuration)); + Enums.Resource.FAMILY, simplifyPermissions)); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java index 5b84d050083..f214620bf39 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/FileMongoDBAdaptor.java @@ -1409,16 +1409,18 @@ private Bson parseQuery(Query query, Document extraQuery, String user) if (query.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || query.containsKey(ParamConstants.ACL_PARAM))) { Document studyDocument = getStudyDocument(null, query.getLong(QueryParams.STUDY_UID.key())); + boolean simplifyPermissions = simplifyPermissions(); if (query.containsKey(ParamConstants.ACL_PARAM)) { - andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.FILE, user, configuration)); + andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.FILE, user, + simplifyPermissions)); } else { if (containsAnnotationQuery(query)) { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, FilePermissions.VIEW_ANNOTATIONS.name(), - Enums.Resource.FILE, configuration)); + Enums.Resource.FILE, simplifyPermissions)); } else { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, FilePermissions.VIEW.name(), - Enums.Resource.FILE, configuration)); + Enums.Resource.FILE, simplifyPermissions)); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java index 707375a5811..13dbb40741c 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/IndividualMongoDBAdaptor.java @@ -1405,17 +1405,18 @@ private Bson parseQuery(Query query, Document extraQuery, String user) if (query.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || query.containsKey(ParamConstants.ACL_PARAM))) { Document studyDocument = getStudyDocument(null, query.getLong(QueryParams.STUDY_UID.key())); + boolean simplifyPermissions = simplifyPermissions(); if (query.containsKey(ParamConstants.ACL_PARAM)) { andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.INDIVIDUAL, user, - configuration)); + simplifyPermissions)); } else { if (containsAnnotationQuery(query)) { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, - IndividualPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.INDIVIDUAL, configuration)); + IndividualPermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.INDIVIDUAL, simplifyPermissions)); } else { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, IndividualPermissions.VIEW.name(), - Enums.Resource.INDIVIDUAL, configuration)); + Enums.Resource.INDIVIDUAL, simplifyPermissions)); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/JobMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/JobMongoDBAdaptor.java index 58b3291a0d4..e193c5b9e09 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/JobMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/JobMongoDBAdaptor.java @@ -814,13 +814,15 @@ private Bson parseQuery(Query query, Document extraQuery, QueryOptions options, if (query.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || query.containsKey(ParamConstants.ACL_PARAM))) { Document studyDocument = getStudyDocument(null, query.getLong(QueryParams.STUDY_UID.key())); + boolean simplifyPermissions = simplifyPermissions(); if (query.containsKey(ParamConstants.ACL_PARAM)) { - andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.JOB, user, configuration)); + andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.JOB, user, + simplifyPermissions)); } else { // Get the document query needed to check the permissions as well andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, JobPermissions.VIEW.name(), - Enums.Resource.JOB, configuration)); + Enums.Resource.JOB, simplifyPermissions)); } query.remove(ParamConstants.ACL_PARAM); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptor.java index a4721dffc8d..b663ffdf755 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/MongoDBAdaptor.java @@ -28,10 +28,12 @@ import org.opencb.opencga.catalog.db.AbstractDBAdaptor; import org.opencb.opencga.catalog.db.api.StudyDBAdaptor; import org.opencb.opencga.catalog.exceptions.*; +import org.opencb.opencga.catalog.managers.OrganizationManager; import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.config.Configuration; +import org.opencb.opencga.core.models.organizations.Organization; import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.Logger; @@ -704,6 +706,24 @@ protected Document getStudyDocument(ClientSession clientSession, long studyUid) return dataResult.first(); } + /** + * Method to obtain whether permissions should be simplified or not. + * + * @return true if permissions should be simplified, false otherwise. + * @throws CatalogDBException if there is any error obtaining the organization configuration. + */ + protected boolean simplifyPermissions() throws CatalogDBException { + Organization organization = dbAdaptorFactory.getCatalogOrganizationDBAdaptor() + .get(OrganizationManager.INCLUDE_ORGANIZATION_CONFIGURATION).first(); + if (organization.getConfiguration().getOptimizations() != null) { + return organization.getConfiguration().getOptimizations().isSimplifyPermissions(); + } else { + logger.warn("Organization '{}' configuration does not contain the 'optimizations.simplifyPermissions' field. Defaulting" + + " to false", organization.getId()); + return false; + } + } + public class NestedArrayUpdateDocument { private Query query; private Document set; diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java index 4bb4d663e44..aa090f498d1 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/OrganizationMongoDBAdaptor.java @@ -405,9 +405,6 @@ private MongoDBIterator getMongoCursor(ClientSession clientSession, Qu } qOptions = filterQueryOptionsToIncludeKeys(qOptions, OrganizationManager.INCLUDE_ORGANIZATION_IDS.getAsStringList(QueryOptions.INCLUDE)); - if (!qOptions.getBoolean(IS_ORGANIZATION_ADMIN_OPTION)) { - qOptions = filterQueryOptionsToExcludeKeys(qOptions, Arrays.asList(QueryParams.CONFIGURATION.key())); - } return organizationCollection.iterator(clientSession, new Document(), null, null, qOptions); } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/PanelMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/PanelMongoDBAdaptor.java index d373c68bb2b..d82535b9351 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/PanelMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/PanelMongoDBAdaptor.java @@ -722,14 +722,15 @@ private Bson parseQuery(Query query, Document extraQuery, String user) if (query.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || query.containsKey(ParamConstants.ACL_PARAM))) { Document studyDocument = getStudyDocument(null, query.getLong(QueryParams.STUDY_UID.key())); + boolean simplifyPermissions = simplifyPermissions(); if (query.containsKey(ParamConstants.ACL_PARAM)) { andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.DISEASE_PANEL, user, - configuration)); + simplifyPermissions)); } else { // Get the document query needed to check the permissions as well andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, PanelPermissions.VIEW.name(), - Enums.Resource.DISEASE_PANEL, configuration)); + Enums.Resource.DISEASE_PANEL, simplifyPermissions)); } query.remove(ParamConstants.ACL_PARAM); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/SampleMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/SampleMongoDBAdaptor.java index a6d3b8521c0..198c9fce456 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/SampleMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/SampleMongoDBAdaptor.java @@ -1327,17 +1327,18 @@ private Bson parseQuery(Query query, Document extraQuery, String user) if (query.containsKey(QueryParams.STUDY_UID.key()) && (StringUtils.isNotEmpty(user) || query.containsKey(ParamConstants.ACL_PARAM))) { Document studyDocument = getStudyDocument(null, query.getLong(QueryParams.STUDY_UID.key())); + boolean simplifyPermissions = simplifyPermissions(); if (query.containsKey(ParamConstants.ACL_PARAM)) { andBsonList.addAll(AuthorizationMongoDBUtils.parseAclQuery(studyDocument, query, Enums.Resource.SAMPLE, user, - configuration)); + simplifyPermissions)); } else { if (containsAnnotationQuery(query)) { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, - SamplePermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.SAMPLE, configuration)); + SamplePermissions.VIEW_ANNOTATIONS.name(), Enums.Resource.SAMPLE, simplifyPermissions)); } else { andBsonList.add(getQueryForAuthorisedEntries(studyDocument, user, SamplePermissions.VIEW.name(), - Enums.Resource.SAMPLE, configuration)); + Enums.Resource.SAMPLE, simplifyPermissions)); } } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/UserMongoDBAdaptor.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/UserMongoDBAdaptor.java index 21b592d1195..19a9a6cd1f3 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/UserMongoDBAdaptor.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/db/mongodb/UserMongoDBAdaptor.java @@ -427,27 +427,35 @@ public OpenCGAResult nativeGet(Query query, QueryOptions options) throws Catalog @Override public OpenCGAResult update(Query query, ObjectMap parameters, QueryOptions queryOptions) throws CatalogDBException { - Map userParameters = new HashMap<>(); + UpdateDocument document = new UpdateDocument(); - final String[] acceptedParams = {QueryParams.NAME.key(), QueryParams.EMAIL.key()}; - filterStringParams(parameters, userParameters, acceptedParams); + final String[] acceptedParams = {QueryParams.NAME.key(), QueryParams.EMAIL.key(), ACCOUNT_EXPIRATION_DATE.key()}; + filterStringParams(parameters, document.getSet(), acceptedParams); if (parameters.containsKey(QueryParams.INTERNAL_STATUS_ID.key())) { - userParameters.put(QueryParams.INTERNAL_STATUS_ID.key(), parameters.get(QueryParams.INTERNAL_STATUS_ID.key())); - userParameters.put(QueryParams.INTERNAL_STATUS_DATE.key(), TimeUtils.getTime()); + document.getSet().put(QueryParams.INTERNAL_STATUS_ID.key(), parameters.get(QueryParams.INTERNAL_STATUS_ID.key())); + document.getSet().put(QueryParams.INTERNAL_STATUS_DATE.key(), TimeUtils.getTime()); } - final String[] acceptedLongParams = {QueryParams.QUOTA.key(), QueryParams.SIZE.key()}; - filterLongParams(parameters, userParameters, acceptedLongParams); + final String[] acceptedIntParams = {INTERNAL_FAILED_ATTEMPTS.key()}; + filterIntParams(parameters, document.getSet(), acceptedIntParams); + + final String[] acceptedObjectParams = {QueryParams.QUOTA.key()}; + filterObjectParams(parameters, document.getSet(), acceptedObjectParams); final String[] acceptedMapParams = {QueryParams.ATTRIBUTES.key()}; - filterMapParams(parameters, userParameters, acceptedMapParams); + filterMapParams(parameters, document.getSet(), acceptedMapParams); + + if (!document.toFinalUpdateDocument().isEmpty()) { + document.getSet().put(INTERNAL_LAST_MODIFIED, TimeUtils.getTime()); + } - if (!userParameters.isEmpty()) { - return new OpenCGAResult(userCollection.update(parseQuery(query), new Document("$set", userParameters), null)); + Document userUpdate = document.toFinalUpdateDocument(); + if (userUpdate.isEmpty()) { + throw new CatalogDBException("Nothing to be updated."); } - return OpenCGAResult.empty(); + return new OpenCGAResult(userCollection.update(parseQuery(query), userUpdate, null)); } @Override @@ -476,11 +484,7 @@ public OpenCGAResult update(String userId, ObjectMap parameters) throws CatalogDBException, CatalogParameterException, CatalogAuthorizationException { checkId(userId); Query query = new Query(QueryParams.ID.key(), userId); - OpenCGAResult update = update(query, parameters, QueryOptions.empty()); - if (update.getNumUpdated() != 1) { - throw new CatalogDBException("Could not update user " + userId); - } - return update; + return update(query, parameters, QueryOptions.empty()); } OpenCGAResult setStatus(Query query, String status) throws CatalogDBException { @@ -666,8 +670,6 @@ private Bson parseQuery(Query query) throws CatalogDBException { case EMAIL: case ORGANIZATION: case INTERNAL_STATUS_DATE: - case SIZE: - case QUOTA: case ACCOUNT_AUTHENTICATION_ID: case ACCOUNT_CREATION_DATE: case TOOL_ID: diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/exceptions/CatalogAuthenticationException.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/exceptions/CatalogAuthenticationException.java index 8cb658885dc..3853e91607f 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/exceptions/CatalogAuthenticationException.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/exceptions/CatalogAuthenticationException.java @@ -61,6 +61,21 @@ public static CatalogAuthenticationException incorrectUserOrPassword(String doma return new CatalogAuthenticationException(domain + ": Incorrect user or password.", e); } + public static CatalogAuthenticationException userIsBanned(String userId) { + return new CatalogAuthenticationException("Too many login attempts. The account for user '" + userId + "' is banned." + + " Please, talk to your organization owner/administrator."); + } + + public static CatalogAuthenticationException userIsSuspended(String userId) { + return new CatalogAuthenticationException("The account for user '" + userId + "' is suspended. Please, talk to your organization" + + " owner/administrator."); + } + + public static CatalogAuthenticationException accountIsExpired(String userId, String expirationDate) { + return new CatalogAuthenticationException("The account for user '" + userId + "' expired on " + expirationDate + ". Please," + + " talk to your organization owner/administrator."); + } + public static CatalogAuthenticationException userNotAllowed(String domain) { return new CatalogAuthenticationException(domain + ": User not allowed to access the system."); } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java index ec8c3dcf2ad..1409b1e9d4c 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/CatalogManager.java @@ -20,6 +20,7 @@ import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.opencga.catalog.auth.authentication.CatalogAuthenticationManager; +import org.opencb.opencga.catalog.auth.authentication.JwtManager; import org.opencb.opencga.catalog.auth.authentication.azure.AuthenticationFactory; import org.opencb.opencga.catalog.auth.authorization.AuthorizationDBAdaptorFactory; import org.opencb.opencga.catalog.auth.authorization.AuthorizationManager; @@ -35,6 +36,7 @@ import org.opencb.opencga.catalog.io.CatalogIOManager; import org.opencb.opencga.catalog.io.IOManagerFactory; import org.opencb.opencga.catalog.migration.MigrationManager; +import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.JwtUtils; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.common.PasswordUtils; @@ -107,7 +109,7 @@ private void init() throws CatalogException { catalogDBAdaptorFactory = new MongoDBAdaptorFactory(configuration, ioManagerFactory); authorizationDBAdaptorFactory = new AuthorizationMongoDBAdaptorFactory((MongoDBAdaptorFactory) catalogDBAdaptorFactory, configuration); - authenticationFactory = new AuthenticationFactory(catalogDBAdaptorFactory); + authenticationFactory = new AuthenticationFactory(catalogDBAdaptorFactory, configuration); logger.debug("CatalogManager configureManager"); configureManagers(configuration); } @@ -259,6 +261,10 @@ private void privateInstall(String algorithm, String secretKey, String password, if (!PasswordUtils.isStrongPassword(password)) { throw new CatalogException("Invalid password. Check password strength for user "); } + if (StringUtils.isEmpty(secretKey)) { + logger.info("Generating secret key"); + secretKey = PasswordUtils.getStrongRandomPassword(JwtManager.SECRET_KEY_MIN_LENGTH); + } ParamUtils.checkParameter(secretKey, "secretKey"); ParamUtils.checkParameter(password, "password"); JwtUtils.validateJWTKey(algorithm, secretKey); @@ -267,7 +273,7 @@ private void privateInstall(String algorithm, String secretKey, String password, OrganizationConfiguration organizationConfiguration = new OrganizationConfiguration( Collections.singletonList(CatalogAuthenticationManager.createOpencgaAuthenticationOrigin()), - new Optimizations(), new TokenConfiguration(algorithm, secretKey, 3600L)); + Constants.DEFAULT_USER_EXPIRATION_DATE, new Optimizations(), new TokenConfiguration(algorithm, secretKey, 3600L)); organizationManager.create(new OrganizationCreateParams(ADMIN_ORGANIZATION, ADMIN_ORGANIZATION, null, null, organizationConfiguration, null), QueryOptions.empty(), null); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java index a1a49273086..90b83a92728 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/managers/OrganizationManager.java @@ -27,15 +27,19 @@ import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.AuthenticationOrigin; import org.opencb.opencga.core.config.Configuration; +import org.opencb.opencga.core.config.Optimizations; import org.opencb.opencga.core.models.JwtPayload; import org.opencb.opencga.core.models.audit.AuditRecord; import org.opencb.opencga.core.models.common.Enums; import org.opencb.opencga.core.models.common.InternalStatus; import org.opencb.opencga.core.models.organizations.*; +import org.opencb.opencga.core.models.user.OrganizationUserUpdateParams; +import org.opencb.opencga.core.models.user.User; import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.util.*; import java.util.stream.Collectors; @@ -288,8 +292,71 @@ public OpenCGAResult update(String organizationId, OrganizationUpd return result; } + public OpenCGAResult updateUser(@Nullable String organizationId, String userId, OrganizationUserUpdateParams updateParams, + QueryOptions options, String token) throws CatalogException { + JwtPayload tokenPayload = catalogManager.getUserManager().validateToken(token); + + ObjectMap auditParams = new ObjectMap() + .append("organizationId", organizationId) + .append("userId", userId) + .append("updateParams", updateParams) + .append("options", options) + .append("token", token); + + options = ParamUtils.defaultObject(options, QueryOptions::new); + String myOrganizationId = StringUtils.isNotEmpty(organizationId) ? organizationId : tokenPayload.getOrganization(); + try { + authorizationManager.checkIsAtLeastOrganizationOwnerOrAdmin(myOrganizationId, tokenPayload.getUserId(myOrganizationId)); + ParamUtils.checkObj(updateParams, "OrganizationUserUpdateParams"); + getUserDBAdaptor(myOrganizationId).checkId(userId); + + if (StringUtils.isNotEmpty(updateParams.getEmail())) { + ParamUtils.checkEmail(updateParams.getEmail()); + } + if (updateParams.getQuota() != null) { + if (updateParams.getQuota().getDiskUsage() < 0) { + throw new CatalogException("Disk usage cannot be negative"); + } + if (updateParams.getQuota().getCpuUsage() < 0) { + throw new CatalogException("CPU usage cannot be negative"); + } + if (updateParams.getQuota().getMaxDisk() < 0) { + throw new CatalogException("Max disk cannot be negative"); + } + if (updateParams.getQuota().getMaxCpu() < 0) { + throw new CatalogException("Max CPU cannot be negative"); + } + } + if (updateParams.getAccount() != null && StringUtils.isNotEmpty(updateParams.getAccount().getExpirationDate())) { + ParamUtils.checkDateIsNotExpired(updateParams.getAccount().getExpirationDate(), "expirationDate"); + } + + ObjectMap updateMap; + try { + updateMap = updateParams.getUpdateMap(); + } catch (JsonProcessingException e) { + throw new CatalogException("Could not parse OrganizationUserUpdateParams object: " + e.getMessage(), e); + } + OpenCGAResult updateResult = getUserDBAdaptor(myOrganizationId).update(userId, updateMap); + auditManager.auditUpdate(myOrganizationId, tokenPayload.getUserId(myOrganizationId), Enums.Resource.USER, userId, "", "", "", + auditParams, new AuditRecord.Status(AuditRecord.Status.Result.SUCCESS)); + + if (options.getBoolean(ParamConstants.INCLUDE_RESULT_PARAM)) { + // Fetch updated user + OpenCGAResult result = getUserDBAdaptor(myOrganizationId).get(userId, options); + updateResult.setResults(result.getResults()); + } + + return updateResult; + } catch (CatalogException e) { + auditManager.auditUpdate(myOrganizationId, tokenPayload.getUserId(myOrganizationId), Enums.Resource.USER, userId, "", "", "", + auditParams, new AuditRecord.Status(AuditRecord.Status.Result.ERROR, e.getError())); + throw e; + } + } + public OpenCGAResult updateConfiguration(String organizationId, OrganizationConfiguration updateParams, - QueryOptions options, String token) throws CatalogException { + QueryOptions options, String token) throws CatalogException { JwtPayload tokenPayload = catalogManager.getUserManager().validateToken(token); String userId = tokenPayload.getUserId(organizationId); @@ -480,6 +547,12 @@ private void validateOrganizationForCreation(Organization organization, String u if (organization.getConfiguration() == null) { organization.setConfiguration(new OrganizationConfiguration()); } + validateOrganizationConfiguration(organization); + + organization.setAttributes(ParamUtils.defaultObject(organization.getAttributes(), HashMap::new)); + } + + private void validateOrganizationConfiguration(Organization organization) throws CatalogParameterException { if (CollectionUtils.isNotEmpty(organization.getConfiguration().getAuthenticationOrigins())) { for (AuthenticationOrigin authenticationOrigin : organization.getConfiguration().getAuthenticationOrigins()) { ParamUtils.checkParameter(authenticationOrigin.getId(), "AuthenticationOrigin id"); @@ -493,7 +566,11 @@ private void validateOrganizationForCreation(Organization organization, String u || StringUtils.isEmpty(organization.getConfiguration().getToken().getSecretKey())) { organization.getConfiguration().setToken(TokenConfiguration.init()); } - organization.setAttributes(ParamUtils.defaultObject(organization.getAttributes(), HashMap::new)); + organization.getConfiguration().setDefaultUserExpirationDate(ParamUtils.defaultString( + organization.getConfiguration().getDefaultUserExpirationDate(), Constants.DEFAULT_USER_EXPIRATION_DATE)); + if (organization.getConfiguration().getOptimizations() == null) { + organization.getConfiguration().setOptimizations(new Optimizations(false)); + } } Set getOrganizationOwnerAndAdmins(String organizationId) throws CatalogException { 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 d882015eae5..4c9f03d12e1 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 @@ -40,6 +40,7 @@ import org.opencb.opencga.core.models.JwtPayload; import org.opencb.opencga.core.models.audit.AuditRecord; import org.opencb.opencga.core.models.common.Enums; +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.user.*; @@ -59,8 +60,8 @@ */ public class UserManager extends AbstractManager { - static final QueryOptions INCLUDE_ACCOUNT = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList( - UserDBAdaptor.QueryParams.ID.key(), UserDBAdaptor.QueryParams.ACCOUNT.key())); + static final QueryOptions INCLUDE_ACCOUNT_AND_INTERNAL = new QueryOptions(QueryOptions.INCLUDE, Arrays.asList( + UserDBAdaptor.QueryParams.ID.key(), UserDBAdaptor.QueryParams.ACCOUNT.key(), UserDBAdaptor.QueryParams.INTERNAL.key())); protected static Logger logger = LoggerFactory.getLogger(UserManager.class); private final CatalogIOManager catalogIOManager; private final AuthenticationFactory authenticationFactory; @@ -118,6 +119,9 @@ public OpenCGAResult create(User user, String password, String token) thro throw new CatalogException("Creating '" + OPENCGA + "' user is forbidden in any organization."); } + Organization organization = getOrganizationDBAdaptor(organizationId).get(OrganizationManager.INCLUDE_ORGANIZATION_CONFIGURATION) + .first(); + ObjectMap auditParams = new ObjectMap("user", user); // Initialise fields @@ -130,7 +134,15 @@ public OpenCGAResult create(User user, String password, String token) thro } user.setAccount(ParamUtils.defaultObject(user.getAccount(), Account::new)); user.getAccount().setCreationDate(TimeUtils.getTime()); - user.getAccount().setExpirationDate(ParamUtils.defaultString(user.getAccount().getExpirationDate(), "")); + if (StringUtils.isEmpty(user.getAccount().getExpirationDate())) { + // By default, user accounts will be valid for 1 year when they are created. + user.getAccount().setExpirationDate(organization.getConfiguration().getDefaultUserExpirationDate()); + Date date = TimeUtils.add1YeartoDate(new Date()); + user.getAccount().setExpirationDate(TimeUtils.getTime(date)); + } else { + // Validate expiration date is not over + ParamUtils.checkDateIsNotExpired(user.getAccount().getExpirationDate(), "account.expirationDate"); + } user.setInternal(new UserInternal(new UserStatus(UserStatus.READY))); user.setQuota(ParamUtils.defaultObject(user.getQuota(), UserQuota::new)); user.setProjects(ParamUtils.defaultObject(user.getProjects(), Collections::emptyList)); @@ -253,7 +265,8 @@ public JwtPayload validateToken(String token) throws CatalogException { if (ParamConstants.ANONYMOUS_USER_ID.equals(jwtPayload.getUserId())) { authOrigin = CatalogAuthenticationManager.OPENCGA; } else { - OpenCGAResult userResult = getUserDBAdaptor(jwtPayload.getOrganization()).get(jwtPayload.getUserId(), INCLUDE_ACCOUNT); + OpenCGAResult userResult = getUserDBAdaptor(jwtPayload.getOrganization()).get(jwtPayload.getUserId(), + INCLUDE_ACCOUNT_AND_INTERNAL); if (userResult.getNumResults() == 0) { throw new CatalogException("User '" + jwtPayload.getUserId() + "' could not be found."); } @@ -631,7 +644,7 @@ public OpenCGAResult update(String userId, ObjectMap parameters, QueryOpti userId = getValidUserId(userId, payload); for (String s : parameters.keySet()) { - if (!s.matches("name|email|attributes")) { + if (!s.matches("name|email")) { throw new CatalogDBException("Parameter '" + s + "' can't be changed"); } } @@ -739,7 +752,7 @@ public OpenCGAResult resetPassword(String userId, String token) throws CatalogEx JwtPayload jwtPayload = validateToken(token); String organizationId = jwtPayload.getOrganization(); try { - authorizationManager.checkIsOpencgaAdministrator(jwtPayload, "reset password"); + authorizationManager.checkIsAtLeastOrganizationOwnerOrAdmin(organizationId, jwtPayload.getUserId()); String authOrigin = getAuthenticationOriginId(organizationId, userId); OpenCGAResult writeResult = authenticationFactory.resetPassword(organizationId, authOrigin, userId); @@ -778,16 +791,55 @@ public AuthenticationResponse login(String organizationId, String username, Stri } } - OpenCGAResult userOpenCGAResult = getUserDBAdaptor(organizationId).get(username, INCLUDE_ACCOUNT); + OpenCGAResult userOpenCGAResult = getUserDBAdaptor(organizationId).get(username, INCLUDE_ACCOUNT_AND_INTERNAL); if (userOpenCGAResult.getNumResults() == 1) { + User user = userOpenCGAResult.first(); + // Only local OPENCGA users that are not superadmins can be automatically banned or their accounts be expired + boolean userCanBeBanned = !ParamConstants.ADMIN_ORGANIZATION.equals(organizationId) + && CatalogAuthenticationManager.OPENCGA.equals(user.getAccount().getAuthentication().getId()); + // We check + if (userCanBeBanned) { + // Check user is not banned, suspended or has an expired account + if (UserStatus.BANNED.equals(user.getInternal().getStatus().getId())) { + throw CatalogAuthenticationException.userIsBanned(username); + } + Date date = TimeUtils.toDate(user.getAccount().getExpirationDate()); + if (date == null) { + throw new CatalogException("Unexpected null expiration date for user '" + username + "'."); + } + if (date.before(new Date())) { + throw CatalogAuthenticationException.accountIsExpired(username, user.getAccount().getExpirationDate()); + } + } + if (UserStatus.SUSPENDED.equals(user.getInternal().getStatus().getId())) { + throw CatalogAuthenticationException.userIsSuspended(username); + } authId = userOpenCGAResult.first().getAccount().getAuthentication().getId(); try { response = authenticationFactory.authenticate(organizationId, authId, username, password); } catch (CatalogAuthenticationException e) { + if (userCanBeBanned) { + // We can only lock the account if it is not the root user + int failedAttempts = userOpenCGAResult.first().getInternal().getFailedAttempts(); + ObjectMap updateParams = new ObjectMap(UserDBAdaptor.QueryParams.INTERNAL_FAILED_ATTEMPTS.key(), failedAttempts + 1); + if (failedAttempts >= (configuration.getMaxLoginAttempts() - 1)) { + // Ban the account + updateParams.append(UserDBAdaptor.QueryParams.INTERNAL_STATUS_ID.key(), UserStatus.BANNED); + } + getUserDBAdaptor(organizationId).update(username, updateParams); + } + auditManager.auditUser(organizationId, username, Enums.Action.LOGIN, username, new AuditRecord.Status(AuditRecord.Status.Result.ERROR, e.getError())); throw e; } + + // If it was a local user and the counter of failed attempts was greater than 0, we reset it + if (userCanBeBanned && userOpenCGAResult.first().getInternal().getFailedAttempts() > 0) { + // Reset login failed attempts counter + ObjectMap updateParams = new ObjectMap(UserDBAdaptor.QueryParams.INTERNAL_FAILED_ATTEMPTS.key(), 0); + getUserDBAdaptor(organizationId).update(username, updateParams); + } } else { // We attempt to login the user with the different authentication managers for (Map.Entry entry @@ -895,6 +947,70 @@ public AuthenticationResponse refreshToken(String token) throws CatalogException return response; } + public OpenCGAResult changeStatus(String organizationId, String userId, String status, QueryOptions options, String token) + throws CatalogException { + JwtPayload tokenPayload = validateToken(token); + String userIdOrganization = StringUtils.isNotEmpty(organizationId) ? organizationId : tokenPayload.getOrganization(); + + ObjectMap auditParams = new ObjectMap() + .append("organizationId", organizationId) + .append("userId", userId) + .append("status", status) + .append("options", options) + .append("token", token); + try { + authorizationManager.checkIsAtLeastOrganizationOwnerOrAdmin(userIdOrganization, tokenPayload.getUserId(userIdOrganization)); + options = ParamUtils.defaultObject(options, QueryOptions::new); + + // Validate user exists + getUserDBAdaptor(userIdOrganization).checkId(userId); + + // Validate status is valid + if (!UserStatus.READY.equals(status) && !UserStatus.SUSPENDED.equals(status)) { + throw new CatalogParameterException("Invalid status '" + status + "'. Valid values are: " + UserStatus.READY + ", " + + UserStatus.SUSPENDED); + } + + if (UserStatus.SUSPENDED.equals(status)) { + // Get organization information + Set ownerAndAdmins = catalogManager.getOrganizationManager().getOrganizationOwnerAndAdmins(userIdOrganization); + if (ownerAndAdmins.contains(userId)) { + if (tokenPayload.getUserId().equals(userId)) { + // The user is trying to suspend himself + throw new CatalogAuthorizationException("You can't suspend your own account."); + } + if (!authorizationManager.isAtLeastOrganizationOwner(userIdOrganization, tokenPayload.getUserId(userIdOrganization))) { + // One of the admins is trying to suspend the owner or one of the admins + throw new CatalogAuthorizationException("Only the owner of the organization can suspend administrators."); + } + } + } + + // Update user status and reset failed attempts to 0 + ObjectMap updateParams = new ObjectMap(UserDBAdaptor.QueryParams.INTERNAL_STATUS_ID.key(), status); + if (UserStatus.READY.equals(status)) { + updateParams.put(UserDBAdaptor.QueryParams.INTERNAL_FAILED_ATTEMPTS.key(), 0); + } + OpenCGAResult result = getUserDBAdaptor(userIdOrganization).update(userId, updateParams); + + auditManager.auditUpdate(organizationId, tokenPayload.getUserId(userIdOrganization), Enums.Resource.USER, userId, "", "", "", + auditParams, new AuditRecord.Status(AuditRecord.Status.Result.SUCCESS)); + + if (options.getBoolean(ParamConstants.INCLUDE_RESULT_PARAM)) { + // Fetch updated user + OpenCGAResult tmpResult = getUserDBAdaptor(userIdOrganization).get(userId, options); + result.setResults(tmpResult.getResults()); + } + + return result; + } catch (Exception e) { + auditManager.auditUpdate(organizationId, tokenPayload.getUserId(userIdOrganization), Enums.Resource.USER, userId, "", "", "", + auditParams, new AuditRecord.Status(AuditRecord.Status.Result.ERROR, new Error(-1, "Could not update user status", + e.getMessage()))); + throw e; + } + } + /** * This method will be only callable by the system. It generates a new session id for the user. * @@ -940,7 +1056,7 @@ public String getNonExpiringToken(String organizationId, String userId, Map userOpenCGAResult = getUserDBAdaptor(organizationId).get(user, INCLUDE_ACCOUNT); + OpenCGAResult userOpenCGAResult = getUserDBAdaptor(organizationId).get(user, INCLUDE_ACCOUNT_AND_INTERNAL); if (userOpenCGAResult.getNumResults() == 1) { String authId = userOpenCGAResult.first().getAccount().getAuthentication().getId(); return authenticationFactory.getOrganizationAuthenticationManager(organizationId, authId); diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/CatalogDemo.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/CatalogDemo.java index ec210c0cd79..d56f598dabb 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/CatalogDemo.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/CatalogDemo.java @@ -17,8 +17,10 @@ package org.opencb.opencga.catalog.utils; import org.opencb.commons.datastore.core.QueryOptions; +import org.opencb.opencga.catalog.auth.authentication.JwtManager; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.managers.CatalogManager; +import org.opencb.opencga.core.common.PasswordUtils; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.models.study.Group; @@ -48,7 +50,7 @@ private CatalogDemo() { */ public static void createDemoDatabase(CatalogManager catalogManager, String organizationId, String adminPassword, boolean force) throws CatalogException { - catalogManager.installCatalogDB("HS256", catalogManager.getConfiguration().getAdmin().getSecretKey(), adminPassword, + catalogManager.installCatalogDB("HS256", PasswordUtils.getStrongRandomPassword(JwtManager.SECRET_KEY_MIN_LENGTH), adminPassword, "opencga@admin.com", force); String token = catalogManager.getUserManager().loginAsAdmin(adminPassword).getToken(); try { diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/Constants.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/Constants.java index 543502a6f87..8b1f4ac747e 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/Constants.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/Constants.java @@ -111,4 +111,6 @@ public class Constants { */ public static final String JOB_DELETED_OUTPUT_DIRECTORY = "deletedOutputFiles"; + public static final String DEFAULT_USER_EXPIRATION_DATE = "21000101000000"; + } diff --git a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java index c39d111d8c7..5e93414f08d 100644 --- a/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java +++ b/opencga-catalog/src/main/java/org/opencb/opencga/catalog/utils/ParamUtils.java @@ -174,6 +174,22 @@ public static void checkDateFormat(String creationDate, String param) throws Cat } } + public static void checkDateIsNotExpired(String dateStr, String param) throws CatalogParameterException { + if (StringUtils.isEmpty(dateStr)) { + throw CatalogParameterException.isNull(param); + } else { + // Validate date can be parsed and has the proper format + Date date = TimeUtils.toDate(dateStr); + if (date == null || dateStr.length() != 14) { + throw new CatalogParameterException("Unexpected '" + param + "' format. Expected format is 'yyyyMMddHHmmss'"); + } + if (date.before(TimeUtils.getDate())) { + throw new CatalogParameterException("The date for '" + param + "' introduced is already expired. Please, introduce a valid" + + " date in the future."); + } + } + } + public static String checkDateOrGetCurrentDate(String date, String param) throws CatalogParameterException { if (StringUtils.isEmpty(date)) { return TimeUtils.getTime(); diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java index a57747acd49..6345386e69f 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/CatalogManagerTest.java @@ -16,7 +16,7 @@ package org.opencb.opencga.catalog.managers; -import com.mongodb.BasicDBObject; +import com.fasterxml.jackson.core.JsonProcessingException; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; @@ -37,6 +37,7 @@ import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; import org.opencb.opencga.core.common.TimeUtils; +import org.opencb.opencga.core.config.Optimizations; import org.opencb.opencga.core.models.Acl; import org.opencb.opencga.core.models.AclEntry; import org.opencb.opencga.core.models.AclEntryList; @@ -49,6 +50,7 @@ import org.opencb.opencga.core.models.individual.Individual; import org.opencb.opencga.core.models.individual.IndividualUpdateParams; import org.opencb.opencga.core.models.job.*; +import org.opencb.opencga.core.models.organizations.OrganizationConfiguration; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; import org.opencb.opencga.core.models.project.DataStore; @@ -57,9 +59,7 @@ import org.opencb.opencga.core.models.project.ProjectOrganism; import org.opencb.opencga.core.models.sample.*; import org.opencb.opencga.core.models.study.*; -import org.opencb.opencga.core.models.user.Account; -import org.opencb.opencga.core.models.user.AuthenticationResponse; -import org.opencb.opencga.core.models.user.User; +import org.opencb.opencga.core.models.user.*; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; @@ -71,6 +71,7 @@ import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.*; +import static org.opencb.opencga.core.common.JacksonUtils.getUpdateObjectMapper; @Category(MediumTests.class) public class CatalogManagerTest extends AbstractManagerTest { @@ -254,6 +255,111 @@ public void anonymousUserLoginTest() throws CatalogException { catalogManager.getUserManager().loginAnonymous(org2); } + @Test + public void incrementLoginAttemptsTest() throws CatalogException { + assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getUserManager().login(organizationId, normalUserId1, "incorrect")); + User user = catalogManager.getUserManager().get(organizationId, normalUserId1, QueryOptions.empty(), ownerToken).first(); + assertEquals(1, user.getInternal().getFailedAttempts()); + assertEquals(UserStatus.READY, user.getInternal().getStatus().getId()); + + for (int i = 2; i < 5; i++) { + assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getUserManager().login(organizationId, normalUserId1, "incorrect")); + user = catalogManager.getUserManager().get(organizationId, normalUserId1, QueryOptions.empty(), ownerToken).first(); + assertEquals(i, user.getInternal().getFailedAttempts()); + assertEquals(UserStatus.READY, user.getInternal().getStatus().getId()); + } + + assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getUserManager().login(organizationId, normalUserId1, "incorrect")); + user = catalogManager.getUserManager().get(organizationId, normalUserId1, QueryOptions.empty(), ownerToken).first(); + assertEquals(5, user.getInternal().getFailedAttempts()); + assertEquals(UserStatus.BANNED, user.getInternal().getStatus().getId()); + + CatalogAuthenticationException incorrect = assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getUserManager().login(organizationId, normalUserId1, "incorrect")); + assertTrue(incorrect.getMessage().contains("banned")); + user = catalogManager.getUserManager().get(organizationId, normalUserId1, QueryOptions.empty(), ownerToken).first(); + assertEquals(5, user.getInternal().getFailedAttempts()); + assertEquals(UserStatus.BANNED, user.getInternal().getStatus().getId()); + + CatalogAuthenticationException authException = assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getUserManager().login(organizationId, normalUserId1, TestParamConstants.PASSWORD)); + assertTrue(authException.getMessage().contains("banned")); + + // Remove ban from user + catalogManager.getUserManager().changeStatus(organizationId, normalUserId1, UserStatus.READY, QueryOptions.empty(), ownerToken); + user = catalogManager.getUserManager().get(organizationId, normalUserId1, QueryOptions.empty(), ownerToken).first(); + assertEquals(0, user.getInternal().getFailedAttempts()); + assertEquals(UserStatus.READY, user.getInternal().getStatus().getId()); + + String token = catalogManager.getUserManager().login(organizationId, normalUserId1, TestParamConstants.PASSWORD).getToken(); + assertNotNull(token); + } + + @Test + public void changeUserStatusTest() throws CatalogException { + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().changeStatus(organizationId, normalUserId1, UserStatus.BANNED, QueryOptions.empty(), normalToken1)); + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().changeStatus(organizationId, normalUserId1, UserStatus.BANNED, QueryOptions.empty(), studyAdminToken1)); + assertThrows(CatalogParameterException.class, () -> catalogManager.getUserManager().changeStatus(organizationId, normalUserId1, UserStatus.BANNED, QueryOptions.empty(), ownerToken)); + catalogManager.getUserManager().changeStatus(organizationId, normalUserId1, UserStatus.SUSPENDED, QueryOptions.empty(), ownerToken); + catalogManager.getUserManager().changeStatus(organizationId, normalUserId1, UserStatus.SUSPENDED, QueryOptions.empty(), orgAdminToken1); + catalogManager.getUserManager().changeStatus(organizationId, normalUserId1, UserStatus.SUSPENDED, QueryOptions.empty(), opencgaToken); + + catalogManager.getUserManager().changeStatus(organizationId, orgAdminUserId1, UserStatus.SUSPENDED, QueryOptions.empty(), ownerToken); + CatalogAuthorizationException authException = assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().changeStatus(organizationId, orgOwnerUserId, UserStatus.SUSPENDED, QueryOptions.empty(), ownerToken)); + assertTrue(authException.getMessage().contains("own account")); + + authException = assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().changeStatus(organizationId, orgAdminUserId1, UserStatus.SUSPENDED, QueryOptions.empty(), orgAdminToken2)); + assertTrue(authException.getMessage().contains("suspend administrators")); + + CatalogAuthenticationException incorrect = assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getUserManager().login(organizationId, orgAdminUserId1, TestParamConstants.PASSWORD)); + assertTrue(incorrect.getMessage().contains("suspended")); + + catalogManager.getUserManager().changeStatus(organizationId, orgAdminUserId1, UserStatus.READY, QueryOptions.empty(), orgAdminToken2); + String token = catalogManager.getUserManager().login(organizationId, orgAdminUserId1, TestParamConstants.PASSWORD).getToken(); + assertNotNull(token); + + CatalogParameterException paramException = assertThrows(CatalogParameterException.class, () -> catalogManager.getUserManager().changeStatus(organizationId, orgAdminUserId1, "NOT_A_STATUS", QueryOptions.empty(), orgAdminToken2)); + assertTrue(paramException.getMessage().contains("Invalid status")); + + CatalogDBException dbException = assertThrows(CatalogDBException.class, () -> catalogManager.getUserManager().changeStatus(organizationId, "notAUser", UserStatus.SUSPENDED, QueryOptions.empty(), orgAdminToken2)); + assertTrue(dbException.getMessage().contains("not exist")); + } + + @Test + public void loginExpiredAccountTest() throws CatalogException { + // Expire account of normalUserId1 + ObjectMap params = new ObjectMap(UserDBAdaptor.QueryParams.ACCOUNT_EXPIRATION_DATE.key(), TimeUtils.getTime()); + catalogManager.getUserManager().getUserDBAdaptor(organizationId).update(normalUserId1, params); + + CatalogAuthenticationException authException = assertThrows(CatalogAuthenticationException.class, () -> catalogManager.getUserManager().login(organizationId, normalUserId1, TestParamConstants.PASSWORD)); + assertTrue(authException.getMessage().contains("expired")); + + // Ensure it doesn't matter whether opencga account is expired or not + catalogManager.getUserManager().getUserDBAdaptor(ParamConstants.ADMIN_ORGANIZATION).update(ParamConstants.OPENCGA_USER_ID, params); + String token = catalogManager.getUserManager().login(ParamConstants.ADMIN_ORGANIZATION, ParamConstants.OPENCGA_USER_ID, TestParamConstants.ADMIN_PASSWORD).getToken(); + assertNotNull(token); + } + + @Test + public void updateUserTest() throws JsonProcessingException, CatalogException { + UserUpdateParams userUpdateParams = new UserUpdateParams() + .setName("newName") + .setEmail("mail@mail.com"); + ObjectMap updateParams = new ObjectMap(getUpdateObjectMapper().writeValueAsString(userUpdateParams)); + User user = catalogManager.getUserManager().update(normalUserId1, updateParams, INCLUDE_RESULT, normalToken1).first(); + assertEquals(userUpdateParams.getName(), user.getName()); + assertEquals(userUpdateParams.getEmail(), user.getEmail()); + + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().update(normalUserId1, updateParams, INCLUDE_RESULT, normalToken2)); + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().update(normalUserId1, updateParams, INCLUDE_RESULT, opencgaToken)); + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().update(normalUserId1, updateParams, INCLUDE_RESULT, ownerToken)); + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().update(normalUserId1, updateParams, INCLUDE_RESULT, orgAdminToken1)); + assertThrows(CatalogAuthorizationException.class, () -> catalogManager.getUserManager().update(normalUserId1, updateParams, INCLUDE_RESULT, studyAdminToken1)); + + userUpdateParams = new UserUpdateParams() + .setEmail("notAnEmail"); + ObjectMap updateParams2 = new ObjectMap(getUpdateObjectMapper().writeValueAsString(userUpdateParams)); + assertThrows(CatalogParameterException.class, () -> catalogManager.getUserManager().update(normalUserId1, updateParams2, INCLUDE_RESULT, normalToken1)); + } + @Test public void testGetUserInfo() throws CatalogException { // OpenCGA administrator @@ -353,10 +459,6 @@ public void testModifyUser() throws CatalogException, InterruptedException, IOEx String newEmail = "new@email.ac.uk"; params.put("name", newName); - ObjectMap attributes = new ObjectMap("myBoolean", true); - attributes.put("value", 6); - attributes.put("object", new BasicDBObject("id", 1234)); - params.put("attributes", attributes); Thread.sleep(10); @@ -373,10 +475,6 @@ public void testModifyUser() throws CatalogException, InterruptedException, IOEx assertEquals(userPost.getEmail(), newEmail); catalogManager.getUserManager().login(organizationId, orgOwnerUserId, newPassword); - for (Map.Entry entry : attributes.entrySet()) { - assertEquals(userPost.getAttributes().get(entry.getKey()), entry.getValue()); - } - catalogManager.getUserManager().changePassword(organizationId, orgOwnerUserId, newPassword, TestParamConstants.PASSWORD); catalogManager.getUserManager().login(organizationId, orgOwnerUserId, TestParamConstants.PASSWORD); @@ -2232,7 +2330,7 @@ public void getEffectivePermissions() throws CatalogException { Arrays.asList(orgOwnerUserId, orgAdminUserId1, orgAdminUserId2, studyAdminUserId1, normalUserId1, normalUserId2, normalUserId3, "user5", "user6", ParamConstants.ANONYMOUS_USER_ID), Arrays.asList("user4", "user7"), aclList.getResults().get(2)); - catalogManager.getConfiguration().getOptimizations().setSimplifyPermissions(true); + catalogManager.getOrganizationManager().updateConfiguration(organizationId, new OrganizationConfiguration().setOptimizations(new Optimizations(true)), null, ownerToken); aclList = catalogManager.getAdminManager().getEffectivePermissions(studyFqn, Arrays.asList(s_7Id, s_8Id, s_9Id), Enums.Resource.SAMPLE.name(), ownerToken); assertEquals(3, aclList.getNumResults()); assertPermissions(s_7Id, Arrays.asList(orgOwnerUserId, orgAdminUserId1, orgAdminUserId2, studyAdminUserId1, normalUserId1, normalUserId2, normalUserId3, "user4", "user5", "user6", "user7", ParamConstants.ANONYMOUS_USER_ID), diff --git a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java index a6d3bd3540f..ca9323b422d 100644 --- a/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java +++ b/opencga-catalog/src/test/java/org/opencb/opencga/catalog/managers/OrganizationManagerTest.java @@ -12,15 +12,20 @@ import org.opencb.opencga.catalog.exceptions.CatalogAuthenticationException; import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; import org.opencb.opencga.catalog.exceptions.CatalogException; +import org.opencb.opencga.catalog.exceptions.CatalogParameterException; import org.opencb.opencga.catalog.utils.Constants; import org.opencb.opencga.catalog.utils.ParamUtils; import org.opencb.opencga.core.api.ParamConstants; +import org.opencb.opencga.core.common.TimeUtils; import org.opencb.opencga.core.config.AuthenticationOrigin; import org.opencb.opencga.core.config.Optimizations; import org.opencb.opencga.core.models.organizations.*; import org.opencb.opencga.core.models.project.Project; import org.opencb.opencga.core.models.study.Group; import org.opencb.opencga.core.models.study.Study; +import org.opencb.opencga.core.models.user.OrganizationUserUpdateParams; +import org.opencb.opencga.core.models.user.User; +import org.opencb.opencga.core.models.user.UserQuota; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.testclassification.duration.MediumTests; @@ -227,6 +232,71 @@ public void organizationInfoProjectsAuthTest() throws CatalogException { assertEquals(projectFqn1, organization.getProjects().get(0).getFqn()); } + @Test + public void validateUserUpdateParamsTest() { + OrganizationUserUpdateParams expiredDateParam = new OrganizationUserUpdateParams() + .setAccount(new OrganizationUserUpdateParams.Account("20200101100000")); + CatalogParameterException exception = assertThrows(CatalogParameterException.class, () -> catalogManager.getOrganizationManager() + .updateUser(organizationId, normalUserId1, expiredDateParam, INCLUDE_RESULT, ownerToken)); + assertTrue(exception.getMessage().contains("expired")); + + OrganizationUserUpdateParams invalidMailParam = new OrganizationUserUpdateParams() + .setEmail("invalidEmail"); + exception = assertThrows(CatalogParameterException.class, () -> catalogManager.getOrganizationManager().updateUser(organizationId, + normalUserId1, invalidMailParam, INCLUDE_RESULT, ownerToken)); + assertTrue(exception.getMessage().contains("not valid")); + } + + @Test + public void updateUserInformationTest() throws CatalogException { + Date date = TimeUtils.getDate(); + Calendar cl = Calendar.getInstance(); + cl.setTime(date); + cl.add(Calendar.YEAR, 1); + String expirationTime = TimeUtils.getTime(cl.getTime()); + + OrganizationUserUpdateParams userUpdateParams = new OrganizationUserUpdateParams() + .setName("newName") + .setEmail("mail@mail.com") + .setAccount(new OrganizationUserUpdateParams.Account(expirationTime)) + .setQuota(new UserQuota(1000, 1000000, 1000, 1000000)) + .setAttributes(Collections.singletonMap("key1", "value1")); + updateAndAssertChanges(organizationId, userUpdateParams, opencgaToken); + + userUpdateParams = new OrganizationUserUpdateParams() + .setName("newName2") + .setEmail("mai2l@mail.com") + .setAccount(new OrganizationUserUpdateParams.Account(expirationTime)) + .setQuota(new UserQuota(1001, 1010000, 1010, 1100000)) + .setAttributes(Collections.singletonMap("key2", "value2")); + updateAndAssertChanges(null, userUpdateParams, ownerToken); + + userUpdateParams = new OrganizationUserUpdateParams() + .setName("newName3") + .setEmail("mai3l@mail.com") + .setAccount(new OrganizationUserUpdateParams.Account(expirationTime)) + .setQuota(new UserQuota(3001, 1010300, 1013, 1300000)) + .setAttributes(Collections.singletonMap("key3", "value3")); + updateAndAssertChanges(null, userUpdateParams, orgAdminToken1); + + thrown.expect(CatalogAuthorizationException.class); + catalogManager.getOrganizationManager().updateUser(organizationId, normalUserId1, userUpdateParams, INCLUDE_RESULT, normalToken1); + } + + private void updateAndAssertChanges(String orgId, OrganizationUserUpdateParams userUpdateParams, String token) throws CatalogException { + User user = catalogManager.getOrganizationManager().updateUser(orgId, normalUserId1, userUpdateParams, INCLUDE_RESULT, token).first(); + assertEquals(userUpdateParams.getName(), user.getName()); + assertEquals(userUpdateParams.getEmail(), user.getEmail()); + assertEquals(userUpdateParams.getAccount().getExpirationDate(), user.getAccount().getExpirationDate()); + assertEquals(userUpdateParams.getQuota().getCpuUsage(), user.getQuota().getCpuUsage()); + assertEquals(userUpdateParams.getQuota().getDiskUsage(), user.getQuota().getDiskUsage()); + assertEquals(userUpdateParams.getQuota().getMaxCpu(), user.getQuota().getMaxCpu()); + assertEquals(userUpdateParams.getQuota().getMaxDisk(), user.getQuota().getMaxDisk()); + for (String key : userUpdateParams.getAttributes().keySet()) { + assertEquals(userUpdateParams.getAttributes().get(key), user.getAttributes().get(key)); + } + } + @Test public void migrationExecutionProjectionTest() throws CatalogException { Organization organization = catalogManager.getOrganizationManager().get(organizationId, null, ownerToken).first(); diff --git a/opencga-catalog/src/test/resources/configuration-test.yml b/opencga-catalog/src/test/resources/configuration-test.yml index 7b84c12702c..2d7cbe0861a 100644 --- a/opencga-catalog/src/test/resources/configuration-test.yml +++ b/opencga-catalog/src/test/resources/configuration-test.yml @@ -6,9 +6,7 @@ databasePrefix: "opencga_test" workspace: "/tmp/opencga/sessions" jobDir: "/tmp/opencga/JOBS" - -admin: - secretKey: "asidnadh19rh230qncfascd1.rffzasf.asd.ad.12ddeASDAsd12e1d.adsx" +maxLoginAttempts: 5 audit: manager: "" # Java manager of the audit implementation to be used to audit. If empty, catalog database will be used. @@ -72,29 +70,6 @@ catalog: password: "" options: authenticationDatabase: "" - searchEngine: ## Solr configuration, by default is the same than storage - hosts: - - "http://localhost:8983/solr/" - user: "" - password: "" - options: - mode: "core" - timeout: 30000 - insertBatchSize: 2000 - -authentication: - expiration: 1000 -#LDAP configuration example -# authenticationOrigins: -# - id: ldap # Any id -# type: LDAP # At the moment, we only support LDAP -# host: ldap://localhost:9000 -# options: -# usersSearch: dc=ge,dc=co,dc=uk # Base search to look for the users -# groupsSearch: ou=general,ou=groups,dc=ge,dc=co,dc=uk # Base search to look for the groups - -optimizations: - simplifyPermissions: false server: rest: diff --git a/opencga-client/src/main/R/R/AllGenerics.R b/opencga-client/src/main/R/R/AllGenerics.R index 195b5ad36f3..5b3e3d890ce 100644 --- a/opencga-client/src/main/R/R/AllGenerics.R +++ b/opencga-client/src/main/R/R/AllGenerics.R @@ -1,6 +1,6 @@ # ############################################################################## ## OrganizationClient -setGeneric("organizationClient", function(OpencgaR, id, organization, endpointName, params=NULL, ...) +setGeneric("organizationClient", function(OpencgaR, id, organization, user, endpointName, params=NULL, ...) standardGeneric("organizationClient")) # ############################################################################## diff --git a/opencga-client/src/main/R/R/Organization-methods.R b/opencga-client/src/main/R/R/Organization-methods.R index aeee2457ec2..da241e16fb5 100644 --- a/opencga-client/src/main/R/R/Organization-methods.R +++ b/opencga-client/src/main/R/R/Organization-methods.R @@ -24,6 +24,8 @@ #' | searchNotes | /{apiVersion}/organizations/notes/search | include, exclude, creationDate, modificationDate, id, scope, visibility, uuid, userId, tags, version | #' | deleteNotes | /{apiVersion}/organizations/notes/{id}/delete | id[*], includeResult | #' | updateNotes | /{apiVersion}/organizations/notes/{id}/update | include, exclude, id[*], includeResult, body[*] | +#' | userUpdateStatus | /{apiVersion}/organizations/user/{user}/status/update | include, exclude, user[*], organization, includeResult, body[*] | +#' | updateUser | /{apiVersion}/organizations/user/{user}/update | include, exclude, user[*], organization, includeResult, body[*] | #' | updateConfiguration | /{apiVersion}/organizations/{organization}/configuration/update | include, exclude, organization[*], includeResult, authenticationOriginsAction, body[*] | #' | info | /{apiVersion}/organizations/{organization}/info | include, exclude, organization[*] | #' | update | /{apiVersion}/organizations/{organization}/update | include, exclude, organization[*], includeResult, adminsAction, body[*] | @@ -34,7 +36,7 @@ #' [*]: Required parameter #' @export -setMethod("organizationClient", "OpencgaR", function(OpencgaR, id, organization, endpointName, params=NULL, ...) { +setMethod("organizationClient", "OpencgaR", function(OpencgaR, id, organization, user, endpointName, params=NULL, ...) { switch(endpointName, #' @section Endpoint /{apiVersion}/organizations/create: @@ -88,6 +90,29 @@ setMethod("organizationClient", "OpencgaR", function(OpencgaR, id, organization, updateNotes=fetchOpenCGA(object=OpencgaR, category="organizations", categoryId=NULL, subcategory="notes", subcategoryId=id, action="update", params=params, httpMethod="POST", as.queryParam=NULL, ...), + #' @section Endpoint /{apiVersion}/organizations/user/{user}/status/update: + #' Update the user status. + #' @param include Fields included in the response, whole JSON path must be provided. + #' @param exclude Fields excluded in the response, whole JSON path must be provided. + #' @param user User ID. + #' @param organization Organization id. + #' @param includeResult Flag indicating to include the created or updated document result in the response. + #' @param data JSON containing the User fields to be updated. + userUpdateStatus=fetchOpenCGA(object=OpencgaR, category="organizations/user", categoryId=user, + subcategory="status", subcategoryId=NULL, action="update", params=params, httpMethod="POST", + as.queryParam=NULL, ...), + + #' @section Endpoint /{apiVersion}/organizations/user/{user}/update: + #' Update the user information. + #' @param include Fields included in the response, whole JSON path must be provided. + #' @param exclude Fields excluded in the response, whole JSON path must be provided. + #' @param user User ID. + #' @param organization Organization id. + #' @param includeResult Flag indicating to include the created or updated document result in the response. + #' @param data JSON containing the User fields to be updated. + updateUser=fetchOpenCGA(object=OpencgaR, category="organizations", categoryId=NULL, subcategory="user", + subcategoryId=user, action="update", params=params, httpMethod="POST", as.queryParam=NULL, ...), + #' @section Endpoint /{apiVersion}/organizations/{organization}/configuration/update: #' Update the Organization configuration attributes. #' @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/OrganizationClient.java b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/OrganizationClient.java index e925df3b441..67d55693b52 100644 --- a/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/OrganizationClient.java +++ b/opencga-client/src/main/java/org/opencb/opencga/client/rest/clients/OrganizationClient.java @@ -27,6 +27,9 @@ import org.opencb.opencga.core.models.organizations.OrganizationConfiguration; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; +import org.opencb.opencga.core.models.user.OrganizationUserUpdateParams; +import org.opencb.opencga.core.models.user.User; +import org.opencb.opencga.core.models.user.UserStatusUpdateParams; import org.opencb.opencga.core.response.RestResponse; @@ -135,6 +138,42 @@ public RestResponse updateNotes(String id, NoteUpdateParams data, ObjectMa return execute("organizations", null, "notes", id, "update", params, POST, Note.class); } + /** + * Update the user status. + * @param user User ID. + * @param data JSON containing the User fields to be updated. + * @param params Map containing any of the following optional parameters. + * include: Fields included in the response, whole JSON path must be provided. + * exclude: Fields excluded in the response, whole JSON path must be provided. + * organization: Organization id. + * includeResult: Flag indicating to include the created or updated document result in the response. + * @return a RestResponse object. + * @throws ClientException ClientException if there is any server error. + */ + public RestResponse userUpdateStatus(String user, UserStatusUpdateParams data, ObjectMap params) throws ClientException { + params = params != null ? params : new ObjectMap(); + params.put("body", data); + return execute("organizations/user", user, "status", null, "update", params, POST, User.class); + } + + /** + * Update the user information. + * @param user User ID. + * @param data JSON containing the User fields to be updated. + * @param params Map containing any of the following optional parameters. + * include: Fields included in the response, whole JSON path must be provided. + * exclude: Fields excluded in the response, whole JSON path must be provided. + * organization: Organization id. + * includeResult: Flag indicating to include the created or updated document result in the response. + * @return a RestResponse object. + * @throws ClientException ClientException if there is any server error. + */ + public RestResponse updateUser(String user, OrganizationUserUpdateParams data, ObjectMap params) throws ClientException { + params = params != null ? params : new ObjectMap(); + params.put("body", data); + return execute("organizations", null, "user", user, "update", params, POST, User.class); + } + /** * Update the Organization configuration attributes. * @param organization Organization id. diff --git a/opencga-client/src/main/javascript/Organization.js b/opencga-client/src/main/javascript/Organization.js index a556f706193..1580a4a7828 100644 --- a/opencga-client/src/main/javascript/Organization.js +++ b/opencga-client/src/main/javascript/Organization.js @@ -105,6 +105,36 @@ export default class Organization extends OpenCGAParentClass { return this._post("organizations", null, "notes", id, "update", data, params); } + /** Update the user status + * @param {String} user - User ID. + * @param {Object} data - JSON containing the User fields to be updated. + * @param {Object} [params] - The Object containing the following optional parameters: + * @param {String} [params.include] - Fields included in the response, whole JSON path must be provided. + * @param {String} [params.exclude] - Fields excluded in the response, whole JSON path must be provided. + * @param {String} [params.organization] - Organization id. + * @param {Boolean} [params.includeResult = "false"] - Flag indicating to include the created or updated document result in the response. + * The default value is false. + * @returns {Promise} Promise object in the form of RestResponse instance. + */ + userUpdateStatus(user, data, params) { + return this._post("organizations/user", user, "status", null, "update", data, params); + } + + /** Update the user information + * @param {String} user - User ID. + * @param {Object} data - JSON containing the User fields to be updated. + * @param {Object} [params] - The Object containing the following optional parameters: + * @param {String} [params.include] - Fields included in the response, whole JSON path must be provided. + * @param {String} [params.exclude] - Fields excluded in the response, whole JSON path must be provided. + * @param {String} [params.organization] - Organization id. + * @param {Boolean} [params.includeResult = "false"] - Flag indicating to include the created or updated document result in the response. + * The default value is false. + * @returns {Promise} Promise object in the form of RestResponse instance. + */ + updateUser(user, data, params) { + return this._post("organizations", null, "user", user, "update", data, params); + } + /** Update the Organization configuration attributes * @param {String} organization - Organization id. * @param {Object} data - JSON containing the params to be updated. diff --git a/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py b/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py index bdeac02be52..0c695ca0a0e 100644 --- a/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py +++ b/opencga-client/src/main/python/pyopencga/rest_clients/organization_client.py @@ -110,6 +110,44 @@ def update_notes(self, id, data=None, **options): return self._post(category='organizations', resource='update', subcategory='notes', second_query_id=id, data=data, **options) + def user_update_status(self, user, data=None, **options): + """ + Update the user status. + PATH: /{apiVersion}/organizations/user/{user}/status/update + + :param dict data: JSON containing the User fields to be updated. + (REQUIRED) + :param str user: User ID. (REQUIRED) + :param str include: Fields included in the response, whole JSON path + must be provided. + :param str exclude: Fields excluded in the response, whole JSON path + must be provided. + :param str organization: Organization id. + :param bool include_result: Flag indicating to include the created or + updated document result in the response. + """ + + return self._post(category='organizations/user', resource='update', query_id=user, subcategory='status', data=data, **options) + + def update_user(self, user, data=None, **options): + """ + Update the user information. + PATH: /{apiVersion}/organizations/user/{user}/update + + :param dict data: JSON containing the User fields to be updated. + (REQUIRED) + :param str user: User ID. (REQUIRED) + :param str include: Fields included in the response, whole JSON path + must be provided. + :param str exclude: Fields excluded in the response, whole JSON path + must be provided. + :param str organization: Organization id. + :param bool include_result: Flag indicating to include the created or + updated document result in the response. + """ + + return self._post(category='organizations', resource='update', subcategory='user', second_query_id=user, data=data, **options) + def update_configuration(self, organization, data=None, **options): """ Update the Organization configuration attributes. diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java b/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java index bf62bb7c09f..0a5a1ad4f9f 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/common/MailUtils.java @@ -16,8 +16,6 @@ package org.opencb.opencga.core.common; -import org.opencb.opencga.core.models.user.User; -import org.opencb.opencga.core.response.OpenCGAResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,8 +32,6 @@ public class MailUtils { private static final Logger logger = LoggerFactory.getLogger(MailUtils.class); - - public static void sendResetPasswordMail(String to, String newPassword, final String mailUser, final String mailPassword, String mailHost, String mailPort, String userId) throws Exception { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/common/TimeUtils.java b/opencga-core/src/main/java/org/opencb/opencga/core/common/TimeUtils.java index 65c2cfda80b..da6aa39f1f6 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/common/TimeUtils.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/common/TimeUtils.java @@ -127,6 +127,14 @@ public static Date add1MonthtoDate(Date date) { return new Date(cal.getTimeInMillis()); } + public static Date add1YeartoDate(Date date) { + Calendar cal = new GregorianCalendar(); + cal.setTime(date); + cal.setTimeInMillis(date.getTime()); + cal.add(Calendar.YEAR, 1); + return new Date(cal.getTimeInMillis()); + } + public static Date toDate(String dateStr) { Date date = null; try { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Catalog.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Catalog.java index 65fe5b7a4b8..0df3425dfba 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Catalog.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Catalog.java @@ -16,27 +16,33 @@ package org.opencb.opencga.core.config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Created by pfurio on 01/02/17. */ public class Catalog { private DatabaseCredentials database; - private DatabaseCredentials searchEngine; + + private static final Logger logger; + + static { + logger = LoggerFactory.getLogger(Catalog.class); + } public Catalog() { } - public Catalog(DatabaseCredentials database, DatabaseCredentials searchEngine) { + public Catalog(DatabaseCredentials database) { this.database = database; - this.searchEngine = searchEngine; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Catalog{"); sb.append("database=").append(database); - sb.append(", searchEngine=").append(searchEngine); sb.append('}'); return sb.toString(); } @@ -50,12 +56,15 @@ public Catalog setDatabase(DatabaseCredentials database) { return this; } + @Deprecated public DatabaseCredentials getSearchEngine() { - return searchEngine; + return null; } + @Deprecated public Catalog setSearchEngine(DatabaseCredentials searchEngine) { - this.searchEngine = searchEngine; + logger.warn("Ignored configuration option 'configuration.yml#catalog.searchEngine' with value '{}'." + + " The option was deprecated and removed.", searchEngine); return this; } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java b/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java index be957b160a7..600def109ff 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/config/Configuration.java @@ -40,14 +40,12 @@ public class Configuration { */ private String logDir; - @Deprecated - private boolean openRegister; - private String databasePrefix; private String workspace; private String jobDir; - private Admin admin; + private int maxLoginAttempts; + private Monitor monitor; private HealthCheck healthCheck; private Audit audit; @@ -59,15 +57,12 @@ public class Configuration { private Analysis analysis; private Panel panel; - private Optimizations optimizations; - private ServerConfiguration server; - private Authentication authentication; - - private static Logger logger; private static final Set reportedFields = new HashSet<>(); + private static final Logger logger; + private static final String DEFAULT_CONFIGURATION_FORMAT = "yaml"; static { @@ -75,7 +70,6 @@ public class Configuration { } public Configuration() { - admin = new Admin(); monitor = new Monitor(); healthCheck = new HealthCheck(); audit = new Audit(); @@ -84,9 +78,7 @@ public Configuration() { catalog = new Catalog(); analysis = new Analysis(); panel = new Panel(); - optimizations = new Optimizations(); server = new ServerConfiguration(); - authentication = new Authentication(); } public void serialize(OutputStream configurationOututStream) throws IOException { @@ -122,11 +114,19 @@ public static Configuration load(InputStream configurationInputStream, String fo throw new IOException("Configuration file could not be parsed: " + e.getMessage(), e); } + addDefaultValueIfMissing(configuration); + // We must always overwrite configuration with environment parameters overwriteWithEnvironmentVariables(configuration); return configuration; } + private static void addDefaultValueIfMissing(Configuration configuration) { + if (configuration.getMaxLoginAttempts() <= 0) { + configuration.setMaxLoginAttempts(5); + } + } + private static void overwriteWithEnvironmentVariables(Configuration configuration) { Map envVariables = System.getenv(); for (String variable : envVariables.keySet()) { @@ -146,6 +146,9 @@ private static void overwriteWithEnvironmentVariables(Configuration configuratio case "OPENCGA_MONITOR_PORT": configuration.getMonitor().setPort(Integer.parseInt(value)); break; + case "OPENCGA.MAX_LOGIN_ATTEMPTS": + configuration.setMaxLoginAttempts(Integer.parseInt(value)); + break; case "OPENCGA_EXECUTION_MODE": case "OPENCGA_EXECUTION_ID": configuration.getAnalysis().getExecution().setId(value); @@ -177,18 +180,6 @@ private static void overwriteWithEnvironmentVariables(Configuration configuratio case "OPENCGA_CATALOG_DB_CONNECTIONS_PER_HOST": configuration.getCatalog().getDatabase().getOptions().put("connectionsPerHost", value); break; - case "OPENCGA_CATALOG_SEARCH_HOST": - configuration.getCatalog().getSearchEngine().setHosts(Collections.singletonList(value)); - break; - case "OPENCGA_CATALOG_SEARCH_TIMEOUT": - configuration.getCatalog().getSearchEngine().getOptions().put("timeout", value); - break; - case "OPENCGA_CATALOG_SEARCH_BATCH": - configuration.getCatalog().getSearchEngine().getOptions().put("insertBatchSize", value); - break; - case "OPENCGA_OPTIMIZATIONS_SIMPLIFY_PERMISSIONS": - configuration.getOptimizations().setSimplifyPermissions(Boolean.parseBoolean(value)); - break; case "OPENCGA_SERVER_REST_PORT": configuration.getServer().getRest().setPort(Integer.parseInt(value)); break; @@ -219,16 +210,17 @@ public String toString() { sb.append(", logDir='").append(logDir).append('\''); sb.append(", databasePrefix='").append(databasePrefix).append('\''); sb.append(", workspace='").append(workspace).append('\''); - sb.append(", admin=").append(admin); + sb.append(", jobDir='").append(jobDir).append('\''); + sb.append(", maxLoginAttempts=").append(maxLoginAttempts); sb.append(", monitor=").append(monitor); + sb.append(", healthCheck=").append(healthCheck); sb.append(", audit=").append(audit); sb.append(", hooks=").append(hooks); sb.append(", email=").append(email); sb.append(", catalog=").append(catalog); + sb.append(", analysis=").append(analysis); sb.append(", panel=").append(panel); - sb.append(", optimizations=").append(optimizations); sb.append(", server=").append(server); - sb.append(", authentication=").append(authentication); sb.append('}'); return sb.toString(); } @@ -263,13 +255,13 @@ public Configuration setLogFile(String logFile) { } @Deprecated - public boolean isOpenRegister() { - return openRegister; + public Boolean isOpenRegister() { + return null; } @Deprecated public Configuration setOpenRegister(boolean openRegister) { - this.openRegister = openRegister; + reportUnusedField("configuration.yml#openRegister", openRegister); return this; } @@ -300,12 +292,23 @@ public Configuration setJobDir(String jobDir) { return this; } + public int getMaxLoginAttempts() { + return maxLoginAttempts; + } + + public Configuration setMaxLoginAttempts(int maxLoginAttempts) { + this.maxLoginAttempts = maxLoginAttempts; + return this; + } + + @Deprecated public Admin getAdmin() { - return admin; + return null; } + @Deprecated public Configuration setAdmin(Admin admin) { - this.admin = admin; + reportUnusedField("configuration.yml#admin", admin); return this; } @@ -383,12 +386,14 @@ public Configuration setPanel(Panel panel) { return this; } + @Deprecated public Optimizations getOptimizations() { - return optimizations; + return null; } + @Deprecated public Configuration setOptimizations(Optimizations optimizations) { - this.optimizations = optimizations; + reportUnusedField("configuration.yml#optimizations", optimizations); return this; } @@ -401,12 +406,15 @@ public Configuration setServer(ServerConfiguration server) { return this; } + @Deprecated public Authentication getAuthentication() { - return authentication; + return null; } - public void setAuthentication(Authentication authentication) { - this.authentication = authentication; + @Deprecated + public Configuration setAuthentication(Authentication authentication) { + reportUnusedField("configuration.yml#authentication", authentication); + return this; } public HealthCheck getHealthCheck() { diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationConfiguration.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationConfiguration.java index c8f3e849c89..cda477b2acc 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationConfiguration.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/organizations/OrganizationConfiguration.java @@ -8,15 +8,17 @@ public class OrganizationConfiguration { private List authenticationOrigins; + private String defaultUserExpirationDate; private Optimizations optimizations; private TokenConfiguration token; public OrganizationConfiguration() { } - public OrganizationConfiguration(List authenticationOrigins, Optimizations optimizations, - TokenConfiguration token) { + public OrganizationConfiguration(List authenticationOrigins, String defaultUserExpirationDate, + Optimizations optimizations, TokenConfiguration token) { this.authenticationOrigins = authenticationOrigins; + this.defaultUserExpirationDate = defaultUserExpirationDate; this.optimizations = optimizations; this.token = token; } @@ -25,6 +27,7 @@ public OrganizationConfiguration(List authenticationOrigin public String toString() { final StringBuilder sb = new StringBuilder("OrganizationConfiguration{"); sb.append("authenticationOrigins=").append(authenticationOrigins); + sb.append(", defaultUserExpirationDate='").append(defaultUserExpirationDate).append('\''); sb.append(", optimizations=").append(optimizations); sb.append(", token=").append(token); sb.append('}'); @@ -40,6 +43,15 @@ public OrganizationConfiguration setAuthenticationOrigins(List attributes; + + public OrganizationUserUpdateParams() { + } + + public OrganizationUserUpdateParams(String name, String email, UserQuota quota, Account account, Map attributes) { + super(name, email); + this.quota = quota; + this.account = account; + this.attributes = attributes; + } + + @JsonIgnore + public ObjectMap getUpdateMap() throws JsonProcessingException { + return new ObjectMap(getUpdateObjectMapper().writeValueAsString(this)); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("OrganizationUserUpdateParams{"); + sb.append("quota=").append(quota); + sb.append(", account=").append(account); + sb.append(", attributes=").append(attributes); + sb.append('}'); + return sb.toString(); + } + + public UserQuota getQuota() { + return quota; + } + + public OrganizationUserUpdateParams setQuota(UserQuota quota) { + this.quota = quota; + return this; + } + + public Account getAccount() { + return account; + } + + public OrganizationUserUpdateParams setAccount(Account account) { + this.account = account; + return this; + } + + public Map getAttributes() { + return attributes; + } + + public OrganizationUserUpdateParams setAttributes(Map attributes) { + this.attributes = attributes; + return this; + } + + @Override + public OrganizationUserUpdateParams setName(String name) { + super.setName(name); + return this; + } + + @Override + public OrganizationUserUpdateParams setEmail(String email) { + super.setEmail(email); + return this; + } + + public static class Account { + private String expirationDate; + + public Account() { + } + + public Account(String expirationDate) { + this.expirationDate = expirationDate; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Account{"); + sb.append("expirationDate='").append(expirationDate).append('\''); + sb.append('}'); + return sb.toString(); + } + + public String getExpirationDate() { + return expirationDate; + } + + public Account setExpirationDate(String expirationDate) { + this.expirationDate = expirationDate; + return this; + } + } + +} diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserInternal.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserInternal.java index 9a4b5fc9e23..329db613b5b 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserInternal.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserInternal.java @@ -16,21 +16,34 @@ package org.opencb.opencga.core.models.user; -public class UserInternal { +import org.opencb.opencga.core.common.TimeUtils; +import org.opencb.opencga.core.models.common.Internal; + +public class UserInternal extends Internal { private UserStatus status; + private int failedAttempts; public UserInternal() { } public UserInternal(UserStatus status) { + this(TimeUtils.getTime(), TimeUtils.getTime(), status); + } + + public UserInternal(String registrationDate, String lastModified, UserStatus status) { + super(null, registrationDate, lastModified); this.status = status; + this.failedAttempts = 0; } @Override public String toString() { final StringBuilder sb = new StringBuilder("UserInternal{"); sb.append("status=").append(status); + sb.append(", failedAttempts=").append(failedAttempts); + sb.append(", registrationDate='").append(registrationDate).append('\''); + sb.append(", lastModified='").append(lastModified).append('\''); sb.append('}'); return sb.toString(); } @@ -43,4 +56,13 @@ public UserInternal setStatus(UserStatus status) { this.status = status; return this; } + + public int getFailedAttempts() { + return failedAttempts; + } + + public UserInternal setFailedAttempts(int failedAttempts) { + this.failedAttempts = failedAttempts; + return this; + } } diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserStatus.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserStatus.java index bf497bd00f0..b9220d8fdce 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserStatus.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserStatus.java @@ -24,8 +24,9 @@ public class UserStatus extends InternalStatus { public static final String BANNED = "BANNED"; + public static final String SUSPENDED = "SUSPENDED"; - public static final List STATUS_LIST = Arrays.asList(READY, DELETED, BANNED); + public static final List STATUS_LIST = Arrays.asList(READY, DELETED, BANNED, SUSPENDED); public UserStatus(String status, String message) { if (isValid(status)) { @@ -47,7 +48,7 @@ public static boolean isValid(String status) { if (InternalStatus.isValid(status)) { return true; } - if (status != null && (status.equals(BANNED))) { + if (status != null && (status.equals(BANNED) || status.equals(SUSPENDED))) { return true; } return false; diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserStatusUpdateParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserStatusUpdateParams.java new file mode 100644 index 00000000000..5c0356ded8e --- /dev/null +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserStatusUpdateParams.java @@ -0,0 +1,30 @@ +package org.opencb.opencga.core.models.user; + +public class UserStatusUpdateParams { + + private String status; + + public UserStatusUpdateParams() { + } + + public UserStatusUpdateParams(String status) { + this.status = status; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("UserStatusUpdateParams{"); + sb.append("status='").append(status).append('\''); + sb.append('}'); + return sb.toString(); + } + + public String getStatus() { + return status; + } + + public UserStatusUpdateParams setStatus(String status) { + this.status = status; + return this; + } +} diff --git a/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserUpdateParams.java b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserUpdateParams.java index d9c44a665c8..eda30607fbb 100644 --- a/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserUpdateParams.java +++ b/opencga-core/src/main/java/org/opencb/opencga/core/models/user/UserUpdateParams.java @@ -16,21 +16,17 @@ package org.opencb.opencga.core.models.user; -import java.util.Map; - public class UserUpdateParams { private String name; private String email; - private Map attributes; public UserUpdateParams() { } - public UserUpdateParams(String name, String email, Map attributes) { + public UserUpdateParams(String name, String email) { this.name = name; this.email = email; - this.attributes = attributes; } @Override @@ -38,7 +34,6 @@ public String toString() { final StringBuilder sb = new StringBuilder("UserUpdateParams{"); sb.append("name='").append(name).append('\''); sb.append(", email='").append(email).append('\''); - sb.append(", attributes=").append(attributes); sb.append('}'); return sb.toString(); } @@ -61,12 +56,4 @@ public UserUpdateParams setEmail(String email) { return this; } - public Map getAttributes() { - return attributes; - } - - public UserUpdateParams setAttributes(Map attributes) { - this.attributes = attributes; - return this; - } } diff --git a/opencga-core/src/main/resources/configuration.yml b/opencga-core/src/main/resources/configuration.yml index 6adc576a258..d4ffe7529db 100644 --- a/opencga-core/src/main/resources/configuration.yml +++ b/opencga-core/src/main/resources/configuration.yml @@ -6,6 +6,9 @@ databasePrefix: ${OPENCGA.DB.PREFIX} workspace: ${OPENCGA.USER.WORKSPACE} jobDir: ${OPENCGA.USER.WORKSPACE}/jobs +# Maximum number of login attempts before banning a user account +maxLoginAttempts: ${OPENCGA.MAX_LOGIN_ATTEMPTS} + panel: host: "http://resources.opencb.org/opencb/opencga/disease-panels" @@ -19,56 +22,6 @@ catalog: options: authenticationDatabase: ${OPENCGA.CATALOG.DB.AUTHENTICATION_DATABASE} connectionsPerHost: ${OPENCGA.CATALOG.DB.CONNECTIONS_PER_HOST} - ## Solr Search engine configuration, by default is the same than storage - searchEngine: - # List of hosts pointing either to the Solr nodes directly using a complete URL or to the zookeper nodes with HOST:PORT - # Example for Solr connection: http://opencga-solr-01.zone:8983/solr - # Example for Zookeeper connection: opencga-zookeeper-01:2181 <-- Recommended for replicated installations - hosts: - - ${OPENCGA.CATALOG.SEARCH.HOST} - user: "" - password: "" - options: - mode: "cloud" - timeout: ${OPENCGA.CATALOG.SEARCH.TIMEOUT} - insertBatchSize: ${OPENCGA.CATALOG.SEARCH.BATCH} - -## We support multiple Authentication providers, if none is provided then we use an internal authentication implementation -authentication: - # Session expiration time in seconds - expiration: 3600 - authenticationOrigins: -# Custom LDAP | LDAPS configuration example -# - id: ldaps # Any id -# type: LDAP -# host: ldaps://localhost:636 # or ldap://localhost:123 -# options: -# authUserId: "" # Auth user id and password if querying the LDAP system requires authentication. By default, empty. -# authPassword: "" # Auth user id and password if querying the LDAP system requires authentication. By default, empty. -# usersSearch: dc=ge,dc=co,dc=uk # Mandatory field. Base search to look for users. By default, empty. -# groupsSearch: ou=general,ou=groups,dc=ge,dc=co,dc=uk # Mandatory field. Base search to look for groups. By default, empty. -# fullNameKey: displayname # Key to get the user's full name when importing users. By default, 'displayname'. -# memberKey: member # Key within groups to extract the user id. By default, 'member'. -# dnKey: dn # Key to get the user's DN or RDN unique identifier. By default, 'dn'. -# dnFormat: "%s" # Formatter to extract the user's DN if it is contained within a larger string. By default, '%s' to take the whole string. -# uidKey: uid # Key to get the user id to be used in OpenCGA. By default, 'uid'. -# uidFormat: "%s" # Formatter to extract the user id if the uid is contained within a larger string. By default, '%s' to take the whole string. -# sslInvalidCertificatesAllowed: false # If using LDAPs with a custom certificate, set to true to accept any certificate. By default, false. -# connectionTimeout: 500 # Connection timeout value. Default: 500 ms -# readTimeout: 1000 # Read timeout value. Default: 1000 ms - -# Azure AD configuration example -# - id: aad # Any id -# type: AzureAD -# host: -# options: -# tenantId: xxxx # Mandatory. Tenant id -# authClientId: xxxx # Mandatory. Client id of the client with permissions to authenticate users. -# syncClientId: xxxx # Mandatory. Client id of the client with permissions to inspect active directory. -# syncSecretKey: xxxx # Mandatory: Secret key of the client with permissions to inspect active directory. -# filters: tokenField1=aa,bb,cc;tokenField2=aa,bb,cc # Optional. Filters to be applied. OpenCGA will check if tokenField1 = aa or bb -# # or cc and tokenField2 = aa or bb or cc. If any of the filters don't succeed, even if the user is properly authenticated -# # in AAD, the user will not be able to generate a token and login in OpenCGA. server: rest: @@ -88,9 +41,6 @@ server: grpc: port: ${OPENCGA.SERVER.GRPC.PORT} -optimizations: - simplifyPermissions: ${OPENCGA_OPTIMIZATIONS_SIMPLIFY_PERMISSIONS} - audit: manager: "" # Java manager of the audit implementation to be used to audit. If empty, catalog database will be used. maxDocuments: 20000000 # Maximum number of documents that will be created in the audit collection. diff --git a/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java b/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java index df649837113..618797dc5c0 100644 --- a/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java +++ b/opencga-core/src/test/java/org/opencb/opencga/core/config/ConfigurationTest.java @@ -40,12 +40,6 @@ public void testDefault() throws IOException { configuration.setLogLevel("INFO"); configuration.setWorkspace("/opt/opencga/sessions"); - - configuration.setAdmin(new Admin()); - - Authentication authentication = new Authentication(); - configuration.setAuthentication(authentication); - configuration.setMonitor(new Monitor()); configuration.getAnalysis().setExecution(new Execution()); @@ -54,14 +48,6 @@ public void testDefault() throws IOException { new HookConfiguration("name", "~*SV*", HookConfiguration.Stage.CREATE, HookConfiguration.Action.ADD, "tags", "SV") )))); - List authenticationOriginList = new ArrayList<>(); - authenticationOriginList.add(new AuthenticationOrigin()); - Map myMap = new HashMap<>(); - myMap.put("ou", "People"); - authenticationOriginList.add(new AuthenticationOrigin("opencga", AuthenticationOrigin.AuthenticationType.LDAP, - "ldap://10.10.0.20:389", myMap)); - configuration.getAuthentication().setAuthenticationOrigins(authenticationOriginList); - Email emailServer = new Email("localhost", "", "", "", "", false); configuration.setEmail(emailServer); diff --git a/opencga-core/src/test/resources/configuration-test.yml b/opencga-core/src/test/resources/configuration-test.yml index dad67f8c5dc..2c3736938b7 100644 --- a/opencga-core/src/test/resources/configuration-test.yml +++ b/opencga-core/src/test/resources/configuration-test.yml @@ -4,6 +4,7 @@ logDir: null databasePrefix: "opencga_test" workspace: "/tmp/opencga/sessions" +maxLoginAttempts: 5 audit: manager: "" # Java manager of the audit implementation to be used to audit. If empty, catalog database will be used. @@ -32,16 +33,6 @@ catalog: password: "" options: authenticationDatabase: "" -authentication: - expiration: 1000 -#LDAP configuration example - authenticationOrigins: - - id: ldap # Any id - type: LDAP # At the moment, we only support LDAP - host: ldap://localhost:9000 - options: - usersSearch: dc=ge,dc=co,dc=uk # Base search to look for the users - groupsSearch: ou=general,ou=groups,dc=ge,dc=co,dc=uk # Base search to look for the groups server: rest: diff --git a/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java b/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java index cc36683e454..15085581bca 100644 --- a/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java +++ b/opencga-server/src/main/java/org/opencb/opencga/server/rest/OrganizationWSServer.java @@ -30,6 +30,9 @@ import org.opencb.opencga.core.models.organizations.OrganizationConfiguration; import org.opencb.opencga.core.models.organizations.OrganizationCreateParams; import org.opencb.opencga.core.models.organizations.OrganizationUpdateParams; +import org.opencb.opencga.core.models.user.OrganizationUserUpdateParams; +import org.opencb.opencga.core.models.user.User; +import org.opencb.opencga.core.models.user.UserStatusUpdateParams; import org.opencb.opencga.core.response.OpenCGAResult; import org.opencb.opencga.core.tools.annotations.*; @@ -225,4 +228,47 @@ public Response deleteNote( } } + @POST + @Path("/user/{user}/update") + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Update the user information", response = User.class) + @ApiImplicitParams({ + @ApiImplicitParam(name = QueryOptions.INCLUDE, value = ParamConstants.INCLUDE_DESCRIPTION, dataType = "string", paramType = "query"), + @ApiImplicitParam(name = QueryOptions.EXCLUDE, value = ParamConstants.EXCLUDE_DESCRIPTION, dataType = "string", paramType = "query") + }) + public Response updateUserInformation( + @ApiParam(value = ParamConstants.USER_DESCRIPTION, required = true) @PathParam("user") String userId, + @ApiParam(value = ParamConstants.ORGANIZATION_DESCRIPTION) @QueryParam(ParamConstants.ORGANIZATION) String organizationId, + @ApiParam(value = ParamConstants.INCLUDE_RESULT_DESCRIPTION, defaultValue = "false") @QueryParam(ParamConstants.INCLUDE_RESULT_PARAM) boolean includeResult, + @ApiParam(value = "JSON containing the User fields to be updated.", required = true) OrganizationUserUpdateParams parameters) { + try { + OpenCGAResult result = catalogManager.getOrganizationManager().updateUser(organizationId, userId, parameters, queryOptions, token); + return createOkResponse(result); + } catch (Exception e) { + return createErrorResponse(e); + } + } + + @POST + @Path("/user/{user}/status/update") + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Update the user status", response = User.class) + @ApiImplicitParams({ + @ApiImplicitParam(name = QueryOptions.INCLUDE, value = ParamConstants.INCLUDE_DESCRIPTION, dataType = "string", paramType = "query"), + @ApiImplicitParam(name = QueryOptions.EXCLUDE, value = ParamConstants.EXCLUDE_DESCRIPTION, dataType = "string", paramType = "query") + }) + public Response updateUserStatus( + @ApiParam(value = ParamConstants.USER_DESCRIPTION, required = true) @PathParam("user") String userId, + @ApiParam(value = ParamConstants.ORGANIZATION_DESCRIPTION) @QueryParam(ParamConstants.ORGANIZATION) String organizationId, + @ApiParam(value = ParamConstants.INCLUDE_RESULT_DESCRIPTION, defaultValue = "false") @QueryParam(ParamConstants.INCLUDE_RESULT_PARAM) boolean includeResult, + @ApiParam(value = "JSON containing the User fields to be updated.", required = true) UserStatusUpdateParams parameters) { + try { + OpenCGAResult result = catalogManager.getUserManager().changeStatus(organizationId, userId, parameters.getStatus(), queryOptions, token); + return createOkResponse(result); + } catch (Exception e) { + return createErrorResponse(e); + } + } + + } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 634283dace0..c8edfc9c225 100644 --- a/pom.xml +++ b/pom.xml @@ -1348,6 +1348,7 @@ opencga LOCAL + 5 https://ws.opencb.org/opencga-prod @@ -1363,12 +1364,6 @@ 20 - http://localhost:8983/solr/ - 30000 - 2000 - - - false hadoop