Skip to content

Commit

Permalink
Merge branch 'develop' into TASK-5610
Browse files Browse the repository at this point in the history
  • Loading branch information
jtarraga committed Jul 2, 2024
2 parents 67fc138 + f7f19a0 commit 8a841f5
Show file tree
Hide file tree
Showing 27 changed files with 819 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public abstract class OpenCgaCompleter implements Completer {
.map(Candidate::new)
.collect(toList());

private List<Candidate> organizationsList = asList( "create","notes-create","notes-search","notes-delete","notes-update","info","update")
private List<Candidate> organizationsList = asList( "create","notes-create","notes-search","notes-delete","notes-update","configuration-update","info","update")
.stream()
.map(Candidate::new)
.collect(toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ public OpencgaCliOptionsParser() {
organizationsSubCommands.addCommand("notes-search", organizationsCommandOptions.searchNotesCommandOptions);
organizationsSubCommands.addCommand("notes-delete", organizationsCommandOptions.deleteNotesCommandOptions);
organizationsSubCommands.addCommand("notes-update", organizationsCommandOptions.updateNotesCommandOptions);
organizationsSubCommands.addCommand("configuration-update", organizationsCommandOptions.updateConfigurationCommandOptions);
organizationsSubCommands.addCommand("info", organizationsCommandOptions.infoCommandOptions);
organizationsSubCommands.addCommand("update", organizationsCommandOptions.updateCommandOptions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.opencb.opencga.app.cli.main.options.OrganizationsCommandOptions;
import org.opencb.opencga.catalog.exceptions.CatalogAuthenticationException;
import org.opencb.opencga.catalog.utils.ParamUtils.AddRemoveAction;
import org.opencb.opencga.catalog.utils.ParamUtils.UpdateAction;
import org.opencb.opencga.client.exceptions.ClientException;
import org.opencb.opencga.core.common.JacksonUtils;
import org.opencb.opencga.core.config.Optimizations;
Expand Down Expand Up @@ -75,6 +76,9 @@ public void execute() throws Exception {
case "notes-update":
queryResponse = updateNotes();
break;
case "configuration-update":
queryResponse = updateConfiguration();
break;
case "info":
queryResponse = info();
break;
Expand Down Expand Up @@ -223,6 +227,41 @@ private RestResponse<Note> updateNotes() throws Exception {
return openCGAClient.getOrganizationClient().updateNotes(commandOptions.id, noteUpdateParams, queryParams);
}

private RestResponse<OrganizationConfiguration> updateConfiguration() throws Exception {
logger.debug("Executing updateConfiguration in Organizations command line");

OrganizationsCommandOptions.UpdateConfigurationCommandOptions commandOptions = organizationsCommandOptions.updateConfigurationCommandOptions;

ObjectMap queryParams = new ObjectMap();
queryParams.putIfNotEmpty("include", commandOptions.include);
queryParams.putIfNotEmpty("exclude", commandOptions.exclude);
queryParams.putIfNotNull("includeResult", commandOptions.includeResult);
queryParams.putIfNotNull("authenticationOriginsAction", commandOptions.authenticationOriginsAction);


OrganizationConfiguration organizationConfiguration = null;
if (commandOptions.jsonDataModel) {
RestResponse<OrganizationConfiguration> res = new RestResponse<>();
res.setType(QueryType.VOID);
PrintUtils.println(getObjectAsJSON(categoryName,"/{apiVersion}/organizations/{organization}/configuration/update"));
return res;
} else if (commandOptions.jsonFile != null) {
organizationConfiguration = JacksonUtils.getDefaultObjectMapper()
.readValue(new java.io.File(commandOptions.jsonFile), OrganizationConfiguration.class);
} else {
ObjectMap beanParams = new ObjectMap();
putNestedIfNotNull(beanParams, "optimizations.simplifyPermissions",commandOptions.optimizationsSimplifyPermissions, true);
putNestedIfNotEmpty(beanParams, "token.algorithm",commandOptions.tokenAlgorithm, true);
putNestedIfNotEmpty(beanParams, "token.secretKey",commandOptions.tokenSecretKey, true);
putNestedIfNotNull(beanParams, "token.expiration",commandOptions.tokenExpiration, true);

organizationConfiguration = JacksonUtils.getDefaultObjectMapper().copy()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true)
.readValue(beanParams.toJson(), OrganizationConfiguration.class);
}
return openCGAClient.getOrganizationClient().updateConfiguration(commandOptions.organization, organizationConfiguration, queryParams);
}

private RestResponse<Organization> info() throws Exception {
logger.debug("Executing info in Organizations command line");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class OrganizationsCommandOptions {
public SearchNotesCommandOptions searchNotesCommandOptions;
public DeleteNotesCommandOptions deleteNotesCommandOptions;
public UpdateNotesCommandOptions updateNotesCommandOptions;
public UpdateConfigurationCommandOptions updateConfigurationCommandOptions;
public InfoCommandOptions infoCommandOptions;
public UpdateCommandOptions updateCommandOptions;

Expand All @@ -51,6 +52,7 @@ public OrganizationsCommandOptions(CommonCommandOptions commonCommandOptions, JC
this.searchNotesCommandOptions = new SearchNotesCommandOptions();
this.deleteNotesCommandOptions = new DeleteNotesCommandOptions();
this.updateNotesCommandOptions = new UpdateNotesCommandOptions();
this.updateConfigurationCommandOptions = new UpdateConfigurationCommandOptions();
this.infoCommandOptions = new InfoCommandOptions();
this.updateCommandOptions = new UpdateCommandOptions();

Expand Down Expand Up @@ -216,6 +218,47 @@ public class UpdateNotesCommandOptions {

}

@Parameters(commandNames = {"configuration-update"}, commandDescription ="Update the Organization configuration attributes")
public class UpdateConfigurationCommandOptions {

@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 = {"--organization"}, description = "Organization id", required = true, 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 = {"--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 = {"--optimizations-simplify-permissions"}, description = "The body web service simplifyPermissions parameter", required = false, help = true, arity = 0)
public boolean optimizationsSimplifyPermissions = false;

@Parameter(names = {"--token-algorithm"}, description = "The body web service algorithm parameter", required = false, arity = 1)
public String tokenAlgorithm;

@Parameter(names = {"--token-secret-key"}, description = "The body web service secretKey parameter", required = false, arity = 1)
public String tokenSecretKey;

@Parameter(names = {"--token-expiration"}, description = "The body web service expiration parameter", required = false, arity = 1)
public Long tokenExpiration;

}

@Parameters(commandNames = {"info"}, commandDescription ="Return the organization information")
public class InfoCommandOptions {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,23 @@
import com.mongodb.client.model.RenameCollectionOptions;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.UpdateResult;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.opencb.commons.datastore.mongodb.MongoDataStore;
import org.opencb.opencga.catalog.db.api.NoteDBAdaptor;
import org.opencb.opencga.catalog.db.mongodb.MongoDBAdaptorFactory;
import org.opencb.opencga.catalog.db.mongodb.OrganizationMongoDBAdaptorFactory;
import org.opencb.opencga.catalog.exceptions.CatalogDBException;
import org.opencb.opencga.catalog.io.IOManagerFactory;
import org.opencb.opencga.catalog.managers.CatalogManager;
import org.opencb.opencga.catalog.migration.Migration;
import org.opencb.opencga.catalog.migration.MigrationTool;
import org.opencb.opencga.core.api.ParamConstants;
import org.opencb.opencga.core.models.notes.Note;

import java.util.Collections;
import java.util.List;

@Migration(id = "migrate_notes", description = "Migrate notes #TASK-5836", version = "3.1.0",
language = Migration.MigrationLanguage.JAVA, domain = Migration.MigrationDomain.CATALOG, date = 20240315)
public class NoteMigration extends MigrationTool {

@Override
protected void run() throws Exception {
IOManagerFactory ioManagerFactory = new IOManagerFactory();
dbAdaptorFactory = new MongoDBAdaptorFactory(configuration, ioManagerFactory);
// First migrate to add the new values
MongoCollection<Document> collection = getMongoCollection(ParamConstants.ADMIN_ORGANIZATION, "notes");
MongoCollection<Document> collection = getMongoCollection(organizationId, "notes");
Bson query = Filters.exists(NoteDBAdaptor.QueryParams.STUDY_UID.key(), false);
Bson update = Updates.combine(
Updates.set(NoteDBAdaptor.QueryParams.STUDY_UID.key(), -1L),
Expand All @@ -43,41 +32,20 @@ protected void run() throws Exception {
);
UpdateResult updateResult = collection.updateMany(query, update);
if (updateResult.getModifiedCount() == 0) {
// Check there are at least 2 organizations present
logger.info("Note data model could not be updated. Detected organizations are: {}", StringUtils.join(dbAdaptorFactory.getOrganizationIds(), ","));
if (dbAdaptorFactory.getOrganizationIds().size() == 2) {
logger.info("Nothing to migrate");
return;
} else {
throw new CatalogDBException("Notes could not be found to migrate.");
}
logger.info("Note data model could not be updated. Notes found in organization '{}': {}", organizationId, updateResult.getMatchedCount());
}
renameNoteCollection(Collections.singletonList(ParamConstants.ADMIN_ORGANIZATION));

dbAdaptorFactory.close();
dbAdaptorFactory = new MongoDBAdaptorFactory(configuration, ioManagerFactory);
// We run it a second time because the first time it will only rename the "opencga" org as OpenCGA will not be able to know
// which other organizations are present in the installation (trying to fetch the information from "note" instead of old "notes")
List<String> organizationIds = dbAdaptorFactory.getOrganizationIds();
organizationIds.remove(ParamConstants.ADMIN_ORGANIZATION);
renameNoteCollection(organizationIds);

// Reload catalog manager to install missing indexes
catalogManager = new CatalogManager(configuration);
catalogManager.installIndexes(token);
}

private void renameNoteCollection(List<String> organizationIds) throws CatalogDBException {
// Rename Note collection
RenameCollectionOptions options = new RenameCollectionOptions().dropTarget(true);
// Rename collection
for (String organizationId : organizationIds) {
String databaseName = dbAdaptorFactory.getMongoDataStore(organizationId).getDatabaseName();
logger.info("Renaming notes collection for organization '{}' -> Database: '{}'", organizationId, databaseName);
MongoDataStore mongoDataStore = dbAdaptorFactory.getMongoDataStore(organizationId);
mongoDataStore.getDb().getCollection("notes").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_COLLECTION), options);
mongoDataStore.getDb().getCollection("notes_archive").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_ARCHIVE_COLLECTION), options);
mongoDataStore.getDb().getCollection("notes_deleted").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.DELETED_NOTE_COLLECTION), options);
}
String databaseName = dbAdaptorFactory.getMongoDataStore(organizationId).getDatabaseName();
logger.info("Renaming notes collection for organization '{}' -> Database: '{}'", organizationId, databaseName);
MongoDataStore mongoDataStore = dbAdaptorFactory.getMongoDataStore(organizationId);
mongoDataStore.getDb().getCollection("notes").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_COLLECTION), options);
mongoDataStore.getDb().getCollection("notes_archive").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.NOTE_ARCHIVE_COLLECTION), options);
mongoDataStore.getDb().getCollection("notes_deleted").renameCollection(new MongoNamespace(databaseName, OrganizationMongoDBAdaptorFactory.DELETED_NOTE_COLLECTION), options);

catalogManager.installIndexes(organizationId, token);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.slf4j.LoggerFactory;

import javax.crypto.spec.SecretKeySpec;
import java.io.Closeable;
import java.security.Key;
import java.util.Collections;
import java.util.Date;
Expand All @@ -37,7 +38,7 @@
/**
* @author Jacobo Coll &lt;[email protected]&gt;
*/
public abstract class AuthenticationManager {
public abstract class AuthenticationManager implements Closeable {

protected JwtManager jwtManager;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ public AzureADAuthenticationManager(AuthenticationOrigin authenticationOrigin) t
Configurator.setLevel("com.microsoft.aad.adal4j.AuthenticationAuthority", Level.WARN);
}

public static void validateAuthenticationOriginConfiguration(AuthenticationOrigin authenticationOrigin) throws CatalogException {
if (authenticationOrigin.getType() != AuthenticationOrigin.AuthenticationType.AzureAD) {
throw new CatalogException("Unknown authentication type. Expected type '" + AuthenticationOrigin.AuthenticationType.AzureAD
+ "' but received '" + authenticationOrigin.getType() + "'.");
}
AzureADAuthenticationManager azureADAuthenticationManager = new AzureADAuthenticationManager(authenticationOrigin);
azureADAuthenticationManager.close();
}

private OIDCProviderMetadata getProviderMetadata(String host) throws IOException, ParseException {
URL providerConfigurationURL = new URL(host);
InputStream stream = providerConfigurationURL.openStream();
Expand Down Expand Up @@ -420,4 +429,8 @@ public String createNonExpiringToken(String organizationId, String userId, Map<S
throw new UnsupportedOperationException("Tokens are generated by Azure via authorization code or user-password");
}

@Override
public void close() {
THREAD_POOL.shutdown();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ public CatalogAuthenticationManager(DBAdaptorFactory dbAdaptorFactory, Email ema
this.logger = LoggerFactory.getLogger(CatalogAuthenticationManager.class);
}

public static void validateAuthenticationOriginConfiguration(AuthenticationOrigin authenticationOrigin) throws CatalogException {
if (!OPENCGA.equals(authenticationOrigin.getId())) {
throw new CatalogException("Unknown authentication origin. Expected origin id '" + OPENCGA + "' but received '"
+ authenticationOrigin.getId() + "'.");
}
if (authenticationOrigin.getType() != AuthenticationOrigin.AuthenticationType.OPENCGA) {
throw new CatalogException("Unknown authentication type. Expected type '" + AuthenticationOrigin.AuthenticationType.OPENCGA
+ "' but received '" + authenticationOrigin.getType() + "'.");
}
}

@Override
public AuthenticationResponse authenticate(String organizationId, String userId, String password)
throws CatalogAuthenticationException {
Expand Down Expand Up @@ -102,12 +113,12 @@ public void newPassword(String organizationId, String userId, String newPassword

@Override
public String createToken(String organizationId, String userId, Map<String, Object> claims, long expiration) {
return jwtManager.createJWTToken(organizationId, userId, claims, expiration);
return jwtManager.createJWTToken(organizationId, AuthenticationOrigin.AuthenticationType.OPENCGA, userId, claims, expiration);
}

@Override
public String createNonExpiringToken(String organizationId, String userId, Map<String, Object> claims) {
return jwtManager.createJWTToken(organizationId, userId, claims, 0L);
return jwtManager.createJWTToken(organizationId, AuthenticationOrigin.AuthenticationType.OPENCGA, userId, claims, 0L);
}

@Override
Expand Down Expand Up @@ -145,4 +156,8 @@ public static AuthenticationOrigin createOpencgaAuthenticationOrigin() {
.setId(CatalogAuthenticationManager.OPENCGA)
.setType(AuthenticationOrigin.AuthenticationType.OPENCGA);
}

@Override
public void close() {
}
}
Loading

0 comments on commit 8a841f5

Please sign in to comment.