diff --git a/.gitignore b/.gitignore
index fb437d4ca8..c770a83d86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+Makefile
WebAPIConfig/
*application.properties
.idea/
@@ -12,6 +13,7 @@ sandbox/
/nbactions*.xml
*~
.DS_Store
+.factorypath
### Developer's personal properties ###
**/resources/config/application*-dev-*.properties
diff --git a/Makefile b/Makefile
index adb76d0ea7..84b1aaac79 100644
--- a/Makefile
+++ b/Makefile
@@ -1,56 +1,21 @@
compile:
- mvn clean
- mvn compile -Pwebapi-postgresql-laertes
+ mvn clean compile -DskipUnitTests -DskipITtests -s WebAPIConfig/settings.xml -P webapi-postgresql
package: compile
- mvn package -Pwebapi-postgresql-laertes
+ mvn package -DskipUnitTests -DskipITtests -s WebAPIConfig/settings.xml -P webapi-postgresql
-deploy: package
- sudo service tomcat7 stop
- sleep 4
- sudo rm -rf /var/lib/tomcat7/webapps/WebAPI*
- sudo cp -r target/WebAPI.war /var/lib/tomcat7/webapps/
- sudo chown tomcat7 /var/lib/tomcat7/webapps/WebAPI.war
- sudo chgrp tomcat7 /var/lib/tomcat7/webapps/WebAPI.war
- sudo service tomcat7 start
+deploy: package
+ /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/bin/shutdown.sh
+ mv /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/webapps/WebAPI /mnt/disk1/webapi-dev-tmp/WebAPI-FOLDER-`date +%m%d%H%S`
+ mv /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/webapps/WebAPI.war /mnt/disk1/webapi-dev-tmp/WebAPI.war-`date +%m%d%H%S`
+ mv target/WebAPI.war /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/webapps/
+ echo "Now run /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/bin/startup.sh"
git-push:
- git push myfork master
+ git push
test:
- wget -O tests/test-general-evidence.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/752061"
- wget -O tests/test-drug-hoi.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/752061-374013"
- wget -O tests/test-drug.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drug/752061"
- wget -O tests/test-hoi.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/hoi/320073"
- wget -O tests/test-info.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/info"
- wget -O tests/test-drug-hoi-eu-spc.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/904351-4190045"
- wget -O tests/test-drug-hoi-splicer.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/19133853-195588"
- wget -O tests/test-drug-hoi-faers-counts-and-signals.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/1154343-433031"
- wget -O tests/test-drug-hoi-pubmed-mesh-cr.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/1154343-433031"
- wget -O tests/test-drug-hoi-pubmed-mesh-clin-trial.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/789578-378144"
- wget -O tests/test-drug-hoi-pubmed-mesh-other.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/19010482-316866"
- wget -O tests/test-drug-hoi-semmed-cr.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/1112807-441202"
- wget -O tests/test-drug-hoi-semmed-clin-trial.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/19059744-381591"
- wget -O tests/test-drug-rollup-ingredient.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drugrollup/ingredient/1000632"
- wget -O tests/test-drug-rollup-clin-drug.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drugrollup/clinicaldrug/19074181"
- wget -O tests/test-drug-rollup-branded-drug.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drugrollup/brandeddrug/1000640"
- wget -O tests/test-rdf-evidencesummary.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/evidencesummary?conditionID=139900&drugID=1115008&evidenceGroup=Literature"
- wget -O tests/test-rdf-evidencedetails.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/evidencedetails?conditionID=24134&drugID=1115008&evidenceType=SPL_SPLICER_ADR"
+ wget -O /tmp/tests/test-drug-rollup-branded-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/brandeddrug/1000640"
test-public:
- wget -O tests/test-general-evidence.json "http://api.ohdsi.org/WebAPI/CS1/evidence/1000640"
- wget -O /tmp/tests/test-drug-hoi.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1000640-137682"
- wget -O /tmp/tests/test-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drug/1000640"
- wget -O /tmp/tests/test-hoi.json "http://api.ohdsi.org/WebAPI/CS1/evidence/hoi/320073"
- wget -O /tmp/tests/test-info.json "http://api.ohdsi.org/WebAPI/CS1/evidence/info"
- wget -O /tmp/tests/test-drug-hoi-eu-spc.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/40239056-75053"
- wget -O /tmp/tests/test-drug-hoi-splicer.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/19133853-195588"
- wget -O /tmp/tests/test-drug-hoi-faers-counts-and-signals.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1154343-433031"
- wget -O /tmp/tests/test-drug-hoi-pubmed-mesh-cr.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1154343-433031"
- wget -O /tmp/tests/test-drug-hoi-pubmed-mesh-clin-trial.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/789578-378144"
- wget -O /tmp/tests/test-drug-hoi-pubmed-mesh-other.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/19010482-316866"
- wget -O /tmp/tests/test-drug-hoi-semmed-cr.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1782521-45612000"
- wget -O /tmp/tests/test-drug-hoi-semmed-clin-trial.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1303425-45616736"
- wget -O /tmp/tests/test-drug-rollup-ingredient.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/ingredient/1000632"
- wget -O /tmp/tests/test-drug-rollup-clin-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/clinicaldrug/19074181"
wget -O /tmp/tests/test-drug-rollup-branded-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/brandeddrug/1000640"
diff --git a/pom.xml b/pom.xml
index b4ef035fea..6b1bd807c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -192,6 +192,11 @@
true
authDataSource
+
+
+
+ true
+
8080
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java
index af2aa3a3f3..13aaff54b6 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java
@@ -42,9 +42,11 @@
import org.ohdsi.webapi.versioning.dto.VersionUpdateDTO;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import javax.ws.rs.Consumes;
@@ -63,6 +65,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -86,6 +89,9 @@ public class CcController {
private CharacterizationChecker checker;
private PermissionService permissionService;
+ @Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
+ private boolean defaultGlobalReadPermissions;
+
public CcController(
final CcService service,
final FeAnalysisService feAnalysisService,
@@ -151,11 +157,28 @@ public CohortCharacterizationDTO copy(@PathParam("id") final Long id) {
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Page list(@Pagination Pageable pageable) {
- return service.getPage(pageable).map(entity -> {
- CcShortDTO dto = convertCcToShortDto(entity);
- permissionService.fillWriteAccess(entity, dto);
- return dto;
- });
+ if (defaultGlobalReadPermissions == true) { // don't filter based on read permissions
+ return service.getPage(pageable).map(entity -> {
+ CcShortDTO dto = convertCcToShortDto(entity);
+ permissionService.fillWriteAccess(entity, dto);
+ permissionService.fillReadAccess(entity, dto);
+ return dto;
+ });
+ } else { // filter out what the user does not have read access to
+ List dtolist = new ArrayList();
+
+ Page newpage = service.getPage(pageable);
+
+ for (CohortCharacterizationEntity entity : newpage) {
+ if(permissionService.hasReadAccess(entity)){
+ CcShortDTO dto = convertCcToShortDto(entity);
+ permissionService.fillWriteAccess(entity, dto);
+ permissionService.fillReadAccess(entity, dto);
+ dtolist.add(dto);
+ }
+ }
+ return new PageImpl(dtolist, pageable, dtolist.size());
+ }
}
/**
diff --git a/src/main/java/org/ohdsi/webapi/estimation/EstimationController.java b/src/main/java/org/ohdsi/webapi/estimation/EstimationController.java
index 90c7b7fa33..9875bc8e5e 100644
--- a/src/main/java/org/ohdsi/webapi/estimation/EstimationController.java
+++ b/src/main/java/org/ohdsi/webapi/estimation/EstimationController.java
@@ -22,6 +22,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.stereotype.Controller;
@@ -70,7 +71,10 @@ public class EstimationController {
private final ScriptExecutionService executionService;
private EstimationChecker checker;
private PermissionService permissionService;
-
+
+ @Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
+ private boolean defaultGlobalReadPermissions;
+
public EstimationController(EstimationService service,
GenericConversionService conversionService,
CommonGenerationSensitiveInfoService sensitiveInfoService,
@@ -97,14 +101,26 @@ public EstimationController(EstimationService service,
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public List getAnalysisList() {
-
- return StreamSupport.stream(service.getAnalysisList().spliterator(), false)
- .map(analysis -> {
- EstimationShortDTO dto = conversionService.convert(analysis, EstimationShortDTO.class);
- permissionService.fillWriteAccess(analysis, dto);
- return dto;
- })
- .collect(Collectors.toList());
+ if (defaultGlobalReadPermissions == true) { // don't filter based on read permissions
+ return StreamSupport.stream(service.getAnalysisList().spliterator(), false)
+ .map(analysis -> {
+ EstimationShortDTO dto = conversionService.convert(analysis, EstimationShortDTO.class);
+ permissionService.fillWriteAccess(analysis, dto);
+ permissionService.fillReadAccess(analysis, dto);
+ return dto;
+ })
+ .collect(Collectors.toList());
+ } else {
+ return StreamSupport.stream(service.getAnalysisList().spliterator(), false)
+ .filter(candidateEstimation -> permissionService.hasReadAccess(candidateEstimation))
+ .map(analysis -> {
+ EstimationShortDTO dto = conversionService.convert(analysis, EstimationShortDTO.class);
+ permissionService.fillWriteAccess(analysis, dto);
+ permissionService.fillReadAccess(analysis, dto);
+ return dto;
+ })
+ .collect(Collectors.toList());
+ }
}
/**
diff --git a/src/main/java/org/ohdsi/webapi/pathway/PathwayController.java b/src/main/java/org/ohdsi/webapi/pathway/PathwayController.java
index 04c50a1434..671e6119b5 100644
--- a/src/main/java/org/ohdsi/webapi/pathway/PathwayController.java
+++ b/src/main/java/org/ohdsi/webapi/pathway/PathwayController.java
@@ -25,8 +25,10 @@
import org.ohdsi.webapi.versioning.dto.VersionDTO;
import org.ohdsi.webapi.versioning.dto.VersionUpdateDTO;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
@@ -34,6 +36,7 @@
import javax.transaction.Transactional;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -53,6 +56,9 @@ public class PathwayController {
private PathwayChecker checker;
private PermissionService permissionService;
+ @Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
+ private boolean defaultGlobalReadPermissions;
+
@Autowired
public PathwayController(ConversionService conversionService, ConverterUtils converterUtils, PathwayService pathwayService, SourceService sourceService, CommonGenerationSensitiveInfoService sensitiveInfoService, PathwayChecker checker, PermissionService permissionService, I18nService i18nService) {
@@ -156,13 +162,29 @@ public PathwayAnalysisDTO importAnalysis(final PathwayAnalysisExportDTO dto) {
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
public Page list(@Pagination Pageable pageable) {
-
- return pathwayService.getPage(pageable).map(pa -> {
- PathwayAnalysisDTO dto = conversionService.convert(pa, PathwayAnalysisDTO.class);
- permissionService.fillWriteAccess(pa, dto);
- return dto;
- });
+ if (defaultGlobalReadPermissions == true) { // don't filter based on read permissions
+ return pathwayService.getPage(pageable).map(pa -> {
+ PathwayAnalysisDTO dto = conversionService.convert(pa, PathwayAnalysisDTO.class);
+ permissionService.fillWriteAccess(pa, dto);
+ permissionService.fillReadAccess(pa, dto);
+ return dto;
+ });
+ } else { // filter out entities that the user does not have read permissions to view
+ List dtolist = new ArrayList();
+
+ Page newpage = pathwayService.getPage(pageable);
+ for (PathwayAnalysisEntity pa : newpage) {
+ if (permissionService.hasReadAccess(pa)) {
+ PathwayAnalysisDTO dto = conversionService.convert(pa, PathwayAnalysisDTO.class);
+ permissionService.fillWriteAccess(pa, dto);
+ permissionService.fillReadAccess(pa, dto);
+ dtolist.add(dto);
+ }
+ }
+ return new PageImpl(dtolist, pageable, dtolist.size());
+ }
}
+
/**
* Check that a pathway analysis name exists.
diff --git a/src/main/java/org/ohdsi/webapi/prediction/PredictionController.java b/src/main/java/org/ohdsi/webapi/prediction/PredictionController.java
index 67a35b7b55..0838f2363a 100644
--- a/src/main/java/org/ohdsi/webapi/prediction/PredictionController.java
+++ b/src/main/java/org/ohdsi/webapi/prediction/PredictionController.java
@@ -21,6 +21,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.stereotype.Controller;
@@ -67,6 +68,9 @@ public class PredictionController {
private PermissionService permissionService;
+ @Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
+ private boolean defaultGlobalReadPermissions;
+
@Autowired
public PredictionController(PredictionService service,
GenericConversionService conversionService,
@@ -93,26 +97,40 @@ public PredictionController(PredictionService service,
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
- public List getAnalysisList() {
-
- return StreamSupport
- .stream(service.getAnalysisList().spliterator(), false)
- .map(analysis -> {
- CommonAnalysisDTO dto = conversionService.convert(analysis, CommonAnalysisDTO.class);
- permissionService.fillWriteAccess(analysis, dto);
- return dto;
- })
- .collect(Collectors.toList());
- }
-
- /**
- * Check to see if a prediction design exists by name
- *
- * @summary Prediction design exists by name
- * @param id The prediction design id
- * @param name The prediction design name
- * @return 1 if a prediction design with the given name and id exist in WebAPI and 0 otherwise
- */
+ public List getAnalysisList() {
+ if (defaultGlobalReadPermissions == true) { // don't filter based on read permissions
+ return StreamSupport
+ .stream(service.getAnalysisList().spliterator(), false)
+ .map(analysis -> {
+ CommonAnalysisDTO dto = conversionService.convert(analysis, CommonAnalysisDTO.class);
+ permissionService.fillWriteAccess(analysis, dto);
+ permissionService.fillReadAccess(analysis, dto);
+ return dto;
+ })
+ .collect(Collectors.toList());
+ } else {
+ return StreamSupport
+ .stream(service.getAnalysisList().spliterator(), false)
+ .filter(candidateAnalysis -> permissionService.hasReadAccess(candidateAnalysis))
+ .map(analysis -> {
+ CommonAnalysisDTO dto = conversionService.convert(analysis, CommonAnalysisDTO.class);
+ permissionService.fillWriteAccess(analysis, dto);
+ permissionService.fillReadAccess(analysis, dto);
+ return dto;
+ })
+ .collect(Collectors.toList());
+ }
+ }
+
+
+ /**
+ * Check to see if a prediction design exists by name
+ *
+ * @summary Prediction design exists by name
+ * @param id The prediction design id
+ * @param name The prediction design name
+ * @return 1 if a prediction design with the given name and id exist in WebAPI and 0 otherwise
+ */
@GET
@Path("/{id}/exists")
@Produces(MediaType.APPLICATION_JSON)
diff --git a/src/main/java/org/ohdsi/webapi/security/PermissionController.java b/src/main/java/org/ohdsi/webapi/security/PermissionController.java
index 7a1bd26c4c..25ae6cedcb 100644
--- a/src/main/java/org/ohdsi/webapi/security/PermissionController.java
+++ b/src/main/java/org/ohdsi/webapi/security/PermissionController.java
@@ -83,26 +83,31 @@ public List listAccessesForEntity(@QueryParam("roleSearch") String role
}
/**
- * Get entity role access information
+ * Get roles that have a permission type (READ/WRITE) to entity
*
- * @summary Get entity role information
+ * @summary Get roles that have a specific permission (READ/WRITE) for the entity
* @param entityType The entity type
* @param entityId The entity ID
- * @return The list of roles
+ * @return The list of permissions for the permission type
* @throws Exception
*/
@GET
- @Path("/access/{entityType}/{entityId}")
+ @Path("/access/{entityType}/{entityId}/{permType}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
- public List listAccessesForEntity(
+ public List listAccessesForEntityByPermType(
@PathParam("entityType") EntityType entityType,
- @PathParam("entityId") Integer entityId
+ @PathParam("entityId") Integer entityId,
+ @PathParam("permType") String permType
) throws Exception {
permissionService.checkCommonEntityOwnership(entityType, entityId);
-
- Set permissionTemplates = permissionService.getTemplatesForType(entityType, AccessType.WRITE).keySet();
+ Set permissionTemplates = null;
+ if (permType == "WRITE") {
+ permissionTemplates = permissionService.getTemplatesForType(entityType, AccessType.WRITE).keySet();
+ } else {
+ permissionTemplates = permissionService.getTemplatesForType(entityType, AccessType.READ).keySet();
+ }
List permissions = permissionTemplates
.stream()
diff --git a/src/main/java/org/ohdsi/webapi/security/PermissionService.java b/src/main/java/org/ohdsi/webapi/security/PermissionService.java
index b3ec2f7e5c..3f11454db6 100644
--- a/src/main/java/org/ohdsi/webapi/security/PermissionService.java
+++ b/src/main/java/org/ohdsi/webapi/security/PermissionService.java
@@ -136,6 +136,8 @@ public Map getPermissionTemplates(EntityPermissionSchema permiss
switch (accessType) {
case WRITE:
return permissionSchema.getWritePermissions();
+ case READ:
+ return permissionSchema.getReadPermissions();
default:
throw new UnsupportedOperationException();
}
@@ -227,6 +229,25 @@ public List getRolesHavingPermissions(EntityType entityType, Number id)
return roles;
}
+ public List getRolesHavingReadPermissions(EntityType entityType, Number id) {
+ Set permissionTemplates = getTemplatesForType(entityType, AccessType.READ).keySet();
+ preparePermissionCache(entityType, permissionTemplates);
+
+ List permissions = permissionTemplates.stream()
+ .map(pt -> getPermission(pt, id))
+ .collect(Collectors.toList());
+ int fitCount = permissions.size();
+ Map roleMap = permissions.stream()
+ .filter(p -> permissionCache.get().get(entityType).get(p) != null)
+ .flatMap(p -> permissionCache.get().get(entityType).get(p).stream())
+ .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
+ List roles = roleMap.entrySet().stream()
+ .filter(es -> es.getValue() == fitCount)
+ .map(es -> es.getKey())
+ .collect(Collectors.toList());
+ return roles;
+ }
+
public void clearPermissionCache() {
this.permissionCache.set(new ConcurrentHashMap<>());
}
@@ -237,7 +258,9 @@ public boolean hasWriteAccess(CommonEntity entity) {
try {
String login = this.permissionManager.getSubjectName();
UserSimpleAuthorizationInfo authorizationInfo = this.permissionManager.getAuthorizationInfo(login);
- if (!Objects.equals(authorizationInfo.getUserId(), entity.getCreatedBy().getId())) {
+ if (Objects.equals(authorizationInfo.getUserId(), entity.getCreatedBy().getId())) {
+ hasAccess = true; // the role is the one that created the artifact
+ } else {
EntityType entityType = entityPermissionSchemaResolver.getEntityType(entity.getClass());
List roles = getRolesHavingPermissions(entityType, entity.getId());
@@ -255,11 +278,44 @@ public boolean hasWriteAccess(CommonEntity entity) {
return hasAccess;
}
+
+ public boolean hasReadAccess(CommonEntity entity) {
+ boolean hasAccess = false;
+ if (securityEnabled && entity.getCreatedBy() != null) {
+ try {
+ String login = this.permissionManager.getSubjectName();
+ UserSimpleAuthorizationInfo authorizationInfo = this.permissionManager.getAuthorizationInfo(login);
+ if (Objects.equals(authorizationInfo.getUserId(), entity.getCreatedBy().getId())){
+ hasAccess = true; // the role is the one that created the artifact
+ } else {
+ EntityType entityType = entityPermissionSchemaResolver.getEntityType(entity.getClass());
+
+ List roles = getRolesHavingReadPermissions(entityType, entity.getId());
+
+ Collection userRoles = authorizationInfo.getRoles();
+ hasAccess = roles.stream()
+ .anyMatch(r -> userRoles.stream()
+ .anyMatch(re -> re.equals(r.getName())));
+ }
+ } catch (Exception e) {
+ logger.error("Error getting user roles and permissions", e);
+ throw new RuntimeException(e);
+ }
+ }
+ return hasAccess;
+ }
+
public void fillWriteAccess(CommonEntity entity, CommonEntityDTO entityDTO) {
if (securityEnabled && entity.getCreatedBy() != null) {
entityDTO.setHasWriteAccess(hasWriteAccess(entity));
}
}
+
+ public void fillReadAccess(CommonEntity entity, CommonEntityDTO entityDTO) {
+ if (securityEnabled && entity.getCreatedBy() != null) {
+ entityDTO.setHasReadAccess(hasReadAccess(entity));
+ }
+ }
public boolean isSecurityEnabled() {
return this.securityEnabled;
diff --git a/src/main/java/org/ohdsi/webapi/security/model/CohortCharacterizationPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/CohortCharacterizationPermissionSchema.java
index 7768187da0..f6ea10012a 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/CohortCharacterizationPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/CohortCharacterizationPermissionSchema.java
@@ -13,8 +13,19 @@ public class CohortCharacterizationPermissionSchema extends EntityPermissionSche
put("cohort-characterization:%s:delete", "Delete Cohort Characterization with ID = %s");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("cohort-characterization:%s:get", "Get cohort characterization");
+ put("cohort-characterization:%s:generation:get", "Get cohort characterization generations");
+ put("cohort-characterization:generation:*:get", "Get cohort characterization generation");
+ put("cohort-characterization:design:get", "cohort-characterization:design:get");
+ put("cohort-characterization:%s:design:get", "Get cohort characterization design");
+ put("cohort-characterization:design:%s:get", "view cohort characterization with id %s");
+ put("cohort-characterization:%s:version:get", "Get list of characterization versions");
+ put("cohort-characterization:%s:version:*:get", "Get list of characterization version");
+ }};
+
public CohortCharacterizationPermissionSchema() {
- super(EntityType.COHORT_CHARACTERIZATION, new HashMap<>(), writePermissions);
+ super(EntityType.COHORT_CHARACTERIZATION, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/CohortDefinitionPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/CohortDefinitionPermissionSchema.java
index 5c7f87c50a..bb6781ae0a 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/CohortDefinitionPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/CohortDefinitionPermissionSchema.java
@@ -14,8 +14,17 @@ public class CohortDefinitionPermissionSchema extends EntityPermissionSchema {
put("cohortdefinition:%s:check:post", "Fix Cohort Definition with ID = %s");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("cohortdefinition:%s:get", "Get Cohort Definition by ID");
+ put("cohortdefinition:%s:info:get","");
+
+ put("cohortdefinition:%s:version:get", "Get list of cohort versions");
+ put("cohortdefinition:%s:version:*:get", "Get cohort version");
+ }
+ };
+
public CohortDefinitionPermissionSchema() {
- super(EntityType.COHORT_DEFINITION, new HashMap<>(), writePermissions);
+ super(EntityType.COHORT_DEFINITION, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/ConceptSetPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/ConceptSetPermissionSchema.java
index 846cbc9b0c..66b4b1a4b2 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/ConceptSetPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/ConceptSetPermissionSchema.java
@@ -14,8 +14,14 @@ public class ConceptSetPermissionSchema extends EntityPermissionSchema {
put("conceptset:%s:delete", "Delete Concept Set with ID = %s");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("conceptset:%s:get", "view conceptset definition with id %s");
+ put("conceptset:%s:expression:get", "Resolve concept set %s expression");
+ put("conceptset:%s:version:*:expression:get", "Get expression for concept set %s items for default source");
+ }};
+
public ConceptSetPermissionSchema() {
- super(EntityType.CONCEPT_SET, new HashMap<>(), writePermissions);
+ super(EntityType.CONCEPT_SET, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/EstimationPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/EstimationPermissionSchema.java
index 416b049fa6..0df160ee52 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/EstimationPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/EstimationPermissionSchema.java
@@ -13,8 +13,19 @@ public class EstimationPermissionSchema extends EntityPermissionSchema {
put("estimation:%s:delete", "Delete Estimation with ID=%s");
}};
- public EstimationPermissionSchema() {
+ private static Map readPermissions = new HashMap() {{
+ put("estimation:%s:get", "Get Estimation instance");
+ put("estimation:%s:generation:get", "View Estimation Generations");
+ put("estimation:%s:copy:get", "Copy Estimation instance");
+ put("estimation:%s:download:get", "Download Estimation package");
+ put("estimation:%s:export:get", "Export Estimation");
+ put("estimation:%s:generation:get", "View Estimation Generations");
+ put("comparativecohortanalysis:%s:get","Get estimation");
+ }
+ };
- super(EntityType.ESTIMATION, new HashMap<>(), writePermissions);
+
+ public EstimationPermissionSchema() {
+ super(EntityType.ESTIMATION, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/FeatureAnalysisPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/FeatureAnalysisPermissionSchema.java
index 0090a45b3b..b36ea20b64 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/FeatureAnalysisPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/FeatureAnalysisPermissionSchema.java
@@ -13,8 +13,14 @@ public class FeatureAnalysisPermissionSchema extends EntityPermissionSchema {
put("feature-analysis:%s:delete", "Delete Feature Analysis with ID = %s");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("feature-analysis:%s:get", "get feature analysis");
+ put("feature-analysis:aggregates:get", "feature-analysis:aggregates:get");
+ }
+ };
+
public FeatureAnalysisPermissionSchema() {
- super(EntityType.FE_ANALYSIS, new HashMap<>(), writePermissions);
+ super(EntityType.FE_ANALYSIS, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/IncidenceRatePermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/IncidenceRatePermissionSchema.java
index 1c336a3ff2..57441e5edf 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/IncidenceRatePermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/IncidenceRatePermissionSchema.java
@@ -15,8 +15,18 @@ public class IncidenceRatePermissionSchema extends EntityPermissionSchema {
put("ir:%s:delete", "Delete Incidence Rate with ID=%s");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("ir:%s:get", "view list of incident rates");
+ put("ir:%s:version:get", "Get list of IR analsis versions");
+ put("ir:%s:version:*:get", "Get IR analysis version");
+ put("ir:%s:copy:get","Copy incidence rate");
+ put("ir:%s:info:get","Get IR info");
+ put("ir:%s:design:get","Export Incidence Rates design");
+ }
+ };
+
public IncidenceRatePermissionSchema() {
- super(EntityType.INCIDENCE_RATE, new HashMap<>(), writePermissions);
+ super(EntityType.INCIDENCE_RATE, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/PathwayAnalysisPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/PathwayAnalysisPermissionSchema.java
index 6c19b76c09..2f6f30ec63 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/PathwayAnalysisPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/PathwayAnalysisPermissionSchema.java
@@ -14,8 +14,19 @@ public class PathwayAnalysisPermissionSchema extends EntityPermissionSchema {
put("pathway-analysis:%s:delete", "Delete Pathway Analysis with ID = %s");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("pathway-analysis:%s:get", "Get Pathways Analysis instance");
+ put("pathway-analysis:%s:generation:get", "Get Pathways Analysis generations list");
+ put("pathway-analysis:generation:*:get", "Get Pathways Analysis generation instance");
+ put("pathway-analysis:generation:*:result:get", "Get Pathways Analysis generation results");
+ put("pathway-analysis:generation:*:design:get", "Get Pathways Analysis generation design");
+ put("pathway-analysis:%s:version:get", "Get list of pathway analysis versions");
+ put("pathway-analysis:%s:version:*:get", "Get pathway analysis version");
+ }
+ };
+
public PathwayAnalysisPermissionSchema() {
- super(EntityType.PATHWAY_ANALYSIS, new HashMap<>(), writePermissions);
+ super(EntityType.PATHWAY_ANALYSIS, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/PredictionPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/PredictionPermissionSchema.java
index ad34f12425..d2e3cc458d 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/PredictionPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/PredictionPermissionSchema.java
@@ -13,8 +13,19 @@ public class PredictionPermissionSchema extends EntityPermissionSchema {
put("prediction:%s:delete", "Delete Estimation with ID=%s");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("prediction:%s:get", "Get Prediction instance");
+ put("prediction:%s:copy:get", "Copy Prediction instance");
+ put("prediction:%s:download:get", "Download Prediction package");
+ put("prediction:%s:export:get", "Export Prediction");
+ put("prediction:%s:generation:get", "View Prediction Generations");
+ put("prediction:%s:exists:get", "Check name uniqueness of prediction");
+ put("plp:%s:get", "Get population level prediction");
+ }
+ };
+
public PredictionPermissionSchema() {
- super(EntityType.PREDICTION, new HashMap<>(), writePermissions);
+ super(EntityType.PREDICTION, readPermissions, writePermissions);
}
}
diff --git a/src/main/java/org/ohdsi/webapi/security/model/ReusablePermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/ReusablePermissionSchema.java
index 78803dc01e..6ee5a940a6 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/ReusablePermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/ReusablePermissionSchema.java
@@ -13,6 +13,13 @@ public class ReusablePermissionSchema extends EntityPermissionSchema {
put("reusable:%s:put", "Update reusable");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("reusable:%s:get", "view reusable with id %s");
+ put("reusable:%s:expression:get", "Resolve reusable %s expression");
+ put("reusable:%s:version:*:get", "Get expression for reusable %s items for default source");
+ }
+ };
+
public ReusablePermissionSchema() {
super(EntityType.REUSABLE, new HashMap<>(), writePermissions);
diff --git a/src/main/java/org/ohdsi/webapi/security/model/TagPermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/TagPermissionSchema.java
index e68747570b..58476a4fe9 100644
--- a/src/main/java/org/ohdsi/webapi/security/model/TagPermissionSchema.java
+++ b/src/main/java/org/ohdsi/webapi/security/model/TagPermissionSchema.java
@@ -13,6 +13,12 @@ public class TagPermissionSchema extends EntityPermissionSchema {
put("tag:%s:put", "Update tag");
}};
+ private static Map readPermissions = new HashMap() {{
+ put("tag:get", "view tag with id %s");
+ put("tag:search:get", "Resolve tag %s expression");
+ }
+ };
+
public TagPermissionSchema() {
super(EntityType.TAG, new HashMap<>(), writePermissions);
diff --git a/src/main/java/org/ohdsi/webapi/service/CohortDefinitionService.java b/src/main/java/org/ohdsi/webapi/service/CohortDefinitionService.java
index 976389eb54..e580a3eb56 100644
--- a/src/main/java/org/ohdsi/webapi/service/CohortDefinitionService.java
+++ b/src/main/java/org/ohdsi/webapi/service/CohortDefinitionService.java
@@ -86,6 +86,7 @@
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.convert.ConversionService;
import org.springframework.jdbc.core.RowMapper;
@@ -204,6 +205,9 @@ public class CohortDefinitionService extends AbstractDaoService implements HasTa
@Autowired
private VersionService versionService;
+ @Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
+ private boolean defaultGlobalReadPermissions;
+
private final MarkdownRender markdownPF = new MarkdownRender();
private final List extensions = Arrays.asList(TablesExtension.create());
@@ -406,14 +410,26 @@ public GenerateSqlResult generateSql(GenerateSqlRequest request) {
@Transactional
public List getCohortDefinitionList() {
List definitions = cohortDefinitionRepository.list();
-
- return definitions.stream()
- .map(def -> {
- CohortMetadataDTO dto = conversionService.convert(def, CohortMetadataImplDTO.class);
- permissionService.fillWriteAccess(def, dto);
- return dto;
- })
- .collect(Collectors.toList());
+ if (defaultGlobalReadPermissions == true) { // don't filter based on read permissions
+ return definitions.stream()
+ .map(def -> {
+ CohortMetadataDTO dto = conversionService.convert(def, CohortMetadataImplDTO.class);
+ permissionService.fillWriteAccess(def, dto);
+ permissionService.fillReadAccess(def, dto);
+ return dto;
+ })
+ .collect(Collectors.toList());
+ } else { // filter out cohortdefinitions that the user does not have read access to
+ return definitions.stream()
+ .filter(candidateCohortDef -> permissionService.hasReadAccess(candidateCohortDef))
+ .map(def -> {
+ CohortMetadataDTO dto = conversionService.convert(def, CohortMetadataImplDTO.class);
+ permissionService.fillWriteAccess(def, dto);
+ permissionService.fillReadAccess(def, dto);
+ return dto;
+ })
+ .collect(Collectors.toList());
+ }
}
/**
diff --git a/src/main/java/org/ohdsi/webapi/service/ConceptSetService.java b/src/main/java/org/ohdsi/webapi/service/ConceptSetService.java
index 1186a22445..76aceb5a10 100644
--- a/src/main/java/org/ohdsi/webapi/service/ConceptSetService.java
+++ b/src/main/java/org/ohdsi/webapi/service/ConceptSetService.java
@@ -58,6 +58,7 @@
import org.ohdsi.webapi.versioning.service.VersionService;
import org.ohdsi.webapi.vocabulary.Concept;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Component;
@@ -103,6 +104,9 @@ public class ConceptSetService extends AbstractDaoService implements HasTags versionService;
+ @Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
+ private boolean defaultGlobalReadPermissions;
+
public static final String COPY_NAME = "copyName";
/**
@@ -131,15 +135,28 @@ public ConceptSetDTO getConceptSet(@PathParam("id") final int id) {
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public Collection getConceptSets() {
- return getTransactionTemplate().execute(transactionStatus ->
- StreamSupport.stream(getConceptSetRepository().findAll().spliterator(), false)
- .map(conceptSet -> {
- ConceptSetDTO dto = conversionService.convert(conceptSet, ConceptSetDTO.class);
- permissionService.fillWriteAccess(conceptSet, dto);
- return dto;
- })
- .collect(Collectors.toList())
- );
+ if (defaultGlobalReadPermissions == true) { // don't filter based on read permissions
+ return getTransactionTemplate().execute(
+ transactionStatus -> StreamSupport.stream(getConceptSetRepository().findAll().spliterator(), false)
+ .map(conceptSet -> {
+ ConceptSetDTO dto = conversionService.convert(conceptSet, ConceptSetDTO.class);
+ permissionService.fillWriteAccess(conceptSet, dto);
+ permissionService.fillReadAccess(conceptSet, dto);
+ return dto;
+ })
+ .collect(Collectors.toList()));
+ } else { // filter out conceptsets that the user does not have read access to
+ return getTransactionTemplate().execute(
+ transactionStatus -> StreamSupport.stream(getConceptSetRepository().findAll().spliterator(), false)
+ .filter(candidateConceptSet -> permissionService.hasReadAccess(candidateConceptSet))
+ .map(conceptSet -> {
+ ConceptSetDTO dto = conversionService.convert(conceptSet, ConceptSetDTO.class);
+ permissionService.fillWriteAccess(conceptSet, dto);
+ permissionService.fillReadAccess(conceptSet, dto);
+ return dto;
+ })
+ .collect(Collectors.toList()));
+ }
}
/**
diff --git a/src/main/java/org/ohdsi/webapi/service/IRAnalysisService.java b/src/main/java/org/ohdsi/webapi/service/IRAnalysisService.java
index ea66cdce4c..9ef34897f6 100644
--- a/src/main/java/org/ohdsi/webapi/service/IRAnalysisService.java
+++ b/src/main/java/org/ohdsi/webapi/service/IRAnalysisService.java
@@ -85,6 +85,7 @@
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.ConversionService;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
@@ -141,6 +142,9 @@ public class IRAnalysisService extends AbstractDaoService implements
private final IRAnalysisQueryBuilder queryBuilder;
+ @Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
+ private boolean defaultGlobalReadPermissions;
+
@Autowired
private IncidenceRateAnalysisRepository irAnalysisRepository;
@@ -341,17 +345,32 @@ private String getStrataTreemapData(int analysisId, int targetId, int outcomeId,
@Override
public List getIRAnalysisList() {
-
- return getTransactionTemplate().execute(transactionStatus -> {
- Iterable analysisList = this.irAnalysisRepository.findAll();
- return StreamSupport.stream(analysisList.spliterator(), false)
- .map(analysis -> {
+ if (defaultGlobalReadPermissions == true) { // don't filter based on read permissions
+ return getTransactionTemplate().execute(transactionStatus -> {
+ Iterable analysisList = this.irAnalysisRepository.findAll();
+ return StreamSupport.stream(analysisList.spliterator(), false)
+ .map(analysis -> {
IRAnalysisShortDTO dto = conversionService.convert(analysis, IRAnalysisShortDTO.class);
permissionService.fillWriteAccess(analysis, dto);
+ permissionService.fillReadAccess(analysis, dto);
return dto;
})
- .collect(Collectors.toList());
- });
+ .collect(Collectors.toList());
+ });
+ } else { // filter out entities that the user does not have read permissions to view
+ return getTransactionTemplate().execute(transactionStatus -> {
+ Iterable analysisList = this.irAnalysisRepository.findAll();
+ return StreamSupport.stream(analysisList.spliterator(), false)
+ .filter(candidateIRAnalysis -> permissionService.hasReadAccess(candidateIRAnalysis))
+ .map(analysis -> {
+ IRAnalysisShortDTO dto = conversionService.convert(analysis, IRAnalysisShortDTO.class);
+ permissionService.fillWriteAccess(analysis, dto);
+ permissionService.fillReadAccess(analysis, dto);
+ return dto;
+ })
+ .collect(Collectors.toList());
+ });
+ }
}
@Override
diff --git a/src/main/java/org/ohdsi/webapi/service/dto/CommonEntityDTO.java b/src/main/java/org/ohdsi/webapi/service/dto/CommonEntityDTO.java
index 5a33780593..287894aef9 100644
--- a/src/main/java/org/ohdsi/webapi/service/dto/CommonEntityDTO.java
+++ b/src/main/java/org/ohdsi/webapi/service/dto/CommonEntityDTO.java
@@ -17,7 +17,9 @@ public abstract class CommonEntityDTO implements CommonDTO {
private Date createdDate;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date modifiedDate;
+
private boolean hasWriteAccess;
+ private boolean hasReadAccess;
public UserDTO getCreatedBy() {
return createdBy;
@@ -58,4 +60,12 @@ public boolean isHasWriteAccess() {
public void setHasWriteAccess(boolean hasWriteAccess) {
this.hasWriteAccess = hasWriteAccess;
}
+
+ public boolean isHasReadAccess() {
+ return hasReadAccess;
+ }
+
+ public void setHasReadAccess(boolean hasReadAccess) {
+ this.hasReadAccess = hasReadAccess;
+ }
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index cbc3823116..cd1afb2013 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -107,6 +107,7 @@ csrf.disable=true
sparql.endpoint=http://virtuoso.ohdsi.org:8890/sparql?default-graph-uri=&query=
+security.defaultGlobalReadPermissions=${security.defaultGlobalReadPermissions}
security.provider=${security.provider}
security.cors.enabled=${security.cors.enabled}
security.token.expiration=${security.token.expiration}