Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
Expand Down Expand Up @@ -34,4 +35,27 @@ public Response getPassport(@Auth DuosUser duosUser) {
return createExceptionResponse(e);
}
}

/**
* Returns a Data Passport for a given dataset identifier, as proposed in the GA4GH Data Passports
* specification. The response uses the same {@code ga4gh_passport_v1} envelope as a Researcher
* Passport but contains dataset-centric visas: {@code ConsentedDataUseTerms}, {@code
* OversightBodies}, and {@code RequiredAgreements} (when a DAA exists).
*
* @param datasetIdentifier the formatted DUOS identifier, e.g. {@code DUOS-000001}
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@RolesAllowed({ADMIN})
@Path("dataset/{datasetIdentifier}")
public Response getDataPassport(
@SuppressWarnings("unused") @Auth DuosUser duosUser,
@PathParam("datasetIdentifier") String datasetIdentifier) {
try {
PassportClaim passport = passportService.generateDataPassport(datasetIdentifier);
return Response.ok().entity(passport).build();
} catch (Exception e) {
return createExceptionResponse(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ public Long asserted() {
Optional.ofNullable(user.getLibraryCard())
.map(LibraryCard::getCreateDate)
.orElse(user.getCreateDate());
return PassportService.getEpochSeconds(assertedDate.toInstant());
if (assertedDate == null) {
return PassportService.getEpochSeconds(java.time.Instant.now());
}
// java.sql.Date#toInstant throws UnsupportedOperationException; use epoch millis instead.
return PassportService.getEpochSeconds(java.time.Instant.ofEpochMilli(assertedDate.getTime()));
}

@Override
public String value() {
public Object value() {
if (user.getEmail() == null) {
return DEFAULT_VALUE;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.broadinstitute.consent.http.service.passport;

public class ApprovedUsersVisa implements VisaClaimType {

private final String datasetIdentifier;

public ApprovedUsersVisa(String datasetIdentifier) {
this.datasetIdentifier = datasetIdentifier;
}

@Override
public String type() {
return VisaClaimTypes.APPROVED_USERS.type;
}

@Override
public Long asserted() {
return PassportService.getEpochSeconds(java.time.Instant.now());
}

@Override
public Object value() {
return PassportService.getApprovedUsersEndpoint(datasetIdentifier);
}

@Override
public String source() {
return PassportService.ISS;
}

@Override
public String by() {
return VisaBy.DAC.name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.broadinstitute.consent.http.service.passport;

import java.time.Instant;
import java.util.List;
import org.broadinstitute.consent.http.models.Dataset;

/**
* Data Passport visa encoding the permitted uses of a dataset based on participant consent,
* expressed as a link to the dataset's data use terms. Leverages the Data Use Ontology (DUO) for
* standardization.
*
* @see <a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5372874">GA4GH Data Passports
* specification</a>
*/
public class ConsentedDataUseTermsVisa implements VisaClaimType {

private final Dataset dataset;

public ConsentedDataUseTermsVisa(Dataset dataset) {
this.dataset = dataset;
}

@Override
public String type() {
return VisaClaimTypes.CONSENTED_DATA_USE_TERMS.type;
}

@Override
public Long asserted() {
if (dataset.getCreateDate() != null) {
// java.sql.Date#toInstant throws UnsupportedOperationException; use epoch millis instead.
return PassportService.getEpochSeconds(
Instant.ofEpochMilli(dataset.getCreateDate().getTime()));
}
return PassportService.getEpochSeconds(Instant.now());
}

/**
* Returns a stable URL pointing to the dataset identifier that describes the consented terms
* dereference this URL to retrieve the full DUO-coded data use object for the dataset.
*/
@Override
public List<String> value() {
return PassportService.dataUseToTermArray(dataset.getDataUse());
}

@Override
public String source() {
return PassportService.ISS;
}

@Override
public String by() {
return VisaBy.DAC.name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.broadinstitute.consent.http.service.passport;

import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import org.broadinstitute.consent.http.models.ApprovedDataset;

/**
Expand Down Expand Up @@ -32,12 +32,12 @@ public Long asserted() {
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) - 1);
return PassportService.getEpochSeconds(calendar.toInstant());
}
// If there is no expiration date, we will use the current time as the asserted time.
return PassportService.getEpochSeconds(new Date().toInstant());
// If there is no expiration date, use the current time as the asserted time.
return PassportService.getEpochSeconds(Instant.now());
}

@Override
public String value() {
public Object value() {
return String.format(
"%s/dataset/%s", PassportService.ISS, approvedDataset.getDatasetIdentifier());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.broadinstitute.consent.http.service.passport;

import java.time.Instant;
import org.broadinstitute.consent.http.models.Dac;

/**
* Data Passport visa describing the entity responsible for governing access to the dataset. Maps to
* the DAC (Data Access Committee) that oversees the dataset in DUOS.
*
* @see <a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5372874">GA4GH Data Passports
* specification</a>
*/
public class OversightBodiesVisa implements VisaClaimType {

private final Dac dac;

public OversightBodiesVisa(Dac dac) {
this.dac = dac;
}

@Override
public String type() {
return VisaClaimTypes.OVERSIGHT_BODIES.type;
}

@Override
public Long asserted() {
if (dac.getCreateDate() != null) {
// java.sql.Date#toInstant throws UnsupportedOperationException; use epoch millis instead.
return PassportService.getEpochSeconds(Instant.ofEpochMilli(dac.getCreateDate().getTime()));
}
return PassportService.getEpochSeconds(Instant.now());
}

/**
* Returns a stable URL identifying the DAC within DUOS. Consumers can dereference this URL to
* retrieve details about the oversight body, including its members and chairpersons. TODO: We

Check warning on line 37 in src/main/java/org/broadinstitute/consent/http/service/passport/OversightBodiesVisa.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=DataBiosphere_consent&issues=AZ2OQITtZLn6M-rSpVBi&open=AZ2OQITtZLn6M-rSpVBi&pullRequest=2858
* need a public and stable identifier to point users to for DACs.
*/
@Override
public Object value() {
return "%s/dac/%d".formatted(PassportService.ISS, dac.getDacId());
}

@Override
public String source() {
return PassportService.ISS;
}

@Override
public String by() {
return VisaBy.DAC.name().toLowerCase();
}
}
Loading
Loading