From bd62aa1cfdb53e3d832c2dd136c2142470d73a9a Mon Sep 17 00:00:00 2001 From: Jeremy Adams Date: Mon, 16 May 2022 13:18:53 -0400 Subject: [PATCH 1/7] bulk object request endpoint implemented using object request handler --- build.gradle | 6 +++ .../drs/app/DrsServerSpringConfig.java | 2 +- .../starterkit/drs/controller/Objects.java | 47 ++++++++++++++++++- .../starterkit/drs/model/BulkRequest.java | 25 ++++++++++ .../starterkit/drs/model/BulkResponse.java | 32 +++++++++++++ .../starterkit/drs/model/BulkSummary.java | 22 +++++++++ 6 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/BulkResponse.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/BulkSummary.java diff --git a/build.gradle b/build.gradle index 50c0c02..a01ab65 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,9 @@ repositories { } dependencies { + compileOnly 'org.projectlombok:lombok:1.18.22' + annotationProcessor 'org.projectlombok:lombok:1.18.22' + implementation 'com.google.guava:guava:28.1-jre' implementation 'org.springframework.boot:spring-boot-starter-web:2.5.0' implementation 'commons-cli:commons-cli:1.4' @@ -64,6 +67,9 @@ dependencies { implementation 'org.xerial:sqlite-jdbc:3.8.11.2' implementation 'org.ga4gh:ga4gh-starter-kit-common:0.5.6' + testCompileOnly 'org.projectlombok:lombok:1.18.22' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.22' + testImplementation 'org.testng:testng:7.0.0' testImplementation 'org.springframework.boot:spring-boot-starter-test:2.5.0' testImplementation 'org.springframework.security:spring-security-test' diff --git a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java index 48f54d9..19c273e 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java +++ b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java @@ -261,7 +261,7 @@ public DrsHibernateUtil getDrsHibernateUtil( * @return drs object request handler */ @Bean - @RequestScope + @Scope(DrsServerConstants.PROTOTYPE) public ObjectRequestHandler objectRequestHandler() { return new ObjectRequestHandler(); } diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java index e53378f..145cae9 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java @@ -2,18 +2,27 @@ import static org.ga4gh.starterkit.drs.constant.DrsApiConstants.DRS_API_V1; +import org.ga4gh.starterkit.common.exception.CustomException; import org.ga4gh.starterkit.common.util.logging.LoggingUtil; import org.ga4gh.starterkit.drs.model.AccessURL; +import org.ga4gh.starterkit.drs.model.BulkRequest; +import org.ga4gh.starterkit.drs.model.BulkResponse; import org.ga4gh.starterkit.drs.model.DrsObject; import org.ga4gh.starterkit.drs.utils.SerializeView; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; + import javax.annotation.Resource; import com.fasterxml.jackson.annotation.JsonView; @@ -22,7 +31,7 @@ */ @RestController @RequestMapping(DRS_API_V1 + "/objects") -public class Objects { +public class Objects implements ApplicationContextAware { @Resource(name = "objectRequestHandler") private ObjectRequestHandler objectRequestHandler; @@ -33,6 +42,8 @@ public class Objects { @Autowired private LoggingUtil loggingUtil; + private ApplicationContext context; + // Standard endpoints /** @@ -65,4 +76,38 @@ public AccessURL getAccessURLById( loggingUtil.debug("Public API request: AccessURL for DRS id '" + objectId + "', access id '" + accessId + "'"); return accessRequestHandler.prepare(objectId, accessId).handleRequest(); } + + @PostMapping + @JsonView(SerializeView.Public.class) + public BulkResponse getBulkObjects( + @RequestBody BulkRequest bulkRequest + ) { + BulkResponse bulkResponse = new BulkResponse(); + int requested = 0; + int resolved = 0; + int unresolved = 0; + + for (String drsObjectId : bulkRequest.getSelection()) { + requested++; + try { + ObjectRequestHandler handler = context.getBean(ObjectRequestHandler.class); + DrsObject drsObject = handler.prepare(drsObjectId, false).handleRequest(); + bulkResponse.getResolvedDrsObject().add(drsObject); + resolved++; + } catch (CustomException ex) { + int httpStatus = ex.getClass().getAnnotation(ResponseStatus.class).value().value(); + bulkResponse.getUnresolvedDrsObject().put(drsObjectId, httpStatus); + unresolved++; + } + } + bulkResponse.getSummary().setRequested(requested); + bulkResponse.getSummary().setResolved(resolved); + bulkResponse.getSummary().setUnresolved(unresolved); + + return bulkResponse; + } + + public void setApplicationContext(ApplicationContext context) { + this.context = context; + } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java new file mode 100644 index 0000000..0fc61c8 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java @@ -0,0 +1,25 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkRequest { + private List selection; + + public BulkRequest() { + selection = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkResponse.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkResponse.java new file mode 100644 index 0000000..35b11a0 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkResponse.java @@ -0,0 +1,32 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkResponse { + + private BulkSummary summary; + private List resolvedDrsObject; + private Map unresolvedDrsObject; + + public BulkResponse() { + summary = new BulkSummary(); + resolvedDrsObject = new ArrayList<>(); + unresolvedDrsObject = new HashMap<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkSummary.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkSummary.java new file mode 100644 index 0000000..7706682 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkSummary.java @@ -0,0 +1,22 @@ +package org.ga4gh.starterkit.drs.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkSummary { + private int requested; + private int resolved; + private int unresolved; +} From 7ff3dca44f076dec4c81e49fb3ba9258007be52e Mon Sep 17 00:00:00 2001 From: Jeremy Adams Date: Mon, 16 May 2022 13:33:42 -0400 Subject: [PATCH 2/7] basic bulk accessURL request --- .../starterkit/drs/controller/Objects.java | 24 ++++++++++++++++++ .../drs/model/BulkAccessRequest.java | 25 +++++++++++++++++++ .../drs/model/BulkObjectAccessId.java | 21 ++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/BulkAccessRequest.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/BulkObjectAccessId.java diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java index 145cae9..9f5a34c 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java @@ -2,9 +2,14 @@ import static org.ga4gh.starterkit.drs.constant.DrsApiConstants.DRS_API_V1; +import java.util.ArrayList; +import java.util.List; + import org.ga4gh.starterkit.common.exception.CustomException; import org.ga4gh.starterkit.common.util.logging.LoggingUtil; import org.ga4gh.starterkit.drs.model.AccessURL; +import org.ga4gh.starterkit.drs.model.BulkAccessRequest; +import org.ga4gh.starterkit.drs.model.BulkObjectAccessId; import org.ga4gh.starterkit.drs.model.BulkRequest; import org.ga4gh.starterkit.drs.model.BulkResponse; import org.ga4gh.starterkit.drs.model.DrsObject; @@ -107,6 +112,25 @@ public BulkResponse getBulkObjects( return bulkResponse; } + @PostMapping(path = "/access") + @JsonView(SerializeView.Public.class) + public List getBulkAccessURLs( + @RequestBody BulkAccessRequest bulkAccessRequest + ) { + List accessURLs = new ArrayList<>(); + for (BulkObjectAccessId idPair : bulkAccessRequest.getSelection()) { + try { + AccessRequestHandler handler = context.getBean(AccessRequestHandler.class); + AccessURL accessURL = handler.prepare(idPair.getObjectId(), idPair.getAccessId()).handleRequest(); + accessURLs.add(accessURL); + } catch (CustomException ex) { + + } + } + + return accessURLs; + } + public void setApplicationContext(ApplicationContext context) { this.context = context; } diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkAccessRequest.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAccessRequest.java new file mode 100644 index 0000000..84c083d --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAccessRequest.java @@ -0,0 +1,25 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkAccessRequest { + private List selection; + + public BulkAccessRequest() { + selection = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkObjectAccessId.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkObjectAccessId.java new file mode 100644 index 0000000..ffdf077 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkObjectAccessId.java @@ -0,0 +1,21 @@ +package org.ga4gh.starterkit.drs.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkObjectAccessId { + private String objectId; + private String accessId; +} From 9e8b5c9f4342b0b8f3d2b8a764e7bf86d3dd1fc5 Mon Sep 17 00:00:00 2001 From: Jeremy Adams Date: Mon, 16 May 2022 15:54:32 -0400 Subject: [PATCH 3/7] begin logic to handle passports-based auth --- build.gradle | 1 + database/sqlite/add-dev-dataset.sql | 110 ++++++ database/sqlite/create-tables.sql | 21 ++ .../drs/app/DrsServerSpringConfig.java | 4 + .../starterkit/drs/controller/Objects.java | 11 +- .../drs/exception/ForbiddenException.java | 23 ++ .../drs/exception/UnauthorizedException.java | 23 ++ .../starterkit/drs/model/BulkRequest.java | 2 + .../ga4gh/starterkit/drs/model/DrsObject.java | 318 +----------------- .../starterkit/drs/model/PassportBroker.java | 68 ++++ .../starterkit/drs/model/PassportVisa.java | 80 +++++ .../drs/utils/passport/UserPassport.java | 19 ++ .../drs/utils/passport/UserPassportMap.java | 49 +++ .../requesthandler/ObjectRequestHandler.java | 33 +- ...BundleRecursiveChecksumCalculatorTest.java | 5 - 15 files changed, 457 insertions(+), 310 deletions(-) create mode 100644 src/main/java/org/ga4gh/starterkit/drs/exception/ForbiddenException.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/exception/UnauthorizedException.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/PassportBroker.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/PassportVisa.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassport.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java diff --git a/build.gradle b/build.gradle index a01ab65..3bd8bf2 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,7 @@ dependencies { implementation 'javax.xml.bind:jaxb-api:2.2.8' implementation 'org.springdoc:springdoc-openapi-ui:1.2.33' implementation 'org.xerial:sqlite-jdbc:3.8.11.2' + implementation 'com.auth0:java-jwt:3.19.1' implementation 'org.ga4gh:ga4gh-starter-kit-common:0.5.6' testCompileOnly 'org.projectlombok:lombok:1.18.22' diff --git a/database/sqlite/add-dev-dataset.sql b/database/sqlite/add-dev-dataset.sql index 1110eb7..b4239ce 100644 --- a/database/sqlite/add-dev-dataset.sql +++ b/database/sqlite/add-dev-dataset.sql @@ -538,3 +538,113 @@ INSERT INTO drs_object_bundle VALUES ("355a74bd-6571-4d4a-8602-a9989936717f", "8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7"), ("355a74bd-6571-4d4a-8602-a9989936717f", "41898242-62a9-4129-9a2c-5a4e8f5f0afb"), ("355a74bd-6571-4d4a-8602-a9989936717f", "6b994f18-6189-4233-bb83-139686490d68"); + +/* MINI DATASET FOR PASSPORTS AUTH */ + +INSERT INTO passport_broker VALUES ( + 'https://ga4gh.org/', + 'insecureSecretPleaseOverride' +); + +INSERT INTO passport_visa VALUES +( + 0, + 'StarterKitDatasetsControlledAccessGrants', + 'https://datasets.starterkit.ga4gh.org/', + '87A3B5D68FD88197254D9889B4AAB', + 'https://ga4gh.org/' +); + +INSERT INTO drs_object ( + id, + description, + created_time, + mime_type, + name, + size, + updated_time, + version, + is_bundle +) VALUES + ( + "e26f8428-1a5d-4465-b389-10998ba7b894", + "PASSPORT AUTH - Phenopackets, Cao Patient 1", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.1", + 4257, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ), + ( + "996ead07-8dfc-4c5d-b319-5b59bebb507e", + "PASSPORT AUTH - Phenopackets, Cao Patient 2", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.2", + 7401, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ), + ( + "81e8b499-1054-4846-8df4-d079ea32a44f", + "PASSPORT AUTH - Phenopackets, Cao Patient 3", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.3", + 4251, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ), + ( + "672d0425-137c-437f-ab12-29a3cce1ffc0", + "PASSPORT AUTH - Phenopackets, Cao Patient 4", + "2022-05-16 20:00:00.000", + "application/json", + "passport.phenopackets.cao.4", + 9264, + "2022-05-16 20:00:00.000", + "1.0.0", + 0 + ); + +INSERT INTO drs_object_alias VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "Passport-Cao-FBN1-1"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "Passport-Cao-FBN1-2"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "Passport-Cao-FBN1-3"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "Passport-Cao-TGFBR2-4"); + +INSERT INTO drs_object_checksum (drs_object_id, checksum, type) VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "f81ea43c74824cc72c77a39a92bf7b71", "md5"), + ("e26f8428-1a5d-4465-b389-10998ba7b894", "34880a6b8aa517a6999da912614753ffb0a837a8", "sha1"), + ("e26f8428-1a5d-4465-b389-10998ba7b894", "ec44e2ad7ec84c7c42ba57b205e67c7c7416ae1932029d8364cc053cef7abe58", "sha256"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "1cbab050aa20410dc14ce6906f0312fa", "md5"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "3f2f2133054faf71ca9d678fa1fd8918a521faec", "sha1"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "2709878797b4e8c6a7db824fa596f42885551cef730d1408b2b620c9eee43089", "sha256"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "45b61bbe53b13463cd602081613ad855", "md5"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "d3192e17ffd97f7255ffbe2c6f9b447568107612", "sha1"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "f60583aad0e25fc4805668663bcc3bef271f4f93ee98be4f57f6e8c3e26d9dba", "sha256"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "7d3b0a967215523c29de61baff26bfa2", "md5"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "ae6c744487ddf785976893d1bdce2f81c017921e", "sha1"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "69dd919e14d162ebd0132a395cf1967346fd203210428287c354b11eebdda8b3", "sha256"); + +INSERT INTO file_access_object (drs_object_id, path) VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "./src/test/resources/data/phenopackets/Cao-2018-FBN1-Patient_1.json"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "./src/test/resources/data/phenopackets/Cao-2018-FBN1-Patient_2.json"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "./src/test/resources/data/phenopackets/Cao-2018-FBN1-Patient_3.json"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "./src/test/resources/data/phenopackets/Cao-2018-TGFBR2-Patient_4.json"); + +INSERT INTO aws_s3_access_object (drs_object_id, region, bucket, key) VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-FBN1-Patient_1.json"), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-FBN1-Patient_2.json"), + ("81e8b499-1054-4846-8df4-d079ea32a44f", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-FBN1-Patient_3.json"), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", "us-east-2", "ga4gh-demo-data", "/phenopackets/Cao-2018-TGFBR2-Patient_4.json"); + +INSERT INTO drs_object_visa VALUES + ("e26f8428-1a5d-4465-b389-10998ba7b894", 0), + ("996ead07-8dfc-4c5d-b319-5b59bebb507e", 0), + ("81e8b499-1054-4846-8df4-d079ea32a44f", 0), + ("672d0425-137c-437f-ab12-29a3cce1ffc0", 0); \ No newline at end of file diff --git a/database/sqlite/create-tables.sql b/database/sqlite/create-tables.sql index 00f4d13..ac7a31b 100644 --- a/database/sqlite/create-tables.sql +++ b/database/sqlite/create-tables.sql @@ -48,3 +48,24 @@ CREATE TABLE drs_object_bundle ( FOREIGN KEY(parent_id) REFERENCES drs_object(id), FOREIGN KEY(child_id) REFERENCES drs_object(id) ); + +CREATE TABLE passport_broker ( + url TEXT PRIMARY KEY, + secret TEXT +); + +CREATE TABLE passport_visa ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + issuer TEXT, + secret TEXT, + passport_broker_url TEXT, + FOREIGN KEY (passport_broker_url) REFERENCES passport_broker(url) +); + +CREATE TABLE drs_object_visa ( + drs_object_id TEXT, + visa_id TEXT, + FOREIGN KEY(drs_object_id) REFERENCES drs_object(id), + FOREIGN KEY(visa_id) REFERENCES passport_visa(id) +); diff --git a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java index 19c273e..b9ba888 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java +++ b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java @@ -22,6 +22,8 @@ import org.ga4gh.starterkit.drs.model.DrsObject; import org.ga4gh.starterkit.drs.model.DrsServiceInfo; import org.ga4gh.starterkit.drs.model.FileAccessObject; +import org.ga4gh.starterkit.drs.model.PassportBroker; +import org.ga4gh.starterkit.drs.model.PassportVisa; import org.ga4gh.starterkit.drs.utils.cache.AccessCache; import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; @@ -232,6 +234,8 @@ public List>> getAnnotat annotatedClasses.add(Checksum.class); annotatedClasses.add(FileAccessObject.class); annotatedClasses.add(AwsS3AccessObject.class); + annotatedClasses.add(PassportBroker.class); + annotatedClasses.add(PassportVisa.class); return annotatedClasses; } diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java index 9f5a34c..dcc00b1 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java @@ -14,6 +14,7 @@ import org.ga4gh.starterkit.drs.model.BulkResponse; import org.ga4gh.starterkit.drs.model.DrsObject; import org.ga4gh.starterkit.drs.utils.SerializeView; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMap; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -64,7 +65,7 @@ public DrsObject getObjectById( @RequestParam(name = "expand", required = false) boolean expand ) { loggingUtil.debug("Public API request: DrsObject with id '" + objectId + "', expand=" + expand); - return objectRequestHandler.prepare(objectId, expand).handleRequest(); + return objectRequestHandler.prepare(objectId, expand, null).handleRequest(); } /** @@ -87,6 +88,12 @@ public AccessURL getAccessURLById( public BulkResponse getBulkObjects( @RequestBody BulkRequest bulkRequest ) { + // parse user passport + UserPassportMap userPassportMap = null; + if (bulkRequest.getPassports() != null) { + userPassportMap = new UserPassportMap(bulkRequest.getPassports()); + } + BulkResponse bulkResponse = new BulkResponse(); int requested = 0; int resolved = 0; @@ -96,7 +103,7 @@ public BulkResponse getBulkObjects( requested++; try { ObjectRequestHandler handler = context.getBean(ObjectRequestHandler.class); - DrsObject drsObject = handler.prepare(drsObjectId, false).handleRequest(); + DrsObject drsObject = handler.prepare(drsObjectId, false, userPassportMap).handleRequest(); bulkResponse.getResolvedDrsObject().add(drsObject); resolved++; } catch (CustomException ex) { diff --git a/src/main/java/org/ga4gh/starterkit/drs/exception/ForbiddenException.java b/src/main/java/org/ga4gh/starterkit/drs/exception/ForbiddenException.java new file mode 100644 index 0000000..ab6185b --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/exception/ForbiddenException.java @@ -0,0 +1,23 @@ +package org.ga4gh.starterkit.drs.exception; + +import org.ga4gh.starterkit.common.exception.CustomException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.FORBIDDEN) +public class ForbiddenException extends CustomException { + + private static final long serialVersionUID = 1L; + + public ForbiddenException() { + super(); + } + + public ForbiddenException(String message) { + super(message); + } + + public ForbiddenException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/exception/UnauthorizedException.java b/src/main/java/org/ga4gh/starterkit/drs/exception/UnauthorizedException.java new file mode 100644 index 0000000..6c62430 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/exception/UnauthorizedException.java @@ -0,0 +1,23 @@ +package org.ga4gh.starterkit.drs.exception; + +import org.ga4gh.starterkit.common.exception.CustomException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.UNAUTHORIZED) +public class UnauthorizedException extends CustomException { + + private static final long serialVersionUID = 1L; + + public UnauthorizedException() { + super(); + } + + public UnauthorizedException(String message) { + super(message); + } + + public UnauthorizedException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java index 0fc61c8..1fadca5 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkRequest.java @@ -18,8 +18,10 @@ @JsonView(SerializeView.Public.class) public class BulkRequest { private List selection; + private List passports; public BulkRequest() { selection = new ArrayList<>(); + passports = new ArrayList<>(); } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java b/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java index 252759b..3cb72eb 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java +++ b/src/main/java/org/ga4gh/starterkit/drs/model/DrsObject.java @@ -17,6 +17,10 @@ import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.springframework.lang.NonNull; + +import lombok.Getter; +import lombok.Setter; + import java.net.URI; import java.time.LocalDateTime; import java.util.ArrayList; @@ -43,6 +47,8 @@ */ @Entity @Table(name = "drs_object") +@Setter +@Getter @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class DrsObject implements HibernateEntity { @@ -176,6 +182,15 @@ public class DrsObject implements HibernateEntity { @JsonManagedReference private List awsS3AccessObjects; + @ManyToMany + @JoinTable( + name = "drs_object_visa", + joinColumns = {@JoinColumn(name = "drs_object_id")}, + inverseJoinColumns = {@JoinColumn(name = "visa_id")} + ) + @JsonView(SerializeView.Admin.class) + private List passportVisas; + /* Transient attributes produced from transforming database records. They are needed to conform to the DRS spec: selfURI, accessMethods, contents @@ -211,25 +226,7 @@ public DrsObject() { checksums = new ArrayList<>(); fileAccessObjects = new ArrayList<>(); awsS3AccessObjects = new ArrayList<>(); - } - - /* Constructors */ - - /** - * Instantiates a new DrsObject with preconfigured attributes - * @param id DRSObject identifier - * @param selfURI self URI - * @param checksums checksum object list - * @param createdTime timestamp of DRSObject creation - * @param size size of file in bytes - */ - public DrsObject(String id, URI selfURI, List checksums, LocalDateTime createdTime, Long size) { - super(); - this.setId(id); - this.setSelfURI(selfURI); - this.setChecksums(checksums); - this.setCreatedTime(createdTime); - this.setSize(size); + passportVisas = new ArrayList<>(); } /* Custom API methods */ @@ -244,287 +241,6 @@ public void loadRelations() { Hibernate.initialize(getDrsObjectParents()); Hibernate.initialize(getFileAccessObjects()); Hibernate.initialize(getAwsS3AccessObjects()); - } - - /* Setters and Getters */ - - /** - * Assign id - * @param id DRSObject identifier - */ - public void setId(String id) { - this.id = id; - } - - /** - * Retrieve id - * @return DRSObject identifier - */ - public String getId() { - return id; - } - - /** - * Assign description - * @param description DRSObject description - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Retrieve description - * @return DRSObject description - */ - public String getDescription() { - return description; - } - - /** - * Assign created time - * @param createdTime timestamp of object creation time - */ - public void setCreatedTime(LocalDateTime createdTime) { - this.createdTime = createdTime; - } - - /** - * Retrieve created time - * @return timestamp of object creation time - */ - public LocalDateTime getCreatedTime() { - return createdTime; - } - - /** - * Assign mimeType - * @param mimeType valid media type - */ - public void setMimeType(String mimeType) { - this.mimeType = mimeType; - } - - /** - * Retrieve mimeType - * @return valid media type - */ - public String getMimeType() { - return mimeType; - } - - /** - * Assign name - * @param name DRSObject name - */ - public void setName(String name) { - this.name = name; - } - - /** - * Retrieve name - * @return DRSObject name - */ - public String getName() { - return name; - } - - /** - * Assign size - * @param size DRSObject size - */ - public void setSize(Long size) { - this.size = size; - } - - /** - * Retrieve size - * @return DRSObject size - */ - public Long getSize() { - return size; - } - - /** - * Assign updatedTime - * @param updatedTime timestamp of object last updated time - */ - public void setUpdatedTime(LocalDateTime updatedTime) { - this.updatedTime = updatedTime; - } - - /** - * Retrieve updatedTime - * @return timestamp of object last updated time - */ - public LocalDateTime getUpdatedTime() { - return updatedTime; - } - - /** - * Assign version - * @param version DRSObject version - */ - public void setVersion(String version) { - this.version = version; - } - - /** - * Retrieve version - * @return DRSObject version - */ - public String getVersion() { - return version; - } - - /** - * Assign aliases - * @param aliases DRSObject aliases - */ - public void setAliases(List aliases) { - this.aliases = aliases; - } - - /** - * Retrieve aliases - * @return DRSObject aliases - */ - public List getAliases() { - return aliases; - } - - /** - * Assign checksums - * @param checksums checksum list - */ - public void setChecksums(List checksums) { - this.checksums = checksums; - } - - /** - * Retrieve checksums - * @return checksum list - */ - public List getChecksums() { - return checksums; - } - - public void setIsBundle(Boolean isBundle) { - this.isBundle = isBundle; - } - - public Boolean getIsBundle() { - return isBundle; - } - - /** - * Assign drsObjectChildren - * @param drsObjectChildren list of child DRS Objects - */ - public void setDrsObjectChildren(List drsObjectChildren) { - this.drsObjectChildren = drsObjectChildren; - } - - /** - * Retrieve drsObjectChildren - * @return list of child DRS Objects - */ - public List getDrsObjectChildren() { - return drsObjectChildren; - } - - /** - * Assign drsObjectParents - * @param drsObjectParents list of parent DRS Objects - */ - public void setDrsObjectParents(List drsObjectParents) { - this.drsObjectParents = drsObjectParents; - } - - /** - * Retrieve drsObjectParents - * @return list of parent DRS Objects - */ - public List getDrsObjectParents() { - return drsObjectParents; - } - - /** - * Assign fileAccessObjects - * @param fileAccessObjects list of file-based access objects - */ - public void setFileAccessObjects(List fileAccessObjects) { - this.fileAccessObjects = fileAccessObjects; - } - - /** - * Retrieve fileAccessObjects - * @return list of file-based access objects - */ - public List getFileAccessObjects() { - return fileAccessObjects; - } - - /** - * Assign awsS3AccessObjects - * @param awsS3AccessObjects list of s3-based access objects - */ - public void setAwsS3AccessObjects(List awsS3AccessObjects) { - this.awsS3AccessObjects = awsS3AccessObjects; - } - - /** - * Retrieve awsS3AccessObjects - * @return list of s3-based access objects - */ - public List getAwsS3AccessObjects() { - return awsS3AccessObjects; - } - - /** - * Assign selfURI - * @param selfURI self-referencing DRS URI - */ - public void setSelfURI(URI selfURI) { - this.selfURI = selfURI; - } - - /** - * Retrieve selfURI - * @return self-referencing DRS URI - */ - public URI getSelfURI() { - return selfURI; - } - - /** - * Assign access methods - * @param accessMethods list of all valid access methods - */ - public void setAccessMethods(List accessMethods) { - this.accessMethods = accessMethods; - } - - /** - * Retrieve access methods - * @return list of all valid access methods - */ - public List getAccessMethods() { - return accessMethods; - } - - /** - * Assign contents - * @param contents list of contents objects representing sub/child DRS Objects - */ - public void setContents(List contents) { - this.contents = contents; - } - - /** - * Retrieve contents - * @return list of contents objects representing child DRS Objects - */ - public List getContents() { - return contents; + Hibernate.initialize(getPassportVisas()); } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/PassportBroker.java b/src/main/java/org/ga4gh/starterkit/drs/model/PassportBroker.java new file mode 100644 index 0000000..dd942f9 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/PassportBroker.java @@ -0,0 +1,68 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.common.hibernate.HibernateEntity; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import org.hibernate.Hibernate; +import org.springframework.lang.NonNull; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "passport_broker") +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class PassportBroker implements HibernateEntity { + + @Id + @Column(name = "url", updatable = false, nullable = false) + @NonNull + @JsonView(SerializeView.Admin.class) + private String url; + + @Column(name = "secret") + @JsonView(SerializeView.Admin.class) + private String secret; + + @OneToMany( + mappedBy = "passportBroker", + fetch = FetchType.LAZY, + cascade = {CascadeType.ALL}, + orphanRemoval = true + ) + @JsonView(SerializeView.Admin.class) + private List passportVisas; + + public PassportBroker() { + passportVisas = new ArrayList<>(); + } + + public void setId(String url) { + this.url = url; + } + + public String getId() { + return url; + } + + public void loadRelations() { + Hibernate.initialize(getPassportVisas()); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/PassportVisa.java b/src/main/java/org/ga4gh/starterkit/drs/model/PassportVisa.java new file mode 100644 index 0000000..893c36c --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/PassportVisa.java @@ -0,0 +1,80 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.common.hibernate.HibernateEntity; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import org.hibernate.Hibernate; + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; + +@Entity +@Table(name = "passport_visa") +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class PassportVisa implements HibernateEntity { + + @Id + @Column(name = "id", updatable = false, nullable = false) + @NonNull + @JsonView(SerializeView.Admin.class) + private Integer id; + + @Column(name = "name") + @JsonView(SerializeView.Admin.class) + private String name; + + @Column(name = "issuer") + @JsonView(SerializeView.Admin.class) + private String issuer; + + @Column(name = "secret") + @JsonView(SerializeView.Admin.class) + private String secret; + + @ManyToOne( + fetch = FetchType.EAGER, + cascade = {CascadeType.PERSIST, CascadeType.MERGE, + CascadeType.DETACH, CascadeType.REFRESH} + ) + @JoinColumn(name = "passport_broker_url") + private PassportBroker passportBroker; + + @ManyToMany + @JoinTable( + name = "drs_object_visa", + joinColumns = {@JoinColumn(name = "visa_id")}, + inverseJoinColumns = {@JoinColumn(name = "drs_object_id")} + ) + @JsonView(SerializeView.Admin.class) + private List drsObjects; + + public PassportVisa() { + drsObjects = new ArrayList<>(); + } + + public void loadRelations() { + Hibernate.initialize(getDrsObjects()); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassport.java b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassport.java new file mode 100644 index 0000000..167025a --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassport.java @@ -0,0 +1,19 @@ +package org.ga4gh.starterkit.drs.utils.passport; + +import java.util.HashMap; +import java.util.Map; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class UserPassport { + + private String passportJwt; + private Map visaJwtMap; + + public UserPassport() { + visaJwtMap = new HashMap<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java new file mode 100644 index 0000000..d233d0f --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java @@ -0,0 +1,49 @@ +package org.ga4gh.starterkit.drs.utils.passport; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class UserPassportMap { + + private Map map; + + public UserPassportMap(List passports) { + map = new HashMap<>(); + System.out.println("***"); + System.out.println("DECODING THE USER'S JWT"); + + for (String rawPassportJwt : passports) { + UserPassport userPassport = new UserPassport(); + userPassport.setPassportJwt(rawPassportJwt); + + DecodedJWT decodedPassportJwt = JWT.decode(rawPassportJwt); + String passportIss = decodedPassportJwt.getClaim("iss").asString(); + + System.out.println("what is the passport-level iss?"); + System.out.println(passportIss); + + String[] rawVisaJwts = decodedPassportJwt.getClaim("ga4gh_passport_v1").asArray(String.class); + for (String rawVisaJwt : rawVisaJwts) { + DecodedJWT decodedVisaJwt = JWT.decode(rawVisaJwt); + String visaIss = decodedPassportJwt.getClaim("iss").asString(); + + System.out.println("-"); + System.out.println(rawVisaJwt); + + } + + + map.put(passportIss, userPassport); + } + System.out.println("***"); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java index 43d3b1e..c812991 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java @@ -8,6 +8,8 @@ import org.ga4gh.starterkit.common.exception.ResourceNotFoundException; import org.ga4gh.starterkit.common.requesthandler.RequestHandler; import org.ga4gh.starterkit.drs.config.DrsServiceProps; +import org.ga4gh.starterkit.drs.exception.ForbiddenException; +import org.ga4gh.starterkit.drs.exception.UnauthorizedException; import org.ga4gh.starterkit.drs.model.AccessMethod; import org.ga4gh.starterkit.drs.model.AccessType; import org.ga4gh.starterkit.drs.model.AccessURL; @@ -15,9 +17,11 @@ import org.ga4gh.starterkit.drs.model.ContentsObject; import org.ga4gh.starterkit.drs.model.DrsObject; import org.ga4gh.starterkit.drs.model.FileAccessObject; +import org.ga4gh.starterkit.drs.model.PassportVisa; import org.ga4gh.starterkit.drs.utils.cache.AccessCache; import org.ga4gh.starterkit.drs.utils.cache.AccessCacheItem; import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMap; import org.springframework.beans.factory.annotation.Autowired; /** @@ -42,6 +46,8 @@ public class ObjectRequestHandler implements RequestHandler { private boolean expand; + private UserPassportMap userPassportMap; + /* Constructors */ /** @@ -57,9 +63,10 @@ public ObjectRequestHandler() { * @param expand boolean indicating whether to return nested/recursive bundles under 'contents' * @return the prepared request handler */ - public ObjectRequestHandler prepare(String objectId, boolean expand) { + public ObjectRequestHandler prepare(String objectId, boolean expand, UserPassportMap userPassportMap) { this.objectId = objectId; this.expand = expand; + this.userPassportMap = userPassportMap; return this; } @@ -67,13 +74,35 @@ public ObjectRequestHandler prepare(String objectId, boolean expand) { * Obtains information about a DRSObject and formats it to the DRS specification */ public DrsObject handleRequest() { - // Get DrsObject from db DrsObject drsObject = hibernateUtil.loadDrsObject(objectId, true); if (drsObject == null) { throw new ResourceNotFoundException("No DrsObject found by id: " + objectId); } + // check if DrsObject requires auth, if so verify the client's passport + List requiredVisas = drsObject.getPassportVisas(); + boolean requiresAuth = false; + if (requiredVisas != null && requiredVisas.size() > 0) { + requiresAuth = true; + } + boolean noPassport = userPassportMap == null || userPassportMap.getMap().size() == 0; + if (requiresAuth) { + if (noPassport) { + throw new UnauthorizedException("Request for controlled data is missing user passport(s)"); + } + + // need to verify at least 1 visa registered with the DRS Object + boolean matchingVisaFound = false; + for (PassportVisa drsObjectRegisteredVisa : drsObject.getPassportVisas()) { + + } + + if (! matchingVisaFound) { + throw new ForbiddenException("No suitable visa found in user passport(s) for requested DRS object"); + } + } + // post query prep of response drsObject.setSelfURI(prepareSelfURI(objectId)); drsObject.setContents(prepareContents(drsObject)); diff --git a/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java b/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java index d9de82d..78a4af8 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/utils/BundleRecursiveChecksumCalculatorTest.java @@ -94,12 +94,7 @@ public Object[][] getData() { @Test(dataProvider = "cases") public void testRecursiveCalculateChecksums(DrsObject drsObject, Map expChecksumMap) { - System.out.println(drsObject); - System.out.println(expChecksumMap); - - Map checksumMap = BundleRecursiveChecksumCalculator.recursiveCalculateChecksums(drsObject); - System.out.println("DONE"); Assert.assertEquals(checksumMap.size(), expChecksumMap.size()); for (String key : expChecksumMap.keySet()) { From beef5e8edc0d31a03ba8fcd77d75724a7ca065a9 Mon Sep 17 00:00:00 2001 From: Jeremy Adams Date: Thu, 19 May 2022 10:27:23 -0400 Subject: [PATCH 4/7] handling of passports auth appears to be complete --- .../drs/app/DrsServerSpringConfig.java | 6 ++ .../starterkit/drs/controller/Objects.java | 22 ++++++- .../model/PostSingleObjectRequestBody.java | 26 +++++++++ .../drs/utils/hibernate/DrsHibernateUtil.java | 30 ++++++++++ .../drs/utils/passport/UserPassportMap.java | 26 +++------ .../passport/UserPassportMapVerifier.java | 58 +++++++++++++++++++ .../requesthandler/ObjectRequestHandler.java | 13 ++++- 7 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/PostSingleObjectRequestBody.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMapVerifier.java diff --git a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java index b9ba888..33d3fc3 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java +++ b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java @@ -26,6 +26,7 @@ import org.ga4gh.starterkit.drs.model.PassportVisa; import org.ga4gh.starterkit.drs.utils.cache.AccessCache; import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMapVerifier; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.FileStreamRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; @@ -303,4 +304,9 @@ public FileStreamRequestHandler fileStreamRequestHandler() { public AccessCache accessCache() { return new AccessCache(); } + + @Bean + public UserPassportMapVerifier userPAssportMapVerifier() { + return new UserPassportMapVerifier(); + } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java index dcc00b1..019dc43 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; - import org.ga4gh.starterkit.common.exception.CustomException; import org.ga4gh.starterkit.common.util.logging.LoggingUtil; import org.ga4gh.starterkit.drs.model.AccessURL; @@ -13,8 +12,10 @@ import org.ga4gh.starterkit.drs.model.BulkRequest; import org.ga4gh.starterkit.drs.model.BulkResponse; import org.ga4gh.starterkit.drs.model.DrsObject; +import org.ga4gh.starterkit.drs.model.PostSingleObjectRequestBody; import org.ga4gh.starterkit.drs.utils.SerializeView; import org.ga4gh.starterkit.drs.utils.passport.UserPassportMap; +import org.ga4gh.starterkit.drs.utils.passport.UserPassportMapVerifier; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -28,7 +29,6 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; - import javax.annotation.Resource; import com.fasterxml.jackson.annotation.JsonView; @@ -48,6 +48,9 @@ public class Objects implements ApplicationContextAware { @Autowired private LoggingUtil loggingUtil; + @Autowired + private UserPassportMapVerifier passportVerifier; + private ApplicationContext context; // Standard endpoints @@ -68,6 +71,20 @@ public DrsObject getObjectById( return objectRequestHandler.prepare(objectId, expand, null).handleRequest(); } + @PostMapping(path = "/{object_id:.+}") + @JsonView(SerializeView.Public.class) + public DrsObject getObjectByIdViaPost( + @PathVariable(name = "object_id") String objectId, + @RequestBody PostSingleObjectRequestBody requestBody + ) { + UserPassportMap userPassportMap = null; + if (requestBody.getPassports() != null) { + userPassportMap = new UserPassportMap(requestBody.getPassports()); + passportVerifier.verifyAll(userPassportMap); + } + return objectRequestHandler.prepare(objectId, requestBody.isExpand(), userPassportMap).handleRequest(); + } + /** * Get an access URL for fetching the DRS Object's file bytes * @param objectId DRSObject identifier @@ -92,6 +109,7 @@ public BulkResponse getBulkObjects( UserPassportMap userPassportMap = null; if (bulkRequest.getPassports() != null) { userPassportMap = new UserPassportMap(bulkRequest.getPassports()); + passportVerifier.verifyAll(userPassportMap); } BulkResponse bulkResponse = new BulkResponse(); diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/PostSingleObjectRequestBody.java b/src/main/java/org/ga4gh/starterkit/drs/model/PostSingleObjectRequestBody.java new file mode 100644 index 0000000..4e3ce09 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/PostSingleObjectRequestBody.java @@ -0,0 +1,26 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class PostSingleObjectRequestBody { + private boolean expand; + private List passports; + + public PostSingleObjectRequestBody() { + expand = false; + passports = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java b/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java index 4b6e458..067d8b7 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/hibernate/DrsHibernateUtil.java @@ -5,12 +5,17 @@ import javax.persistence.PersistenceException; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + import org.ga4gh.starterkit.drs.model.DrsObject; +import org.ga4gh.starterkit.drs.model.PassportVisa; import org.ga4gh.starterkit.drs.utils.BundleRecursiveChecksumCalculator; import org.ga4gh.starterkit.common.hibernate.HibernateEntity; import org.ga4gh.starterkit.common.hibernate.HibernateUtil; import org.hibernate.HibernateException; import org.hibernate.Session; +import org.hibernate.query.Query; /** * Provides access to DRS entities/tables in the database, enabling access, creation, @@ -108,4 +113,29 @@ public > List getEntityList } return entities; } + + public PassportVisa findPassportVisa(String visaName, String visaIssuer) { + Session session = newTransaction(); + PassportVisa visa = null; + try { + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(PassportVisa.class); + Root root = cq.from(PassportVisa.class); + Predicate[] predicates = new Predicate[2]; + predicates[0] = cb.equal(root.get("name"), visaName); + predicates[1] = cb.equal(root.get("issuer"), visaIssuer); + cq.select(root).where(predicates); + Query query = session.createQuery(cq); + List results = query.getResultList(); + if (results.size() != 1) { + throw new Exception("no unique visa found"); + } + visa = results.get(0); + } catch (Exception ex) { + + } finally { + endTransaction(session); + } + return visa; + } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java index d233d0f..e6b6d28 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMap.java @@ -3,10 +3,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import com.auth0.jwt.JWT; import com.auth0.jwt.interfaces.DecodedJWT; - import lombok.Getter; import lombok.Setter; @@ -18,8 +16,6 @@ public class UserPassportMap { public UserPassportMap(List passports) { map = new HashMap<>(); - System.out.println("***"); - System.out.println("DECODING THE USER'S JWT"); for (String rawPassportJwt : passports) { UserPassport userPassport = new UserPassport(); @@ -28,22 +24,18 @@ public UserPassportMap(List passports) { DecodedJWT decodedPassportJwt = JWT.decode(rawPassportJwt); String passportIss = decodedPassportJwt.getClaim("iss").asString(); - System.out.println("what is the passport-level iss?"); - System.out.println(passportIss); - + String[] containedVisas = decodedPassportJwt.getClaim("contained_visas").asArray(String.class); String[] rawVisaJwts = decodedPassportJwt.getClaim("ga4gh_passport_v1").asArray(String.class); - for (String rawVisaJwt : rawVisaJwts) { - DecodedJWT decodedVisaJwt = JWT.decode(rawVisaJwt); - String visaIss = decodedPassportJwt.getClaim("iss").asString(); - - System.out.println("-"); - System.out.println(rawVisaJwt); - + for (int i = 0; i < containedVisas.length; i++) { + String containedVisa = containedVisas[i]; + String rawVisaJwt = rawVisaJwts[i]; + userPassport.getVisaJwtMap().put(containedVisa, rawVisaJwt); } - - map.put(passportIss, userPassport); } - System.out.println("***"); + } + + public void verifyAllJwts() { + } } diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMapVerifier.java b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMapVerifier.java new file mode 100644 index 0000000..ae70214 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/passport/UserPassportMapVerifier.java @@ -0,0 +1,58 @@ +package org.ga4gh.starterkit.drs.utils.passport; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; + +import org.ga4gh.starterkit.common.exception.BadRequestException; +import org.ga4gh.starterkit.drs.exception.UnauthorizedException; +import org.ga4gh.starterkit.drs.model.PassportBroker; +import org.ga4gh.starterkit.drs.model.PassportVisa; +import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.springframework.beans.factory.annotation.Autowired; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class UserPassportMapVerifier { + + @Autowired + private DrsHibernateUtil hibernateUtil; + + public void verifyAll(UserPassportMap userPassportMap) { + try { + for (String passportIssKey : userPassportMap.getMap().keySet()) { + // verify the passport-level JWT + // first find the secret from the DB + // then use it to verify + UserPassport userPassport = userPassportMap.getMap().get(passportIssKey); + String rawPassportJwt = userPassport.getPassportJwt(); + PassportBroker passportBroker = hibernateUtil.readEntityObject(PassportBroker.class, passportIssKey, false); + if (passportBroker == null) { + throw new Exception("Passports from issuer: '" + passportIssKey + "' are not accepted here."); + } + String passportBrokerSecret = passportBroker.getSecret(); + JWTVerifier passportVerifier = JWT.require(Algorithm.HMAC256(passportBrokerSecret)).build(); + passportVerifier.verify(rawPassportJwt); + + for (String visaKey : userPassport.getVisaJwtMap().keySet()) { + String rawVisaJwt = userPassport.getVisaJwtMap().get(visaKey); + String visaName = visaKey.split("@")[0]; + String visaIssuer = visaKey.split("@")[1]; + PassportVisa registeredVisa = hibernateUtil.findPassportVisa(visaName, visaIssuer); + if (registeredVisa == null) { + throw new Exception("The Visa you provided: '" + visaKey + "' is not accepted here."); + } + String visaSecret = registeredVisa.getSecret(); + JWTVerifier visaVerifier = JWT.require(Algorithm.HMAC256(visaSecret)).build(); + visaVerifier.verify(rawVisaJwt); + } + } + } catch (Exception ex) { + String message = "Invalid Passport(s), message: " + ex.getMessage(); + throw new UnauthorizedException(message); + } + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java index c812991..b411d55 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/ObjectRequestHandler.java @@ -21,6 +21,7 @@ import org.ga4gh.starterkit.drs.utils.cache.AccessCache; import org.ga4gh.starterkit.drs.utils.cache.AccessCacheItem; import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.ga4gh.starterkit.drs.utils.passport.UserPassport; import org.ga4gh.starterkit.drs.utils.passport.UserPassportMap; import org.springframework.beans.factory.annotation.Autowired; @@ -95,7 +96,17 @@ public DrsObject handleRequest() { // need to verify at least 1 visa registered with the DRS Object boolean matchingVisaFound = false; for (PassportVisa drsObjectRegisteredVisa : drsObject.getPassportVisas()) { - + String passportBrokerIss = drsObjectRegisteredVisa.getPassportBroker().getUrl(); + String visaName = drsObjectRegisteredVisa.getName(); + String visaIssuer = drsObjectRegisteredVisa.getIssuer(); + UserPassport userPassport = userPassportMap.getMap().get(passportBrokerIss); + if (userPassport != null) { + String visaKey = visaName + "@" + visaIssuer; + String visaJwt = userPassport.getVisaJwtMap().get(visaKey); + if (visaJwt != null) { + matchingVisaFound = true; + } + } } if (! matchingVisaFound) { From 796e4da6187ccc8e6417ac3315ec629d9fab4cfb Mon Sep 17 00:00:00 2001 From: Jeremy Adams Date: Thu, 19 May 2022 10:39:23 -0400 Subject: [PATCH 5/7] aim to restore testing --- .../drs/model/AwsS3AccessObjectTest.java | 10 +++++++-- .../starterkit/drs/model/ChecksumTest.java | 15 ++++++++++--- .../starterkit/drs/model/DrsObjectTest.java | 21 +++++++------------ .../drs/model/FileAccessObjectTest.java | 15 ++++++++++--- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java index e595934..3c602af 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/AwsS3AccessObjectTest.java @@ -14,7 +14,10 @@ public Object[][] getData() { "us-east-1", "ga4gh-demo-data", "/path/to/the/file.bam", - new DrsObject("12345", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("12345"); + setSize(Long.valueOf(1L)); + }}, "12345" }, { @@ -22,7 +25,10 @@ public Object[][] getData() { "us-east-2", "ga4gh-production-data", "/data/phenopackets/0001.json", - new DrsObject("98765", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("98765"); + setSize(Long.valueOf(1L)); + }}, "98765" }, }; diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java index 4814649..b8dbbe4 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/ChecksumTest.java @@ -13,21 +13,30 @@ public Object[][] getData() { Long.valueOf(1L), "47a43e2f1981cef23dee95dd85fb8233", "md5", - new DrsObject("12345", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("12345"); + setSize(Long.valueOf(1L)); + }}, "12345" }, { Long.valueOf(200L), "d3882df45306b4cc6f6e9227b54a6026", "trunc512", - new DrsObject("98765", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("98765"); + setSize(Long.valueOf(1L)); + }}, "98765" }, { Long.valueOf(4000000L), "92f9760ae68487f8959d6e0e6a5f219e", "crc32c", - new DrsObject("00001", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("00001"); + setSize(Long.valueOf(1L)); + }}, "00001" } }; diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java index 7c120ae..8d5da51 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/DrsObjectTest.java @@ -80,6 +80,11 @@ public Object[][] getData() throws URISyntaxException { } private void setters(DrsObject drsObject, TestCase testCase) { + drsObject.setId(testCase.id); + drsObject.setSelfURI(testCase.selfURI); + drsObject.setChecksums(testCase.checksums); + drsObject.setCreatedTime(testCase.createdTime); + drsObject.setSize(testCase.size); drsObject.setAccessMethods(testCase.accessMethods); drsObject.setAliases(testCase.aliases); drsObject.setContents(testCase.contents); @@ -107,20 +112,8 @@ private void assertions(DrsObject drsObject, TestCase testCase) { } @Test(dataProvider = "cases") - public void testDrsObjectConstructor(TestCase testCase) { - DrsObject drsObject = new DrsObject(testCase.id, testCase.selfURI, testCase.checksums, testCase.createdTime, testCase.size); - setters(drsObject, testCase); - assertions(drsObject, testCase); - } - - @Test(dataProvider = "cases") - public void testDrsObjectSetters(TestCase testCase) { - DrsObject drsObject = new DrsObject(null, null, null, null, Long.valueOf(0)); - drsObject.setId(testCase.id); - drsObject.setSelfURI(testCase.selfURI); - drsObject.setChecksums(testCase.checksums); - drsObject.setCreatedTime(testCase.createdTime); - drsObject.setSize(testCase.size); + public void testDrsObject(TestCase testCase) { + DrsObject drsObject = new DrsObject(); setters(drsObject, testCase); assertions(drsObject, testCase); } diff --git a/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java b/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java index 878f856..00ed445 100644 --- a/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java +++ b/src/test/java/org/ga4gh/starterkit/drs/model/FileAccessObjectTest.java @@ -12,19 +12,28 @@ public Object[][] getData() { { Long.valueOf(1L), "/path/to/the/file.bam", - new DrsObject("12345", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("12345"); + setSize(Long.valueOf(1L)); + }}, "12345" }, { Long.valueOf(200L), "/data/phenopackets/0001.json", - new DrsObject("98765", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("98765"); + setSize(Long.valueOf(1L)); + }}, "98765" }, { Long.valueOf(4000000L), "./data/cram/myfile.cram", - new DrsObject("00001", null, null, null, Long.valueOf(1L)), + new DrsObject() {{ + setId("00001"); + setSize(Long.valueOf(1L)); + }}, "00001" } }; From 4d1d56dd9a637c09d00b55cad1d5d35703d7c6a5 Mon Sep 17 00:00:00 2001 From: Jeremy Adams Date: Thu, 19 May 2022 12:45:10 -0400 Subject: [PATCH 6/7] 2 options endpoints (single object and bulk) for getting auth info --- .../drs/app/DrsServerSpringConfig.java | 7 +++ .../starterkit/drs/controller/Objects.java | 45 ++++++++++++++ .../ga4gh/starterkit/drs/model/AuthInfo.java | 33 +++++++++++ .../starterkit/drs/model/AuthIssuer.java | 27 +++++++++ .../ga4gh/starterkit/drs/model/AuthType.java | 8 +++ .../drs/model/BulkAuthInfoRequest.java | 24 ++++++++ .../drs/model/BulkAuthInfoResponse.java | 29 ++++++++++ .../AuthInfoRequestHandler.java | 58 +++++++++++++++++++ .../responses/admin/index/success-00.json | 2 +- 9 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/AuthInfo.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/AuthIssuer.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/AuthType.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoRequest.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoResponse.java create mode 100644 src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/AuthInfoRequestHandler.java diff --git a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java index 33d3fc3..bcc7bc8 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java +++ b/src/main/java/org/ga4gh/starterkit/drs/app/DrsServerSpringConfig.java @@ -28,6 +28,7 @@ import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; import org.ga4gh.starterkit.drs.utils.passport.UserPassportMapVerifier; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; +import org.ga4gh.starterkit.drs.utils.requesthandler.AuthInfoRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.FileStreamRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -282,6 +283,12 @@ public AccessRequestHandler accessRequestHandler() { return new AccessRequestHandler(); } + @Bean + @RequestScope + public AuthInfoRequestHandler authInfoRequestHandler() { + return new AuthInfoRequestHandler(); + } + /** * Get new request handler facilitating streaming of a local file over http(s) * @return streaming request handler diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java index 019dc43..96e8a59 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/Objects.java @@ -7,7 +7,10 @@ import org.ga4gh.starterkit.common.exception.CustomException; import org.ga4gh.starterkit.common.util.logging.LoggingUtil; import org.ga4gh.starterkit.drs.model.AccessURL; +import org.ga4gh.starterkit.drs.model.AuthInfo; import org.ga4gh.starterkit.drs.model.BulkAccessRequest; +import org.ga4gh.starterkit.drs.model.BulkAuthInfoRequest; +import org.ga4gh.starterkit.drs.model.BulkAuthInfoResponse; import org.ga4gh.starterkit.drs.model.BulkObjectAccessId; import org.ga4gh.starterkit.drs.model.BulkRequest; import org.ga4gh.starterkit.drs.model.BulkResponse; @@ -17,6 +20,7 @@ import org.ga4gh.starterkit.drs.utils.passport.UserPassportMap; import org.ga4gh.starterkit.drs.utils.passport.UserPassportMapVerifier; import org.ga4gh.starterkit.drs.utils.requesthandler.AccessRequestHandler; +import org.ga4gh.starterkit.drs.utils.requesthandler.AuthInfoRequestHandler; import org.ga4gh.starterkit.drs.utils.requesthandler.ObjectRequestHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -26,6 +30,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; @@ -45,6 +50,9 @@ public class Objects implements ApplicationContextAware { @Resource(name = "accessRequestHandler") private AccessRequestHandler accessRequestHandler; + @Resource(name = "authInfoRequestHandler") + private AuthInfoRequestHandler authInfoRequestHandler; + @Autowired private LoggingUtil loggingUtil; @@ -85,6 +93,14 @@ public DrsObject getObjectByIdViaPost( return objectRequestHandler.prepare(objectId, requestBody.isExpand(), userPassportMap).handleRequest(); } + @RequestMapping(value = "/{object_id:.+}", method = RequestMethod.OPTIONS) + @JsonView(SerializeView.Public.class) + public AuthInfo singleObjectAuthInfo( + @PathVariable(name = "object_id") String objectId + ) { + return authInfoRequestHandler.prepare(objectId).handleRequest(); + } + /** * Get an access URL for fetching the DRS Object's file bytes * @param objectId DRSObject identifier @@ -137,6 +153,35 @@ public BulkResponse getBulkObjects( return bulkResponse; } + @RequestMapping(method = RequestMethod.OPTIONS) + @JsonView(SerializeView.Public.class) + public BulkAuthInfoResponse getBulkAuthInfo( + @RequestBody BulkAuthInfoRequest request + ) { + BulkAuthInfoResponse response = new BulkAuthInfoResponse(); + int requested = 0; + int resolved = 0; + int unresolved = 0; + + for (String drsObjectId : request.getSelection()) { + requested++; + try { + AuthInfoRequestHandler handler = context.getBean(AuthInfoRequestHandler.class); + AuthInfo authInfo = handler.prepare(drsObjectId).handleRequest(); + response.getResolvedDrsObjectAuthInfo().put(drsObjectId, authInfo); + resolved++; + } catch (CustomException ex) { + int httpStatus = ex.getClass().getAnnotation(ResponseStatus.class).value().value(); + response.getUnresolvedDrsObjectAuthInfo().put(drsObjectId, httpStatus); + unresolved++; + } + } + response.getSummary().setRequested(requested); + response.getSummary().setResolved(resolved); + response.getSummary().setUnresolved(unresolved); + return response; + } + @PostMapping(path = "/access") @JsonView(SerializeView.Public.class) public List getBulkAccessURLs( diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/AuthInfo.java b/src/main/java/org/ga4gh/starterkit/drs/model/AuthInfo.java new file mode 100644 index 0000000..920bbe2 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/AuthInfo.java @@ -0,0 +1,33 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.drs.utils.SerializeView; + +import lombok.Getter; +import lombok.Setter; + +// Authorization information for a single DRS object +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AuthInfo { + + @JsonView(SerializeView.Always.class) + private List supportedTypes; + + @JsonView(SerializeView.Always.class) + private List passportAuthIssuers; + + public AuthInfo() { + supportedTypes = new ArrayList<>(); + passportAuthIssuers = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/AuthIssuer.java b/src/main/java/org/ga4gh/starterkit/drs/model/AuthIssuer.java new file mode 100644 index 0000000..139d452 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/AuthIssuer.java @@ -0,0 +1,27 @@ +package org.ga4gh.starterkit.drs.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import org.ga4gh.starterkit.drs.utils.SerializeView; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class AuthIssuer { + + @JsonView(SerializeView.Always.class) + private String brokerUrl; + + @JsonView(SerializeView.Always.class) + private String visaName; + + @JsonView(SerializeView.Always.class) + private String visaIssuer; +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/AuthType.java b/src/main/java/org/ga4gh/starterkit/drs/model/AuthType.java new file mode 100644 index 0000000..e8dcdae --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/AuthType.java @@ -0,0 +1,8 @@ +package org.ga4gh.starterkit.drs.model; + +public enum AuthType { + None, + BasicAuth, + BearerAuth, + PassportAuth +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoRequest.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoRequest.java new file mode 100644 index 0000000..f576ffa --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoRequest.java @@ -0,0 +1,24 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.ArrayList; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkAuthInfoRequest { + private List selection; + + public BulkAuthInfoRequest() { + selection = new ArrayList<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoResponse.java b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoResponse.java new file mode 100644 index 0000000..fcba244 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/model/BulkAuthInfoResponse.java @@ -0,0 +1,29 @@ +package org.ga4gh.starterkit.drs.model; + +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import org.ga4gh.starterkit.drs.utils.SerializeView; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@JsonView(SerializeView.Public.class) +public class BulkAuthInfoResponse { + + private BulkSummary summary; + private Map resolvedDrsObjectAuthInfo; + private Map unresolvedDrsObjectAuthInfo; + + public BulkAuthInfoResponse() { + summary = new BulkSummary(); + resolvedDrsObjectAuthInfo = new HashMap<>(); + unresolvedDrsObjectAuthInfo = new HashMap<>(); + } +} diff --git a/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/AuthInfoRequestHandler.java b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/AuthInfoRequestHandler.java new file mode 100644 index 0000000..867ffe8 --- /dev/null +++ b/src/main/java/org/ga4gh/starterkit/drs/utils/requesthandler/AuthInfoRequestHandler.java @@ -0,0 +1,58 @@ +package org.ga4gh.starterkit.drs.utils.requesthandler; + +import org.ga4gh.starterkit.common.exception.ResourceNotFoundException; +import org.ga4gh.starterkit.common.requesthandler.RequestHandler; +import org.ga4gh.starterkit.drs.model.AuthInfo; +import org.ga4gh.starterkit.drs.model.AuthIssuer; +import org.ga4gh.starterkit.drs.model.AuthType; +import org.ga4gh.starterkit.drs.model.DrsObject; +import org.ga4gh.starterkit.drs.model.PassportVisa; +import org.ga4gh.starterkit.drs.utils.hibernate.DrsHibernateUtil; +import org.springframework.beans.factory.annotation.Autowired; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +public class AuthInfoRequestHandler implements RequestHandler { + + @Autowired + private DrsHibernateUtil hibernateUtil; + + private String objectId; + + public AuthInfoRequestHandler prepare(String objectId) { + this.objectId = objectId; + return this; + } + + public AuthInfo handleRequest() { + AuthInfo authInfo = new AuthInfo(); + + DrsObject drsObject = hibernateUtil.loadDrsObject(objectId, false); + if (drsObject == null) { + throw new ResourceNotFoundException("No DrsObject found by id: " + objectId); + } + + if (drsObject.getPassportVisas().size() == 0) { + authInfo.getSupportedTypes().add(AuthType.None); + } else { + authInfo.getSupportedTypes().add(AuthType.PassportAuth); + + for (PassportVisa visa : drsObject.getPassportVisas()) { + AuthIssuer issuer = new AuthIssuer(); + issuer.setBrokerUrl(visa.getPassportBroker().getId()); + issuer.setVisaName(visa.getName()); + issuer.setVisaIssuer(visa.getIssuer()); + authInfo.getPassportAuthIssuers().add(issuer); + } + } + System.out.println("what is auth info??"); + System.out.println(authInfo.getSupportedTypes().size()); + System.out.println(authInfo.getSupportedTypes().get(0)); + return authInfo; + } +} diff --git a/src/test/resources/responses/admin/index/success-00.json b/src/test/resources/responses/admin/index/success-00.json index 932aba7..5c68993 100644 --- a/src/test/resources/responses/admin/index/success-00.json +++ b/src/test/resources/responses/admin/index/success-00.json @@ -1 +1 @@ -[{"id":"b8cd0667-2c33-4c9f-967b-161b905932c9","description":"Open dataset of 384 phenopackets","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.test.dataset","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a1dd4ae2-8d26-43b0-a199-342b64c7dff6","description":"Synthetic Phenopacket dataset: Cao family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.cao.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1a570e4e-2489-4218-9333-f65549495872","description":"Phenopackets, Cao family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.1","size":4257,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"4d83ba3f-a476-4c7c-868f-3d1fcf77fe29","description":"Phenopackets, Cao family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.2","size":7401,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"924901d5-6d31-4c33-b443-7931eadfac4b","description":"Phenopackets, Cao family, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.3","size":4251,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0f8abce3-e161-4bdf-981f-86257d505d69","description":"Phenopackets, Cao family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.4","size":9264,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c69a3d6c-4a28-4b7c-b215-0782f8d62429","description":"Synthetic Phenopacket dataset: Lalani family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.lalani.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"456e9ee0-5b60-4f38-82b5-83ba5d338038","description":"Phenopackets, Lalani family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.1","size":11606,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af6b862-7fc8-411a-86c5-d8e280e5b77a","description":"Phenopackets, Lalani family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.2","size":17531,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c37b37fd-0450-432d-b6aa-9ffdece35ad0","description":"Phenopackets, Lalani family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.4","size":9827,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0bb9d297-2710-48f6-ab4d-80d5eb0c9eaa","description":"Phenopackets, Lalani family, Patient 5","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.5","size":12111,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a3bb76d7-76ae-414b-81f7-97e663b02206","description":"Phenopackets, Lalani family, Patient 6","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.6","size":10316,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af5cdcf-898c-4dbc-944e-1ac95e82c0ea","description":"Synthetic Phenopacket dataset: Mundhofir family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.mundhofir.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"2506f0e1-29e4-4132-9b37-f7452dc8a89b","description":"Phenopackets, Mundhofir family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.1","size":7002,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c00c264a-8f17-471f-8ded-1a1f10e965ac","description":"Phenopackets, Mundhofir family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.2","size":7634,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"355a74bd-6571-4d4a-8602-a9989936717f","description":"Synthetic Phenopacket dataset: Zhang family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.zhang.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"697907bf-d5bd-433e-aac2-1747f1faf366","description":"Phenopackets, Zhang family, 2009, proband","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2009.proband","size":6471,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"3a45fab2-f350-445d-a137-4b1f946bf184","description":"Phenopackets, Zhang family, 2011, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2011.2","size":4001,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"ac53ca59-46f3-4f61-b1e7-bb75a49bfbfe","description":"Phenopackets, Zhang family, 2016, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2016.1","size":4709,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1275f896-4c8f-47e1-99a1-873a6b2ef5fb","description":"Phenopackets, Zhang family, 2017, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.1","size":7555,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7","description":"Phenopackets, Zhang family, 2017, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.2","size":7114,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"41898242-62a9-4129-9a2c-5a4e8f5f0afb","description":"Phenopackets, Zhang family, 2017, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.3","size":6262,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"6b994f18-6189-4233-bb83-139686490d68","description":"Phenopackets, Zhang family, 2017, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.4","size":6289,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"}] \ No newline at end of file +[{"id":"b8cd0667-2c33-4c9f-967b-161b905932c9","description":"Open dataset of 384 phenopackets","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.test.dataset","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a1dd4ae2-8d26-43b0-a199-342b64c7dff6","description":"Synthetic Phenopacket dataset: Cao family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.cao.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1a570e4e-2489-4218-9333-f65549495872","description":"Phenopackets, Cao family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.1","size":4257,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"4d83ba3f-a476-4c7c-868f-3d1fcf77fe29","description":"Phenopackets, Cao family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.2","size":7401,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"924901d5-6d31-4c33-b443-7931eadfac4b","description":"Phenopackets, Cao family, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.3","size":4251,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0f8abce3-e161-4bdf-981f-86257d505d69","description":"Phenopackets, Cao family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.cao.4","size":9264,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c69a3d6c-4a28-4b7c-b215-0782f8d62429","description":"Synthetic Phenopacket dataset: Lalani family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.lalani.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"456e9ee0-5b60-4f38-82b5-83ba5d338038","description":"Phenopackets, Lalani family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.1","size":11606,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af6b862-7fc8-411a-86c5-d8e280e5b77a","description":"Phenopackets, Lalani family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.2","size":17531,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c37b37fd-0450-432d-b6aa-9ffdece35ad0","description":"Phenopackets, Lalani family, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.4","size":9827,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"0bb9d297-2710-48f6-ab4d-80d5eb0c9eaa","description":"Phenopackets, Lalani family, Patient 5","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.5","size":12111,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"a3bb76d7-76ae-414b-81f7-97e663b02206","description":"Phenopackets, Lalani family, Patient 6","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.lalani.6","size":10316,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1af5cdcf-898c-4dbc-944e-1ac95e82c0ea","description":"Synthetic Phenopacket dataset: Mundhofir family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.mundhofir.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"2506f0e1-29e4-4132-9b37-f7452dc8a89b","description":"Phenopackets, Mundhofir family, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.1","size":7002,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"c00c264a-8f17-471f-8ded-1a1f10e965ac","description":"Phenopackets, Mundhofir family, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.mundhofir.2","size":7634,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"355a74bd-6571-4d4a-8602-a9989936717f","description":"Synthetic Phenopacket dataset: Zhang family","created_time":"2021-03-12T20:00:00Z","name":"phenopackets.zhang.family","updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"697907bf-d5bd-433e-aac2-1747f1faf366","description":"Phenopackets, Zhang family, 2009, proband","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2009.proband","size":6471,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"3a45fab2-f350-445d-a137-4b1f946bf184","description":"Phenopackets, Zhang family, 2011, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2011.2","size":4001,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"ac53ca59-46f3-4f61-b1e7-bb75a49bfbfe","description":"Phenopackets, Zhang family, 2016, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2016.1","size":4709,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"1275f896-4c8f-47e1-99a1-873a6b2ef5fb","description":"Phenopackets, Zhang family, 2017, Patient 1","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.1","size":7555,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"8f40acc0-0c54-45c5-8c85-a6f5fb32a1a7","description":"Phenopackets, Zhang family, 2017, Patient 2","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.2","size":7114,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"41898242-62a9-4129-9a2c-5a4e8f5f0afb","description":"Phenopackets, Zhang family, 2017, Patient 3","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.3","size":6262,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"6b994f18-6189-4233-bb83-139686490d68","description":"Phenopackets, Zhang family, 2017, Patient 4","created_time":"2021-03-12T20:00:00Z","mime_type":"application/json","name":"phenopackets.zhang.2017.4","size":6289,"updated_time":"2021-03-13T12:30:45Z","version":"1.0.0"},{"id":"e26f8428-1a5d-4465-b389-10998ba7b894","description":"PASSPORT AUTH - Phenopackets, Cao Patient 1","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.1","size":4257,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"},{"id":"996ead07-8dfc-4c5d-b319-5b59bebb507e","description":"PASSPORT AUTH - Phenopackets, Cao Patient 2","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.2","size":7401,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"},{"id":"81e8b499-1054-4846-8df4-d079ea32a44f","description":"PASSPORT AUTH - Phenopackets, Cao Patient 3","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.3","size":4251,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"},{"id":"672d0425-137c-437f-ab12-29a3cce1ffc0","description":"PASSPORT AUTH - Phenopackets, Cao Patient 4","created_time":"2022-05-16T20:00:00Z","mime_type":"application/json","name":"passport.phenopackets.cao.4","size":9264,"updated_time":"2022-05-16T20:00:00Z","version":"1.0.0"}] \ No newline at end of file From 8e67094dffcd0af77b54ae7ec0b1d12cb4d3bd4e Mon Sep 17 00:00:00 2001 From: Jeremy Adams Date: Thu, 19 May 2022 12:59:47 -0400 Subject: [PATCH 7/7] aim to restore admin tests --- src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java b/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java index 3cf9956..25801dd 100644 --- a/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java +++ b/src/main/java/org/ga4gh/starterkit/drs/controller/DrsAdmin.java @@ -177,5 +177,6 @@ private void breakInterminableFetch(DrsObject drsObject) { drsObject.setAwsS3AccessObjects(null); drsObject.setDrsObjectChildren(null); drsObject.setDrsObjectParents(null); + drsObject.setPassportVisas(null); } }