Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.broadinstitute.consent.http.resources.MetricsResource;
import org.broadinstitute.consent.http.resources.NihAccountResource;
import org.broadinstitute.consent.http.resources.OAuth2Resource;
import org.broadinstitute.consent.http.resources.PassportResource;
import org.broadinstitute.consent.http.resources.SamResource;
import org.broadinstitute.consent.http.resources.SchemaResource;
import org.broadinstitute.consent.http.resources.StatusResource;
Expand Down Expand Up @@ -139,7 +140,8 @@ public static void main(String[] args) throws Exception {
LOGGER.error("Unable to bootstrap sentry logging.");
}
} catch (Exception e) {
LOGGER.error(MessageFormat.format("Exception loading sentry properties: {0}", e.getMessage()));
LOGGER.error(
MessageFormat.format("Exception loading sentry properties: {0}", e.getMessage()));
}
new ConsentApplication().run(args);
LOGGER.info("Consent Application Started");
Expand All @@ -163,7 +165,8 @@ public void run(ConsentConfiguration config, Environment env) {
// Services
final DarCollectionService darCollectionService = injector.getProvider(
DarCollectionService.class).get();
final DataAccessRequestService dataAccessRequestService = injector.getProvider(DataAccessRequestService.class).get();
final DataAccessRequestService dataAccessRequestService = injector.getProvider(
DataAccessRequestService.class).get();
final DatasetService datasetService = injector.getProvider(DatasetService.class).get();
final ElectionService electionService = injector.getProvider(ElectionService.class).get();
final EmailService emailService = injector.getProvider(EmailService.class).get();
Expand Down Expand Up @@ -218,8 +221,7 @@ public void run(ConsentConfiguration config, Environment env) {
// Register standard application resources.
env.jersey().register(injector.getInstance(DaaResource.class));
env.jersey().register(injector.getInstance(DataAccessRequestResource.class));
env.jersey().register(new DatasetResource(datasetService, userService,
datasetRegistrationService, elasticSearchService, tdrService, gcsService));
env.jersey().register(injector.getInstance(DatasetResource.class));
env.jersey().register(injector.getInstance(DacResource.class));
env.jersey().register(injector.getInstance(DACAutomationRuleResource.class));
env.jersey().register(new DACUserResource(userService));
Expand All @@ -231,13 +233,15 @@ public void run(ConsentConfiguration config, Environment env) {
env.jersey().register(new MatchResource(matchService));
env.jersey().register(new MetricsResource(metricsService));
env.jersey().register(new NihAccountResource(nihService));
env.jersey().register(injector.getInstance(PassportResource.class));
env.jersey().register(new SamResource(samService, userService));
env.jersey().register(new SchemaResource());
env.jersey().register(new SwaggerResource(config.getGoogleAuthentication()));
env.jersey().register(new StatusResource(env.healthChecks()));
env.jersey().register(injector.getInstance(SupportResource.class));
env.jersey().register(
new UserResource(samService, userService, datasetService, acknowledgementService, nihService));
new UserResource(samService, userService, datasetService, acknowledgementService,
nihService));
env.jersey().register(new TosResource(samService));
env.jersey().register(injector.getInstance(VersionResource.class));
env.jersey().register(new VoteResource(userService, voteService, electionService));
Expand All @@ -251,12 +255,16 @@ public void run(ConsentConfiguration config, Environment env) {

// Authentication filters
final OAuthAuthenticator authenticator = injector.getProvider(OAuthAuthenticator.class).get();
final DuosUserAuthenticator duosUserAuthenticator = injector.getProvider(DuosUserAuthenticator.class).get();
final AuthorizationHelper authorizationHelper = injector.getProvider(AuthorizationHelper.class).get();
final DuosUserAuthenticator duosUserAuthenticator = injector.getProvider(
DuosUserAuthenticator.class).get();
final AuthorizationHelper authorizationHelper = injector.getProvider(AuthorizationHelper.class)
.get();
// Requests annotated with @Auth AuthUser will be authenticated through this filter
final AuthFilter<String, AuthUser> primaryAuthFilter = new OAuthCustomAuthFilter<>(authenticator, authorizationHelper);
final AuthFilter<String, AuthUser> primaryAuthFilter = new OAuthCustomAuthFilter<>(
authenticator, authorizationHelper);
// Requests annotated with @Auth DuosUser will be authenticated through this filter and are guaranteed to have a populated User object
final AuthFilter<String, DuosUser> duosAuthUserFilter = new OAuthCustomAuthFilter<>(duosUserAuthenticator, authorizationHelper);
final AuthFilter<String, DuosUser> duosAuthUserFilter = new OAuthCustomAuthFilter<>(
duosUserAuthenticator, authorizationHelper);
final PolymorphicAuthDynamicFeature<AuthUser> feature = new PolymorphicAuthDynamicFeature<>(
Map.of(
AuthUser.class, primaryAuthFilter,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.broadinstitute.consent.http.models.passport;

import org.broadinstitute.consent.http.service.PassportService;

public class AcceptedTermsAndPolicies implements VisaClaimType {

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

@Override
public Long asserted() {
return 0L;
}

@Override
public String value() {
return "";
}

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

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.broadinstitute.consent.http.models.passport;

import java.util.Optional;
import org.broadinstitute.consent.http.models.LibraryCard;
import org.broadinstitute.consent.http.models.User;
import org.broadinstitute.consent.http.service.PassportService;

/**
* <a href="https://github.com/ga4gh-duri/ga4gh-duri.github.io/blob/master/researcher_ids/ga4gh_passport_v1.md#affiliationandrole">AffiliationAndRole</a>
*/
public class AffiliationAndRole implements VisaClaimType {

private final User user;

public AffiliationAndRole(User user) {
this.user = user;
}

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

@Override
public Long asserted() {
var assertedDate = Optional.ofNullable(user.getLibraryCard())
.map(LibraryCard::getCreateDate)
.orElse(user.getCreateDate());
return assertedDate.getTime();
}

// TODO
// Is there a better way to get the user's singular institutional domain?
// Institutions can have multiple domains, e.g. "broadinstitute.org" and "broad.mit.edu".
@Override
public String value() {
String[] splitEmail = user.getEmail().split("@");
if (splitEmail.length > 1) {
String domain = splitEmail[splitEmail.length - 1];
return String.format("duos.researcher@%s", domain);
}
return "[email protected]";
}

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

@Override
public String by() {
if (user.getLibraryCard() == null) {
return VisaBy.SYSTEM.name().toLowerCase();
}
return VisaBy.SO.name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.broadinstitute.consent.http.models.passport;

import java.util.Calendar;
import org.broadinstitute.consent.http.models.ApprovedDataset;
import org.broadinstitute.consent.http.service.PassportService;

/**
* <a href="https://github.com/ga4gh-duri/ga4gh-duri.github.io/blob/master/researcher_ids/ga4gh_passport_v1.md#controlledaccessgrants">ControlledAccessGrants</a>
*/
public class ControlledAccessGrants implements VisaClaimType {

private final ApprovedDataset approvedDataset;

public ControlledAccessGrants(ApprovedDataset approvedDataset) {
this.approvedDataset = approvedDataset;
}

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

@Override
public Long asserted() {
if (approvedDataset.getExpirationDate() != null) {
var calendar = Calendar.getInstance();
calendar.setTime(approvedDataset.getExpirationDate());
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) - 1);
return calendar.getTimeInMillis();
}
return null;
}

@Override
public String value() {
return String.format("%s/dataset/%s", PassportService.ISS, approvedDataset.getDatasetIdentifier());
}

@Override
public String source() {
return approvedDataset.getDacName();
}

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

import java.util.List;

/**
* <a href="https://ga4gh.github.io/data-security/ga4gh-passport">GA4GH Passport Claim</a>
* @param ga4gh_passport_v1 List of Visa objects representing the user's claims
*/
public record PassportClaim(List<Visa> ga4gh_passport_v1) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.broadinstitute.consent.http.models.passport;

import java.util.Optional;
import org.broadinstitute.consent.http.models.LibraryCard;
import org.broadinstitute.consent.http.models.User;
import org.broadinstitute.consent.http.service.PassportService;

/**
* <a href="https://github.com/ga4gh-duri/ga4gh-duri.github.io/tree/master/researcher_ids#researcherstatus">ResearcherStatus</a>
*/
public class ResearcherStatus implements VisaClaimType {

private final User user;

public ResearcherStatus(User user) {
this.user = user;
}

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

@Override
public Long asserted() {
var assertedDate = Optional.ofNullable(user.getLibraryCard())
.map(LibraryCard::getCreateDate)
.orElse(user.getCreateDate());
return assertedDate.getTime();
}

@Override
public String value() {
// TODO Collect public URL for the user's profile such as an ORCID or institutional profile.
return PassportService.ISS;
}

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

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

public record Visa(String iss, String sub, Long iat, Long exp, VisaClaim ga4gh_visa_v1) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.broadinstitute.consent.http.models.passport;

public enum VisaBy {
DAC, SELF, SO, SYSTEM
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.broadinstitute.consent.http.models.passport;

public record VisaClaim(
String type,
Long asserted,
String value,
String source,
String by) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.broadinstitute.consent.http.models.passport;

import java.util.List;

public interface VisaClaimType {
String type();
Long asserted();
String value();
String source();
String by();
default List<VisaCondition> conditions() {
return List.of();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.broadinstitute.consent.http.models.passport;

public enum VisaClaimTypes {
AFFILIATION_AND_ROLE ("AffiliationAndRole"),
CONTROLLED_ACCESS_GRANTS ("ControlledAccessGrants"),
RESEARCHER_STATUS ("ResearcherStatus"),
ACCEPTED_TERMS_AND_POLICIES("AcceptedTermsAndPolicies"),
;

public final String type;

VisaClaimTypes(String type) {
this.type = type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.broadinstitute.consent.http.models.passport;

public record VisaCondition(VisaClaimType type, String value, String source, VisaBy by) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.broadinstitute.consent.http.resources;

import com.google.inject.Inject;
import io.dropwizard.auth.Auth;
import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.broadinstitute.consent.http.models.DuosUser;
import org.broadinstitute.consent.http.models.passport.PassportClaim;
import org.broadinstitute.consent.http.service.PassportService;

@Path("/api/passport")
public class PassportResource extends Resource {

private final PassportService passportService;

@Inject
public PassportResource(PassportService passportService) {
this.passportService = passportService;
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@PermitAll
public Response getPassport(@Auth DuosUser duosUser) {
try {
PassportClaim passport = passportService.generatePassport(duosUser);
return Response.ok().entity(passport).build();
} catch (Exception e) {
return createExceptionResponse(e);
}
}
}
Loading
Loading