/* * Copyright 2015-2016 OpenCB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opencb.opencga.catalog.managers; import org.apache.commons.collections.map.LinkedMap; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.core.QueryResult; import org.opencb.opencga.catalog.audit.AuditManager; import org.opencb.opencga.catalog.audit.AuditRecord; import org.opencb.opencga.catalog.auth.authorization.AuthorizationManager; import org.opencb.opencga.catalog.config.Configuration; import org.opencb.opencga.catalog.db.DBAdaptorFactory; import org.opencb.opencga.catalog.db.api.ProjectDBAdaptor; import org.opencb.opencga.catalog.db.api.StudyDBAdaptor; import org.opencb.opencga.catalog.exceptions.CatalogAuthorizationException; import org.opencb.opencga.catalog.exceptions.CatalogDBException; import org.opencb.opencga.catalog.exceptions.CatalogException; import org.opencb.opencga.catalog.exceptions.CatalogIOException; import org.opencb.opencga.catalog.io.CatalogIOManagerFactory; import org.opencb.opencga.catalog.managers.api.IProjectManager; import org.opencb.opencga.catalog.models.*; import org.opencb.opencga.catalog.models.acls.permissions.StudyAclEntry; import org.opencb.opencga.catalog.utils.ParamUtils; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; /** * @author Jacobo Coll <jacobo167@gmail.com> */ public class ProjectManager extends AbstractManager implements IProjectManager { @Deprecated public ProjectManager(AuthorizationManager authorizationManager, AuditManager auditManager, DBAdaptorFactory catalogDBAdaptorFactory, CatalogIOManagerFactory ioManagerFactory, Properties catalogProperties) { super(authorizationManager, auditManager, catalogDBAdaptorFactory, ioManagerFactory, catalogProperties); } public ProjectManager(AuthorizationManager authorizationManager, AuditManager auditManager, CatalogManager catalogManager, DBAdaptorFactory catalogDBAdaptorFactory, CatalogIOManagerFactory ioManagerFactory, Configuration configuration) { super(authorizationManager, auditManager, catalogManager, catalogDBAdaptorFactory, ioManagerFactory, configuration); } @Override public String getUserId(long projectId) throws CatalogException { return projectDBAdaptor.getOwnerId(projectId); } @Override public long getId(String userId, String projectStr) throws CatalogException { if (StringUtils.isNumeric(projectStr)) { long projectId = Long.parseLong(projectStr); if (projectId > configuration.getCatalog().getOffset()) { projectDBAdaptor.checkId(projectId); return projectId; } } String userOwner; String projectAlias; if (StringUtils.isBlank(projectStr)) { userOwner = userId; projectAlias = null; } else { String[] split = projectStr.split("@"); if (split.length == 2) { // user@project userOwner = split[0]; projectAlias = split[1]; } else { // project userOwner = userId; projectAlias = projectStr; } } if (!userOwner.equals("anonymous") && StringUtils.isNotBlank(projectAlias)) { return projectDBAdaptor.getId(userOwner, projectAlias); } else { // Anonymous user Query query = new Query(); if (StringUtils.isNotBlank(projectAlias)) { query.put(ProjectDBAdaptor.QueryParams.ALIAS.key(), projectAlias); } if (!userOwner.equals("anonymous")) { query.put(ProjectDBAdaptor.QueryParams.USER_ID.key(), userOwner); } QueryOptions options = new QueryOptions(QueryOptions.INCLUDE, ProjectDBAdaptor.QueryParams.ID.key()); QueryResult<Project> projectQueryResult = projectDBAdaptor.get(query, options); if (projectQueryResult.getNumResults() != 1) { if (projectQueryResult.getNumResults() == 0) { throw new CatalogException("No projects found with alias " + projectAlias); } else { throw new CatalogException("More than one project found with alias " + projectAlias); } } return projectQueryResult.first().getId(); } } @Override public List<Long> getIds(String userId, String projectStr) throws CatalogException { if (StringUtils.isNumeric(projectStr)) { return Arrays.asList(Long.parseLong(projectStr)); } String userOwner; String projectAlias; String[] split = projectStr.split("@"); if (split.length == 2) { // user@project userOwner = split[0]; projectAlias = split[1]; } else { // project userOwner = userId; projectAlias = projectStr; } if (!userOwner.equals("anonymous")) { return Arrays.asList(projectDBAdaptor.getId(userOwner, projectAlias)); } else { // Anonymous user Query query = new Query(ProjectDBAdaptor.QueryParams.ALIAS.key(), projectAlias); QueryOptions options = new QueryOptions(QueryOptions.INCLUDE, ProjectDBAdaptor.QueryParams.ID.key()); QueryResult<Project> projectQueryResult = projectDBAdaptor.get(query, options); if (projectQueryResult.getNumResults() == 0) { throw new CatalogException("No projects found with alias " + projectAlias); } return projectQueryResult.getResult().stream().map(project -> project.getId()).collect(Collectors.toList()); } } @Deprecated @Override public long getId(String projectId) throws CatalogException { if (StringUtils.isNumeric(projectId)) { return Long.parseLong(projectId); } String[] split = projectId.split("@"); if (split.length != 2) { return -1; } return projectDBAdaptor.getId(split[0], split[1]); } @Override public QueryResult<Project> create(String name, String alias, String description, String organization, String scientificName, String commonName, String taxonomyCode, String assembly, QueryOptions options, String sessionId) throws CatalogException { ParamUtils.checkParameter(name, "name"); ParamUtils.checkParameter(scientificName, "organism.scientificName"); ParamUtils.checkParameter(assembly, "organism.assembly"); ParamUtils.checkAlias(alias, "alias", configuration.getCatalog().getOffset()); ParamUtils.checkParameter(sessionId, "sessionId"); //Only the user can create a project String userId = userDBAdaptor.getUserIdBySessionId(sessionId); if (userId.isEmpty()) { throw new CatalogException("The session id introduced does not correspond to any registered user."); } // Check that the account type is not guest QueryResult<User> user = userDBAdaptor.get(userId, new QueryOptions(), null); if (user.getNumResults() == 0) { throw new CatalogException("Internal error happened. Could not find user " + userId); } if (Account.GUEST.equalsIgnoreCase(user.first().getAccount().getType())) { throw new CatalogException("User " + userId + " has a guest account and is not authorized to create new projects. If you " + " think this might be an error, please contact with your administrator."); } description = description != null ? description : ""; organization = organization != null ? organization : ""; // Organism Project.Organism organism = new Project.Organism(scientificName, assembly); if (StringUtils.isNumeric(taxonomyCode)) { organism.setTaxonomyCode(Integer.parseInt(taxonomyCode)); } if (StringUtils.isNotEmpty(commonName)) { organism.setCommonName(assembly); } Project project = new Project(name, alias, description, new Status(), organization, organism); QueryResult<Project> queryResult = projectDBAdaptor.insert(project, userId, options); project = queryResult.getResult().get(0); try { catalogIOManagerFactory.getDefault().createProject(userId, Long.toString(project.getId())); } catch (CatalogIOException e) { try { projectDBAdaptor.delete(project.getId()); } catch (Exception e1) { logger.error("Error deleting project from catalog after failing creating the folder in the filesystem", e1); throw e; } throw e; } userDBAdaptor.updateUserLastModified(userId); // auditManager.recordCreation(AuditRecord.Resource.project, queryResult.first().getId(), userId, queryResult.first(), null, null); auditManager.recordAction(AuditRecord.Resource.project, AuditRecord.Action.create, AuditRecord.Magnitude.low, queryResult.first().getId(), userId, null, queryResult.first(), null, null); return queryResult; } @Override public QueryResult<Project> get(Long projectId, QueryOptions options, String sessionId) throws CatalogException { String userId = catalogManager.getUserManager().getId(sessionId); authorizationManager.checkProjectPermission(projectId, userId, StudyAclEntry.StudyPermissions.VIEW_STUDY); QueryResult<Project> projectResult = projectDBAdaptor.get(projectId, options); if (!projectResult.getResult().isEmpty()) { authorizationManager.filterStudies(userId, projectResult.getResult().get(0).getStudies()); } return projectResult; } @Override public QueryResult<Project> get(Query query, QueryOptions options, String sessionId) throws CatalogException { query = ParamUtils.defaultObject(query, Query::new); options = ParamUtils.defaultObject(options, QueryOptions::new); String userId = catalogManager.getUserManager().getId(sessionId); String ownerId = query.getString("ownerId", query.getString("userId", userId)); ParamUtils.checkParameter(ownerId, "ownerId"); QueryResult<Project> allProjects = projectDBAdaptor.get(ownerId, options); List<Project> projects = allProjects.getResult(); authorizationManager.filterProjects(userId, projects); allProjects.setResult(projects); allProjects.setNumResults(projects.size()); return allProjects; } @Override public QueryResult<Project> update(Long projectId, ObjectMap parameters, QueryOptions options, String sessionId) throws CatalogException { ParamUtils.checkObj(parameters, "Parameters"); ParamUtils.checkParameter(sessionId, "sessionId"); String userId = userDBAdaptor.getUserIdBySessionId(sessionId); String ownerId = projectDBAdaptor.getOwnerId(projectId); if (!userId.equals(ownerId)) { throw new CatalogException("Permission denied: Only the owner of the project can update it."); } QueryResult<Project> queryResult = new QueryResult<>(); if (parameters.containsKey("alias")) { rename(projectId, parameters.getString("alias"), sessionId); //Clone and remove alias from parameters. Do not modify the original parameter parameters = new ObjectMap(parameters); parameters.remove("alias"); } // Update organism information only if any of the fields was not properly defined if (parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_SCIENTIFIC_NAME.key()) || parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_COMMON_NAME.key()) || parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_TAXONOMY_CODE.key()) || parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_ASSEMBLY.key())) { QueryResult<Project> projectQR = projectDBAdaptor .get(projectId, new QueryOptions(QueryOptions.INCLUDE, ProjectDBAdaptor.QueryParams.ORGANISM.key())); if (projectQR.getNumResults() == 0) { throw new CatalogException("Project " + projectId + " not found"); } ObjectMap objectMap = new ObjectMap(); if (parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_SCIENTIFIC_NAME.key()) && StringUtils.isEmpty(projectQR.first().getOrganism().getScientificName())) { objectMap.put(ProjectDBAdaptor.QueryParams.ORGANISM_SCIENTIFIC_NAME.key(), parameters.getString(ProjectDBAdaptor.QueryParams.ORGANISM_SCIENTIFIC_NAME.key())); parameters.remove(ProjectDBAdaptor.QueryParams.ORGANISM_SCIENTIFIC_NAME.key()); } if (parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_COMMON_NAME.key()) && StringUtils.isEmpty(projectQR.first().getOrganism().getCommonName())) { objectMap.put(ProjectDBAdaptor.QueryParams.ORGANISM_COMMON_NAME.key(), parameters.getString(ProjectDBAdaptor.QueryParams.ORGANISM_COMMON_NAME.key())); parameters.remove(ProjectDBAdaptor.QueryParams.ORGANISM_COMMON_NAME.key()); } if (parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_TAXONOMY_CODE.key()) && projectQR.first().getOrganism().getTaxonomyCode() <= 0) { objectMap.put(ProjectDBAdaptor.QueryParams.ORGANISM_TAXONOMY_CODE.key(), parameters.getInt(ProjectDBAdaptor.QueryParams.ORGANISM_TAXONOMY_CODE.key())); parameters.remove(ProjectDBAdaptor.QueryParams.ORGANISM_TAXONOMY_CODE.key()); } if (parameters.containsKey(ProjectDBAdaptor.QueryParams.ORGANISM_ASSEMBLY.key()) && StringUtils.isEmpty(projectQR.first().getOrganism().getAssembly())) { objectMap.put(ProjectDBAdaptor.QueryParams.ORGANISM_ASSEMBLY.key(), parameters.getString(ProjectDBAdaptor.QueryParams.ORGANISM_ASSEMBLY.key())); parameters.remove(ProjectDBAdaptor.QueryParams.ORGANISM_ASSEMBLY.key()); } if (!objectMap.isEmpty()) { queryResult = projectDBAdaptor.update(projectId, objectMap); } else { throw new CatalogException("Cannot update organism information that is already filled in"); } } for (String s : parameters.keySet()) { if (!s.matches("name|description|organization|attributes")) { throw new CatalogDBException("Parameter '" + s + "' can't be changed"); } } userDBAdaptor.updateUserLastModified(ownerId); if (parameters.size() > 0) { queryResult = projectDBAdaptor.update(projectId, parameters); } auditManager.recordUpdate(AuditRecord.Resource.project, projectId, userId, parameters, null, null); return queryResult; } public QueryResult rename(long projectId, String newProjectAlias, String sessionId) throws CatalogException { ParamUtils.checkAlias(newProjectAlias, "newProjectAlias", configuration.getCatalog().getOffset()); ParamUtils.checkParameter(sessionId, "sessionId"); String userId = userDBAdaptor.getUserIdBySessionId(sessionId); String ownerId = projectDBAdaptor.getOwnerId(projectId); if (!userId.equals(ownerId)) { throw new CatalogException("Permission denied: Only the owner of the project can update it."); } userDBAdaptor.updateUserLastModified(ownerId); QueryResult queryResult = projectDBAdaptor.renameAlias(projectId, newProjectAlias); auditManager.recordUpdate(AuditRecord.Resource.project, projectId, userId, new ObjectMap("alias", newProjectAlias), null, null); return queryResult; } @Override public List<QueryResult<Project>> delete(String ids, QueryOptions options, String sessionId) throws CatalogException { throw new UnsupportedOperationException(); } @Override public List<QueryResult<Project>> delete(Query query, QueryOptions options, String sessionId) throws CatalogException, IOException { throw new UnsupportedOperationException(); } @Override public List<QueryResult<Project>> restore(String ids, QueryOptions options, String sessionId) throws CatalogException { throw new UnsupportedOperationException(); } @Override public List<QueryResult<Project>> restore(Query query, QueryOptions options, String sessionId) throws CatalogException { throw new UnsupportedOperationException(); } @Override public void setStatus(String id, String status, String message, String sessionId) throws CatalogException { throw new NotImplementedException("Project: Operation not yet supported"); // ParamUtils.checkParameter(sessionId, "sessionId"); // String userId = catalogManager.getUserManager().getId(sessionId); // long projectId = getId(userId, id); // String ownerId = projectDBAdaptor.getOwnerId(projectId); // // if (!userId.equals(ownerId)) { // throw new CatalogException("Permission denied: Only the owner of the project can update the status."); // } // // if (!Status.isValid(status)) { // throw new CatalogException("The status " + status + " is not valid project status."); // } // // ObjectMap param = new ObjectMap(ProjectDBAdaptor.QueryParams.STATUS_NAME.key(), status); // projectDBAdaptor.update(projectId, param); // userDBAdaptor.updateUserLastModified(ownerId); // auditManager.recordUpdate(AuditRecord.Resource.project, projectId, userId, param, null, null); } @Override public QueryResult rank(String userId, Query query, String field, int numResults, boolean asc, String sessionId) throws CatalogException { query = ParamUtils.defaultObject(query, Query::new); ParamUtils.checkObj(field, "field"); ParamUtils.checkObj(userId, "userId"); ParamUtils.checkObj(sessionId, "sessionId"); String userOfQuery = userDBAdaptor.getUserIdBySessionId(sessionId); if (!userOfQuery.equals(userId)) { // The user cannot read projects of other users. throw CatalogAuthorizationException.cantRead(userOfQuery, "Project", -1, userId); } // TODO: In next release, we will have to check the count parameter from the queryOptions object. boolean count = true; // query.append(CatalogFileDBAdaptor.QueryParams.STUDY_ID.key(), studyId); QueryResult queryResult = null; if (count) { // We do not need to check for permissions when we show the count of files queryResult = projectDBAdaptor.rank(query, field, numResults, asc); } return ParamUtils.defaultObject(queryResult, QueryResult::new); } @Override public QueryResult groupBy(String userId, Query query, String field, QueryOptions options, String sessionId) throws CatalogException { query = ParamUtils.defaultObject(query, Query::new); options = ParamUtils.defaultObject(options, QueryOptions::new); ParamUtils.checkObj(field, "field"); ParamUtils.checkObj(userId, "userId"); ParamUtils.checkObj(sessionId, "sessionId"); String userOfQuery = userDBAdaptor.getUserIdBySessionId(sessionId); if (!userOfQuery.equals(userId)) { // The user cannot read projects of other users. throw CatalogAuthorizationException.cantRead(userOfQuery, "Project", -1, userId); } // TODO: In next release, we will have to check the count parameter from the queryOptions object. boolean count = true; QueryResult queryResult = null; if (count) { // We do not need to check for permissions when we show the count of files queryResult = projectDBAdaptor.groupBy(query, field, options); } return ParamUtils.defaultObject(queryResult, QueryResult::new); } @Override public QueryResult groupBy(String userId, Query query, List<String> fields, QueryOptions options, String sessionId) throws CatalogException { query = ParamUtils.defaultObject(query, Query::new); options = ParamUtils.defaultObject(options, QueryOptions::new); ParamUtils.checkObj(fields, "fields"); ParamUtils.checkObj(userId, "userId"); ParamUtils.checkObj(sessionId, "sessionId"); String userOfQuery = userDBAdaptor.getUserIdBySessionId(sessionId); if (!userOfQuery.equals(userId)) { // The user cannot read projects of other users. throw CatalogAuthorizationException.cantRead(userOfQuery, "Project", -1, userId); } // TODO: In next release, we will have to check the count parameter from the queryOptions object. boolean count = true; QueryResult queryResult = null; if (count) { // We do not need to check for permissions when we show the count of files queryResult = projectDBAdaptor.groupBy(query, fields, options); } return ParamUtils.defaultObject(queryResult, QueryResult::new); } @Override public QueryResult<Project> getSharedProjects(String userId, QueryOptions queryOptions, String sessionId) throws CatalogException { queryOptions = ParamUtils.defaultObject(queryOptions, QueryOptions::new); long startTime = System.currentTimeMillis(); String userSessionId = catalogManager.getUserManager().getId(sessionId); if (!userSessionId.equals(userId)) { throw new CatalogException("Invalid session id: The user corresponding to the session provided is not " + userId); } // Search all studies shared with the user // 1. Look for userId in a group in all the studies. Query query = new Query(StudyDBAdaptor.QueryParams.GROUP_USER_IDS.key(), userId); QueryResult<Study> studyGroupQR = catalogManager.getStudyManager().get(query, queryOptions, sessionId); // The studies obtained are already filtered in studyManager, so if we get them is because those have been shared with the user // 2. Look for userId in an ACL in all the studies. query = new Query(StudyDBAdaptor.QueryParams.ACL_MEMBER.key(), userId); QueryResult<Study> studyACLQR = catalogManager.getStudyManager().get(query, queryOptions, sessionId); List<Study> studyList = new ArrayList<>(); studyList.addAll(studyGroupQR.getResult()); studyList.addAll(studyACLQR.getResult()); if (studyList.size() == 0) { // No studies are shared with userId return new QueryResult<>(userId, (int) (System.currentTimeMillis() - startTime), 0, 0, "", "", Collections.emptyList()); } // Obtain the projects corresponding to each study List<Long> projectIds = new LinkedList<>(); Map<Long, List<Study>> projectStudyMap = new LinkedMap(); for (Study study : studyList) { Long projectId = catalogManager.getStudyManager().getProjectId(study.getId()); if (!projectStudyMap.containsKey(projectId)) { projectStudyMap.put(projectId, new LinkedList<>()); projectIds.add(projectId); } projectStudyMap.get(projectId).add(study); } // Obtain the project info of all the project ids needed query = new Query(ProjectDBAdaptor.QueryParams.ID.key(), projectIds); QueryOptions options = new QueryOptions(queryOptions); // Copy of queryOptions if (options.containsKey(QueryOptions.EXCLUDE)) { List<String> excludeList = options.getAsStringList(QueryOptions.EXCLUDE); excludeList.add("projects.studies"); options.put(QueryOptions.EXCLUDE, excludeList); } else { options.add(QueryOptions.EXCLUDE, "projects.studies"); } QueryResult<Project> projectQueryResult = projectDBAdaptor.get(query, options); for (Project project : projectQueryResult.getResult()) { // Update with the studies shared with the user project.setStudies(projectStudyMap.get(project.getId())); // Add user info to the alias String ownerId = projectDBAdaptor.getOwnerId(project.getId()); project.setAlias(ownerId + "@" + project.getAlias()); } projectQueryResult.setDbTime((int) (System.currentTimeMillis() - startTime)); projectQueryResult.setId(userId); return projectQueryResult; } }