Implementation plans and migration design notes live in docs/plans. Check that directory before starting larger feature, migration, or refactor work.
- Maven 3.9
- Java 25
- Dropwizard Docs: http://www.dropwizard.io/
git clone git@github.com:broadinstitute/consent.gitcd consent
mvn clean test package Tests require docker or another OCI runtime to spin up postgres and http servers that run against localhost. Ensure that your test environment supports that. Command line docker, podman, and Docker Desktop have all worked to run these tests. Podman was observed to perform better when running tests, however docker is the tool of choice for other developers and the CI/CD environment.
Consent docker images are stored in GCR: gcr.io/broad-dsp-gcr-public/consent
# build the docker image
docker build . -t consent
This image can then be run with the proper configuration files provided.
Specific to internal Broad systems:
APP_NAME=consent ENV=local OUTPUT_DIR=config ../firecloud-develop/configure.rbOtherwise, use src/test/resources/consent-config.yml as a template to
create your own environment-specific configuration.
Specific to internal Broad systems:
mvn clean compile
docker-compose -p consent -f config/docker-compose.yaml upOr, if not using docker:
java -jar /path/to/consent.jar server /path/to/config/fileVisit local swagger page: https://local.dsde-dev.broadinstitute.org:27443
Port 7777 is open in the configured docker compose.
Set up a remote debug configuration pointing to local.dsde-dev.broadinstitute.org
and the defaults should be correct.
elastic:
image: docker.elastic.co/elasticsearch/elasticsearch:9.4.4
ports:
- "9200:9200"
container_name: elastic
volumes:
- elastic:/usr/share/elasticsearch/data
deploy:
resources:
limits:
memory: 4gb
environment:
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
# X-Pack Security is OFF by default so the default `docker compose up` behaves exactly as
# before. Epics A-C and E (application-layer fallback) need no security. To work on Epic D
# (native DLS/FLS), start the stack with security on:
#
# ES_SECURITY_ENABLED=true docker-compose -p consent -f config/docker-compose.yaml up
#
# DLS/FLS is a Platinum feature, so also activate the 30-day trial license once per cluster:
#
# curl -u elastic:devpassword -XPOST 'localhost:9200/_license/start_trial?acknowledge=true'
#
# See DEVNOTES.md ("Developing with a local Elastic Search instance") for the full workflow.
- xpack.security.enabled=${ES_SECURITY_ENABLED:-false}
# Bootstraps the `elastic` superuser password when security is on; ignored when it is off.
# Must match authUser/authPassword in consent.yaml.
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD:-devpassword}
# Correct in both modes: transport SSL is only required for multi-node clusters.
- xpack.security.transport.ssl.enabled=false
# Keep the HTTP layer on plain http so consent's `protocol: http` client keeps working.
- xpack.security.http.ssl.enabled=false
I also suggest changing the default bucket location so uploaded ontology files do not interfere with other dev environments.
By default the local cluster runs with xpack.security.enabled=false: requests are
unauthenticated, which is what most work needs. Only work on native Elasticsearch document- and
field-level security (DLS/FLS) requires security to be on. Application-layer authorization work
does not — it never touches Elasticsearch security.
Security is env-var gated in the compose file, so the default stays off for everyone else:
mvn clean compile
ES_SECURITY_ENABLED=true docker-compose -p consent -f config/docker-compose.yaml upThat bootstraps the elastic superuser with the password from ELASTIC_PASSWORD
(default devpassword). Override it with
ES_SECURITY_ENABLED=true ELASTIC_PASSWORD=<your-password> docker-compose ... up, and keep
elasticSearch.authUser/authPassword in config/consent.yaml in sync.
The HTTP layer stays on plain http in both modes, so consent's elasticSearch.protocol: http
needs no change. xpack.security.transport.ssl.enabled=false is correct even with security
enabled — transport SSL is only required for multi-node clusters.
Enabling security is necessary but not sufficient for DLS/FLS. The Docker image self-generates
a basic license, and DLS/FLS is a Platinum/Enterprise feature. With a basic license, creating a
role or API key that carries a DLS query or an FLS field_security grant fails closed with
HTTP 403 current license is non-compliant for [field and document level security]. Note that
POST /_security/api_key accepts such a role descriptor at creation time — the rejection happens
later, on the search request.
Activate the 30-day trial license once the secured cluster is up:
curl -u elastic:devpassword -XPOST 'localhost:9200/_license/start_trial?acknowledge=true'
curl -u elastic:devpassword 'localhost:9200/_license' # => "type": "trial"Caveats:
- A trial can be started once per cluster (
GET /_license/trial_statusreports eligibility). After 30 days the license reverts to basic and DLS/FLS stops working. To get another trial, wipe the cluster's data volume:docker-compose -p consent -f config/docker-compose.yaml downthendocker volume rm consent_elastic(this deletes local indices — they must be re-indexed). - Authentication, role-based access control, and API keys all work fine on the basic license. Only the DLS/FLS grants require trial/Platinum.
Everything described above — the two security modes, the license gating, and that an unenforceable DLS/FLS grant fails closed instead of returning unrestricted data — is asserted by tests, so an upgrade does not need to be re-verified by hand:
# 1. bump ElasticSearchTestCluster.IMAGE (the only version pin in the test tree)
./mvnw test -Dgroups=elasticsearch
# 2. then match it here: config/docker-compose.yaml, pom.xml (elasticsearch-rest-client), this fileThese four classes carry the JUnit tag elasticsearch and are not run by CI — see
"How they run in CI" below. Running them is a deliberate step when changing the Elasticsearch
version or the Epic D security work, not something a build does for you.
See "Qualifying a new Elasticsearch version" in the integration test README.
DUOS uses SendGrid for sending emails and FreeMarker for expanding email templates before passing them to SendGrid. To add a new email, follow these steps: 1. 2. Create the HTML template and add it to src/main/resources/freemarker. 2. Add a new entry to EmailType that references that file. 3. Add a new MailMessage subclass that references this EmailType and implements methods to provide data for the freemarker template and database operation 4. Add a method to EmailService that creates this message and passes it to sendMessage().
Consent code includes a maven plugin for the OWASP dependency check tool. Three environment variables are needed to use this. You will need the dotNet 8 SDK to generate a report.
Install the dotNet 8 SDK with:
$ brew install --cask dotnet-sdk@8
Register at the National Vulnerability Database for an API key here: https://nvd.nist.gov/developers/request-an-api-key
Assign the key value they issue you as environment variable NVD_API_KEY
e.g.
$ export NVD_API_KEY=<your api key here>
Register at the OSS Index for an API key: https://ossindex.sonatype.org/
Assign the username you used (email address) to the environment variable OSS_INDEX_USERNAME
e.g.
$ export OSS_INDEX_USERNAME=<your username>
Assign the API Token (visible on the User Settings page) to the environment variable OSS_INDEX_PASSWORD
e.g.
$ export OSS_INDEX_PASSWORD=<your API token>
Run the dependency checker:
$ mvn org.owasp:dependency-check-maven:check
Integration tests live in src/test/java/**/integration/ and are run as part
of the standard mvn test lifecycle — no special profile, external server, or
manual Postgres setup is required.
Each test class extends ContainerTests, which uses a JUnit 5
DropwizardAppExtension to boot the full application in-process against the
config at src/test/resources/consent-ci.yaml. A WireMock server on port 9999
stands in for all external services (Sam, ECM, GCS, etc.).
Database seeding is performed programmatically in ContainerTests.seedDatabase()
via typed DAO calls (@BeforeAll). The seed data is fully synthetic and
idempotent. To add new baseline rows, extend the relevant seed* helper method
inside ContainerTests.
ContainerTests starts its own Testcontainers
PostgreSQLContainer in a static initializer and passes the container's
coordinates directly to DropwizardAppExtension via ConfigOverride. The
hardcoded coordinates in consent-ci.yaml are never reached at runtime. No
local Postgres is needed in any environment.
The GitHub Actions workflow at .github/workflows/coverage.yaml runs
mvn clean test on every push/PR to develop, which exercises unit and
integration tests together via Testcontainers — no additional CI configuration
is needed.
The exception is the Elasticsearch container tests. They are tagged
elasticsearch, and the ci profile in pom.xml — activated by the CI=true
that GitHub Actions sets in every job — feeds that tag to surefire's
excludedGroups, so they do not run in CI. They start up to three Elasticsearch
containers and exist to qualify a local Elasticsearch version rather than to
gate the build; see "Upgrading the local Elastic Search version" above.
To run them in CI anyway (a workflow edit, or a workflow_dispatch job), pass an
empty value on the command line, which overrides the profile:
./mvnw clean jacoco:prepare-agent test jacoco:report -DexcludedTestGroups=Integration tests only:
mvn clean test -Dtest="org.broadinstitute.consent.integration.**"All tests (unit + integration together, as CI does):
mvn clean testNote that locally this also runs the Elasticsearch container tests, which CI
skips. To match CI exactly, add -DexcludedTestGroups=elasticsearch.
From the IDE: run or debug any test class in the integration package
directly — DAOTestHelper activates automatically and provides the database.