diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..39cdfb19 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @samply/bridgehead-developers diff --git a/README.md b/README.md index 0e8131a1..d0e462a7 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,16 @@ This repository is the starting point for any information and tools you will nee - [TLS terminating proxies](#tls-terminating-proxies) - [File structure](#file-structure) - [BBMRI-ERIC Directory entry needed](#bbmri-eric-directory-entry-needed) + - [Directory sync tool](#directory-sync-tool) - [Loading data](#loading-data) + - [Teiler (Frontend)](#teiler-frontend) + - [Data Exporter Service](#data-exporter-service) + - [Data Quality Report](#data-quality-report) 4. [Things you should know](#things-you-should-know) - [Auto-Updates](#auto-updates) - [Auto-Backups](#auto-backups) - [Non-Linux OS](#non-linux-os) + - [FAQ](#faq) 5. [Troubleshooting](#troubleshooting) - [Docker Daemon Proxy Configuration](#docker-daemon-proxy-configuration) - [Monitoring](#monitoring) @@ -80,6 +85,8 @@ The following URLs need to be accessible (prefix with `https://`): * hub.docker.com * registry-1.docker.io * production.cloudflare.docker.com + * GitHub Container Registry - (for use of DNPM:DIP) + * ghcr.io * To report bridgeheads operational status * healthchecks.verbis.dkfz.de * only for DKTK/CCP @@ -90,7 +97,7 @@ The following URLs need to be accessible (prefix with `https://`): * only for German Biobank Node * broker.bbmri.de -> 📝 This URL list is subject to change. Instead of the individual names, we highly recommend whitelisting wildcard domains: *.dkfz.de, github.com, *.docker.com, *.docker.io, *.samply.de, *.bbmri.de. +> 📝 This URL list is subject to change. Instead of the individual names, we highly recommend whitelisting wildcard domains: *.dkfz.de, github.com, *.docker.com, *.docker.io, *.ghcr.io, *.samply.de, *.bbmri.de. > 📝 Ubuntu's pre-installed uncomplicated firewall (ufw) is known to conflict with Docker, more info [here](https://github.com/chaifeng/ufw-docker). @@ -301,26 +308,45 @@ Once you have added your biobank to the Directory you got persistent identifier ### Directory sync tool -The Bridgehead's **Directory Sync** is an optional feature that keeps the Directory up to date with your local data, e.g. number of samples. Conversely, it also updates the local FHIR store with the latest contact details etc. from the Directory. You must explicitly set your country specific directory URL, username and password to enable this feature. +The Bridgehead's **Directory Sync** is an optional feature that keeps the BBMRI-ERIC Directory up to date with your local data, e.g. number of samples. Conversely, it can also update the local FHIR store with the latest contact details etc. from the BBMRI-ERIC Directory. You should talk with your local data protection group regarding the information that is published by Directory sync. -Full details can be found in [directory_sync_service](https://github.com/samply/directory_sync_service). - -To enable it, you will need to set these variables to the ```bbmri.conf``` file of your GitLab repository. Here is an example config: +To enable it, you will need to explicitly set the username and password variables for BBMRI-ERIC Directory login in the configuration file of your GitLab repository (e.g. ```bbmri.conf```). Here is an example minimal config: ``` DS_DIRECTORY_USER_NAME=your_directory_username DS_DIRECTORY_USER_PASS=your_directory_password ``` -Please contact your National Node to obtain this information. +Alternatively, if you have obtained a token from the Directory, you can insert the following into the configuration file: +``` +DS_DIRECTORY_USER_TOKEN=your_directory_token +``` +If you don't supply any authentification information (either login credentials or a token), Directory sync will not start. -Optionally, you **may** change when you want Directory sync to run by specifying a [cron](https://crontab.guru) expression, e.g. `DS_TIMER_CRON="0 22 * * *"` for 10 pm every evening. +Please contact your National Node or Directory support (directory-dev@helpdesk.bbmri-eric.eu) to obtain these credentials. -Once you edited the gitlab config, the bridgehead will autoupdate the config with the values and will sync the data. +The following environment variables can be used from within your config file to control the behavior of Directory sync: + +| Variable | Purpose | Default if not specified | +|:-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------| +| DS_DIRECTORY_URL | Base URL of the Directory | https://directory-backend.molgenis.net | +| DS_DIRECTORY_USER_NAME | User name for logging in to Directory | | +| DS_DIRECTORY_USER_PASS | Password for logging in to Directory | | +| DS_DIRECTORY_USER_TOKEN | Token for logging in to Directory | | +| DS_DIRECTORY_DEFAULT_COLLECTION_ID | ID of collection to be used if not in samples | | +| DS_DIRECTORY_ALLOW_STAR_MODEL | Set to 'True' to send star model info to Directory | True | +| DS_FHIR_STORE_URL | URL for FHIR store | http://bridgehead-bbmri-blaze:8080 | +| DS_TIMER_CRON | Execution interval for Directory sync, [cron](https://crontab.guru) format | 30 22 * * * | +| DS_IMPORT_BIOBANKS | Set to 'True' to import biobank metadata from Directory | True | +| DS_IMPORT_COLLECTIONS | Set to 'True' to import collection metadata from Directory | True | + +Once you have finished editing the config, the Bridgehead will autoupdate the config with the values and will sync data at regular intervals, using the time specified in DS_TIMER_CRON. There will be a delay before the effects of Directory sync become visible. First, you will need to wait until the time you have specified in ```TIMER_CRON```. Second, the information will then be synchronized from your national node with the central European Directory. This can take up to 24 hours. +More details of Directory sync can be found in [directory_sync_service](https://github.com/samply/directory_sync_service). + ### Loading data The data accessed by the federated search is held in the Bridgehead in a FHIR store (we use Blaze). @@ -340,6 +366,24 @@ The storage space on your hard drive will depend on the number of FHIR resources For more information on Blaze performance, please refer to [import performance](https://github.com/samply/blaze/blob/master/docs/performance/import.md). +### Clearing data + +The Bridgehead's FHIR store, Blaze, saves its data in a Docker volume. This means that the data will persist even if you stop the Bridgehead. You can clear existing data from the FHIR store by deleting the relevant Docker volume. + +First, stop the Bridgehead: +```shell +sudo systemctl stop bridgehead@.service +``` +Now remove the volume: +```shell +docker volume rm _blaze-data +``` +Finally, restart the Bridgehead: +```shell +sudo systemctl start bridgehead@.service +``` +You will need to do this for example if you are using a VM as a test environment and you subsequently want to use the same VM for production. + #### ETL for BBMRI and GBA Normally, you will need to build your own ETL to feed the Bridgehead. However, there is one case where a short cut might be available: @@ -347,6 +391,39 @@ Normally, you will need to build your own ETL to feed the Bridgehead. However, t You can find the profiles for generating FHIR in [Simplifier](https://simplifier.net/bbmri.de/~resources?category=Profile). +### Teiler (Frontend) + +Teiler is the web-based frontend of the Bridgehead, providing access to its various internal, and external services and components. +To learn how to integrate your custom module into Teiler, please refer to https://github.com/samply/teiler-dashboard. +- To activate Teiler, set the following environment variable in your `.conf` file: + +```bash +ENABLE_TEILER=true +``` +[For further information](docs/teiler.md) + +### Data Exporter Service + +The Exporter is a dedicated service for extracting and exporting Bridgehead data in (tabular) formats such as Excel, CSV, Opal, JSON, XML, ... +- To enable the Exporter service, set the following environment variable in your `.conf` file: + +```bash +ENABLE_EXPORTER=true +``` + +#### Data Quality Report +To assess the quality and plausibility of your imported data, the Reporter component is pre-configured to generate Excel reports with data quality metrics and statistical analyses. Reporter is part of the Exporter and can be enabled by setting the same environment variable in your `.conf` file: +```bash +ENABLE_EXPORTER=true +``` + +For convenience, it's recommended to enable the Teiler web frontend alongside the Exporter to access export and quality control features via a web interface: set the following environment varibles in your `.conf` file: +```bash +ENABLE_TEILER=true +ENABLE_EXPORTER=true +``` +[For further information](docs/exporter.md) + ## Things you should know ### Auto-Updates @@ -386,6 +463,54 @@ We have tested the installation procedure with an Ubuntu 22.04 guest system runn Installation under WSL ought to work, but we have not tested this. +### FAQ + +**Q: How is the security of GitHub pulls, volumes/containers, and image signing ensured?** + +A: Changes to Git branches that could be delivered to sites (main and develop) must be accepted via a pull request with at least two positive reviews. +Containers/images are not built manually, but rather automatically through a CI/CD pipeline, so that an image can be rolled back to a defined code version at any time without changes. +**Note:** If firewall access for (outgoing) connections to GitHub and/or Docker Hub is problematic at the site, mirrors for both services are available, operated by the DKFZ. + +**Q: How is authentication between users and components regulated?** + +A: When setting up a Bridgehead, a private key and a so-called Certificate Sign Request (CSR) are generated locally. This CSR is manually signed by the broker operator, which allows the Bridgehead access to the network infrastructure. +All communication runs via Samply.Beam and is therefore end-to-end encrypted, but also signed. This allows the integrity and authenticity of the sender to be technically verified (which happens automatically both in the broker and at the recipients). +The connection to the broker is additionally secured using traditional TLS (transport encryption over https). + +**Q: Are there any statistics on incoming traffic from the Bridgehead (what goes in and what goes out)?** + +A: Incoming and outgoing traffic can only enter/leave the Bridgehead via a forward or reverse proxy, respectively. These components log all connections. +Statistical analysis is not currently being conducted, but is on the roadmap for some projects. We are also working on a dashboard for all tasks/responses delivered via Samply.Beam. + +**Q: How is container access controlled, and what permission level is used?** + +A: Currently, it is not possible to run the Bridgehead "out-of-the-box" as a rootless Docker Compose stack. The main reason is the operation of the reverse proxy (Traefik), which binds to the privileged ports 80 (HTTP) and 443 (HTTPS). +Otherwise, there are no known technical obstacles, although we don't have concrete experience implementing this. +At the file system level, a "bridgehead" user is created during installation, which manages the configuration and Bridgehead folders. + +**Q: Is a cloud installation (not a company-owned one, but an external service provider) possible?** + +A: Technically, yes. This is primarily a data protection issue between the participant and their cloud provider. +The Bridgehead contains a data storage system that, during use, contains sensitive patient and sample data. +There are cloud providers with whom appropriately worded contracts can be concluded to make this possible. +Of course, the details must be discussed with the responsible data protection officer. + +**Q: What needs to be considered regarding the Docker distribution/registry, and how is it used here?** + +A: The Bridgehead images are located both in Docker Hub and mirrored in a registry operated by the DKFZ. +The latter is used by default, avoiding potential issues with Docker Hub URL activation or rate limits. +When using automatic updates (highly recommended), an daily check is performed for: +- site configuration updates +- Bridgehead software updates +- container image updates + +If updates are found, they are downloaded and applied. +See the first question for the control mechanism. + +**Q: Is data only transferred one-way (Bridgehead/FHIR Store → Central/Locator), or is two-way access necessary?** + +A: By using Samply.Beam, only one outgoing connection to the broker is required at the network level (i.e., Bridgehead → Broker). + ## Troubleshooting ### Docker Daemon Proxy Configuration diff --git a/bbmri/docker-compose.yml b/bbmri/docker-compose.yml index 1903c626..334b0487 100644 --- a/bbmri/docker-compose.yml +++ b/bbmri/docker-compose.yml @@ -4,7 +4,7 @@ version: "3.7" services: blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.31 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-bbmri-blaze environment: BASE_URL: "http://bridgehead-bbmri-blaze:8080" diff --git a/bbmri/modules/directory-sync-compose.yml b/bbmri/modules/directory-sync-compose.yml index 33a7d312..58e1c69a 100644 --- a/bbmri/modules/directory-sync-compose.yml +++ b/bbmri/modules/directory-sync-compose.yml @@ -7,10 +7,16 @@ services: DS_DIRECTORY_URL: ${DS_DIRECTORY_URL:-https://directory.bbmri-eric.eu} DS_DIRECTORY_USER_NAME: ${DS_DIRECTORY_USER_NAME} DS_DIRECTORY_USER_PASS: ${DS_DIRECTORY_USER_PASS} - DS_TIMER_CRON: ${DS_TIMER_CRON:-0 22 * * *} + DS_DIRECTORY_USER_TOKEN: ${DS_DIRECTORY_USER_TOKEN} + DS_TIMER_CRON: ${DS_TIMER_CRON:-30 22 * * *} DS_DIRECTORY_ALLOW_STAR_MODEL: ${DS_DIRECTORY_ALLOW_STAR_MODEL:-true} DS_DIRECTORY_MOCK: ${DS_DIRECTORY_MOCK} DS_DIRECTORY_DEFAULT_COLLECTION_ID: ${DS_DIRECTORY_DEFAULT_COLLECTION_ID} DS_DIRECTORY_COUNTRY: ${DS_DIRECTORY_COUNTRY} + DS_IMPORT_BIOBANKS: ${DS_IMPORT_BIOBANKS:-true} + DS_IMPORT_COLLECTIONS: ${DS_IMPORT_COLLECTIONS:-true} depends_on: - "blaze" + volumes: + - /etc/localtime:/etc/localtime:ro # inherit host timezone + - /etc/timezone:/etc/timezone:ro # inherit host timezone name diff --git a/bbmri/modules/directory-sync.sh b/bbmri/modules/directory-sync.sh index 2765dfbb..01ce7df7 100755 --- a/bbmri/modules/directory-sync.sh +++ b/bbmri/modules/directory-sync.sh @@ -1,6 +1,6 @@ #!/bin/bash -if [ -n "${DS_DIRECTORY_USER_NAME}" ]; then +if [ -n "${DS_DIRECTORY_USER_NAME}" ] || [ -n "${DS_DIRECTORY_USER_TOKEN}" ]; then log INFO "Directory sync setup detected -- will start directory sync service." OVERRIDE+=" -f ./$PROJECT/modules/directory-sync-compose.yml" fi diff --git a/bbmri/modules/eric-compose.yml b/bbmri/modules/eric-compose.yml index 7fc0ef62..e6ed0447 100644 --- a/bbmri/modules/eric-compose.yml +++ b/bbmri/modules/eric-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: focus-eric: - image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG}-bbmri + image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG} container_name: bridgehead-focus-eric environment: API_KEY: ${ERIC_FOCUS_BEAM_SECRET_SHORT} @@ -11,6 +11,7 @@ services: BLAZE_URL: "http://blaze:8080/fhir/" BEAM_PROXY_URL: http://beam-proxy-eric:8081 RETRY_COUNT: ${FOCUS_RETRY_COUNT} + OBFUSCATE_BBMRI_ERIC_WAY: "true" depends_on: - "beam-proxy-eric" - "blaze" diff --git a/bbmri/modules/eric-setup.sh b/bbmri/modules/eric-setup.sh index d2f36bb0..fd44e731 100644 --- a/bbmri/modules/eric-setup.sh +++ b/bbmri/modules/eric-setup.sh @@ -10,6 +10,10 @@ if [ "${ENABLE_ERIC}" == "true" ]; then export ERIC_BROKER_ID=broker.bbmri.samply.de export ERIC_ROOT_CERT=eric ;; + "acceptance") + export ERIC_BROKER_ID=broker-acc.bbmri-acc.samply.de + export ERIC_ROOT_CERT=eric.acc + ;; "test") export ERIC_BROKER_ID=broker-test.bbmri-test.samply.de export ERIC_ROOT_CERT=eric.test diff --git a/bbmri/modules/eric.acc.root.crt.pem b/bbmri/modules/eric.acc.root.crt.pem new file mode 100644 index 00000000..1055d35e --- /dev/null +++ b/bbmri/modules/eric.acc.root.crt.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgIUFzdpDi1OLdXyogtCsktHFhCILtMwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAxMLQnJva2VyLVJvb3QwHhcNMjUwNjEwMTQzNjE1WhcNMzUw +NjA4MTQzNjQ1WjAWMRQwEgYDVQQDEwtCcm9rZXItUm9vdDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALpJCWE9Qe19R9DqotdkPV6jfiuJSKI3UYkCWdWG +nRfkKB6OaY5t3JCHDqaEME9FwSd2nFXhTp5F6snG/K7g8MCLIEzGzuSnrdjGqINq +zXLfgqnxvQpPR4ARLNNgnKxZaq7m4Q3T/l+QAshK6CnCUWFQ6q5x3g/pZHFP2USd +/G2FtDHX6YK4bHbbnigIPG6PdY2RYy60i30XGdIPBNf82XGkAtPUBz731gHOV5Vg +d+jfAqTwZAhYC2CcNmswFw1H9GrvTI/9KZWKcZNUIqemc0A/FyEyONUM18/vjQ7D +lUwOcQsgAg44QTOUPgqXv3sJPQM5EnGuv3yYV9u6Y2i78M8CAwEAAaN7MHkwDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPrDeNWgtEyZ +VM0yeoRZdK2QGjyvMB8GA1UdIwQYMBaAFPrDeNWgtEyZVM0yeoRZdK2QGjyvMBYG +A1UdEQQPMA2CC0Jyb2tlci1Sb290MA0GCSqGSIb3DQEBCwUAA4IBAQAD2S0kqL18 +laewh+qnyZ0WMq12mLV/Rwll6ZuShCx2uAu3UZuIGWk3l7gG5zlws+i+zbaNcn4o +HsS3WG9kiNLOMKp8LXGkjErl6RaQr+kb8qgYFTPjOr6v0OdVn6ve9RDNYB5Hd+zE +9jAWmS8PfS2AldE4VAd0C4pWTAinhnKGrKdn1YAX5x+LMq1y0lc1Pd4CDgsjD6SS +3td7JtenXqCX0mN0XSeck7vvFGa6QpcQoVcN9tRENctHZTwyeGA21IkXylpFPUkE +LT60k48fNC8TZkBlfvtVGRebpm5krXIKEaVy5LniEpSuOR4hTqsgoQDntBjW4zHA +GeWQ1wQNTEBX +-----END CERTIFICATE----- diff --git a/bbmri/modules/exporter-compose.yml b/bbmri/modules/exporter-compose.yml new file mode 100644 index 00000000..de2ef428 --- /dev/null +++ b/bbmri/modules/exporter-compose.yml @@ -0,0 +1,86 @@ +version: "3.7" + +services: + exporter: + image: docker.verbis.dkfz.de/ccp/dktk-exporter:latest + container_name: bridgehead-bbmri-exporter + environment: + JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" + LOG_LEVEL: "INFO" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" # Set in exporter-setup.sh + CROSS_ORIGINS: "https://${HOST}" + EXPORTER_DB_USER: "exporter" + EXPORTER_DB_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh + EXPORTER_DB_URL: "jdbc:postgresql://exporter-db:5432/exporter" + HTTP_RELATIVE_PATH: "/bbmri-exporter" + SITE: "${SITE_ID}" + HTTP_SERVLET_REQUEST_SCHEME: "https" + OPAL_PASSWORD: "${EXPORTER_OPAL_PASSWORD}" + labels: + - "traefik.enable=true" + - "traefik.http.routers.exporter_bbmri.rule=PathPrefix(`/bbmri-exporter`)" + - "traefik.http.services.exporter_bbmri.loadbalancer.server.port=8092" + - "traefik.http.routers.exporter_bbmri.tls=true" + - "traefik.http.middlewares.exporter_bbmri_strip.stripprefix.prefixes=/bbmri-exporter" + - "traefik.http.routers.exporter_bbmri.middlewares=exporter_bbmri_strip" + # Main router + - "traefik.http.routers.exporter_bbmri.priority=20" + + # API router + - "traefik.http.routers.exporter_bbmri_api.middlewares=exporter_bbmri_strip,exporter_auth" + - "traefik.http.routers.exporter_bbmri_api.rule=PathRegexp(`/bbmri-exporter/.+`)" + - "traefik.http.routers.exporter_bbmri_api.tls=true" + - "traefik.http.routers.exporter_bbmri_api.priority=25" + + # Shared middlewares + - "traefik.http.middlewares.exporter_auth.basicauth.users=${EXPORTER_USER}" + + volumes: + - "/var/cache/bridgehead/bbmri/exporter-files:/app/exporter-files/output" + + exporter-db: + image: docker.verbis.dkfz.de/cache/postgres:${POSTGRES_TAG} + container_name: bridgehead-bbmri-exporter-db + environment: + POSTGRES_USER: "exporter" + POSTGRES_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh + POSTGRES_DB: "exporter" + volumes: + # Consider removing this volume once we find a solution to save Lens-queries to be executed in the explorer. + - "/var/cache/bridgehead/bbmri/exporter-db:/var/lib/postgresql/data" + + reporter: + image: docker.verbis.dkfz.de/ccp/dktk-reporter:latest + container_name: bridgehead-bbmri-reporter + environment: + JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" + LOG_LEVEL: "INFO" + CROSS_ORIGINS: "https://${HOST}" + HTTP_RELATIVE_PATH: "/bbmri-reporter" + SITE: "${SITE_ID}" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" # Set in exporter-setup.sh + EXPORTER_URL: "http://exporter:8092" + LOG_FHIR_VALIDATION: "false" + HTTP_SERVLET_REQUEST_SCHEME: "https" + + # In this initial development state of the bridgehead, we are trying to have so many volumes as possible. + # However, in the first executions in the bbmri sites, this volume seems to be very important. A report is + # a process that can take several hours, because it depends on the exporter. + # There is a risk that the bridgehead restarts, losing the already created export. + + volumes: + - "/var/cache/bridgehead/bbmri/reporter-files:/app/reports" + labels: + - "traefik.enable=true" + - "traefik.http.routers.reporter_bbmri.rule=PathPrefix(`/bbmri-reporter`)" + - "traefik.http.services.reporter_bbmri.loadbalancer.server.port=8095" + - "traefik.http.routers.reporter_bbmri.tls=true" + - "traefik.http.middlewares.reporter_bbmri_strip.stripprefix.prefixes=/bbmri-reporter" + - "traefik.http.routers.reporter_bbmri.middlewares=reporter_bbmri_strip" + - "traefik.http.routers.reporter_bbmri.priority=20" + + - "traefik.http.routers.reporter_bbmri_api.middlewares=reporter_bbmri_strip,exporter_auth" + - "traefik.http.routers.reporter_bbmri_api.rule=PathRegexp(`/bbmri-reporter/.+`)" + - "traefik.http.routers.reporter_bbmri_api.tls=true" + - "traefik.http.routers.reporter_bbmri_api.priority=25" + diff --git a/bbmri/modules/exporter-setup.sh b/bbmri/modules/exporter-setup.sh new file mode 100644 index 00000000..9b947a60 --- /dev/null +++ b/bbmri/modules/exporter-setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +if [ "$ENABLE_EXPORTER" == true ]; then + log INFO "Exporter setup detected -- will start Exporter service." + OVERRIDE+=" -f ./$PROJECT/modules/exporter-compose.yml" + EXPORTER_DB_PASSWORD="$(echo \"This is a salt string to generate one consistent password for the exporter. It is not required to be secret.\" | sha1sum | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 30)" + EXPORTER_API_KEY="$(echo \"This is a salt string to generate one consistent API KEY for the exporter. It is not required to be secret.\" | sha1sum | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 64)" +fi diff --git a/bbmri/modules/gbn-compose.yml b/bbmri/modules/gbn-compose.yml index 0fa75856..94631ba2 100644 --- a/bbmri/modules/gbn-compose.yml +++ b/bbmri/modules/gbn-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: focus-gbn: - image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG}-bbmri + image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG} container_name: bridgehead-focus-gbn environment: API_KEY: ${GBN_FOCUS_BEAM_SECRET_SHORT} diff --git a/bbmri/modules/teiler-compose.yml b/bbmri/modules/teiler-compose.yml new file mode 100644 index 00000000..e241b9c3 --- /dev/null +++ b/bbmri/modules/teiler-compose.yml @@ -0,0 +1,64 @@ +version: "3.7" + +services: + + teiler-orchestrator: + image: docker.verbis.dkfz.de/cache/samply/teiler-orchestrator:latest + container_name: bridgehead-teiler-orchestrator + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_orchestrator_bbmri.rule=PathPrefix(`/bbmri-teiler`)" + - "traefik.http.services.teiler_orchestrator_bbmri.loadbalancer.server.port=9000" + - "traefik.http.routers.teiler_orchestrator_bbmri.tls=true" + - "traefik.http.middlewares.teiler_orchestrator_bbmri_strip.stripprefix.prefixes=/bbmri-teiler" + - "traefik.http.routers.teiler_orchestrator_bbmri.middlewares=teiler_orchestrator_bbmri_strip" + environment: + TEILER_BACKEND_URL: "/bbmri-teiler-backend" + TEILER_DASHBOARD_URL: "/bbmri-teiler-dashboard" + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE_LOWER_CASE}" + HTTP_RELATIVE_PATH: "/bbmri-teiler" + + teiler-dashboard: + image: docker.verbis.dkfz.de/cache/samply/teiler-dashboard:${TEILER_DASHBOARD_TAG} + container_name: bridgehead-teiler-dashboard + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_dashboard_bbmri.rule=PathPrefix(`/bbmri-teiler-dashboard`)" + - "traefik.http.services.teiler_dashboard_bbmri.loadbalancer.server.port=80" + - "traefik.http.routers.teiler_dashboard_bbmri.tls=true" + - "traefik.http.middlewares.teiler_dashboard_bbmri_strip.stripprefix.prefixes=/bbmri-teiler-dashboard" + - "traefik.http.routers.teiler_dashboard_bbmri.middlewares=teiler_dashboard_bbmri_strip" + environment: + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" + TEILER_BACKEND_URL: "/bbmri-teiler-backend" + TEILER_DASHBOARD_URL: "/bbmri-teiler-dashboard" + TEILER_ADMIN_NAME: "${OPERATOR_FIRST_NAME} ${OPERATOR_LAST_NAME}" + TEILER_ADMIN_EMAIL: "${OPERATOR_EMAIL}" + TEILER_ADMIN_PHONE: "${OPERATOR_PHONE}" + TEILER_PROJECT: "${PROJECT}" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" + TEILER_ORCHESTRATOR_URL: "/bbmri-teiler" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/bbmri-teiler" + REPORTER_DEFAULT_TEMPLATE_ID: "bbmri-qb" + EXPORTER_DEFAULT_TEMPLATE_ID: "bbmri" + + + teiler-backend: + image: docker.verbis.dkfz.de/ccp/bbmri-teiler-backend:latest + container_name: bridgehead-teiler-backend + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_backend_bbmri.rule=PathPrefix(`/bbmri-teiler-backend`)" + - "traefik.http.services.teiler_backend_bbmri.loadbalancer.server.port=8085" + - "traefik.http.routers.teiler_backend_bbmri.tls=true" + - "traefik.http.middlewares.teiler_backend_bbmri_strip.stripprefix.prefixes=/bbmri-teiler-backend" + - "traefik.http.routers.teiler_backend_bbmri.middlewares=teiler_backend_bbmri_strip" + environment: + LOG_LEVEL: "INFO" + APPLICATION_PORT: "8085" + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/bbmri-teiler" + TEILER_ORCHESTRATOR_URL: "/bbmri-teiler" + TEILER_DASHBOARD_DE_URL: "/bbmri-teiler-dashboard/de" + TEILER_DASHBOARD_EN_URL: "/bbmri-teiler-dashboard/en" + HTTP_PROXY: "http://forward_proxy:3128" diff --git a/bbmri/modules/teiler-setup.sh b/bbmri/modules/teiler-setup.sh new file mode 100644 index 00000000..dc8dfc47 --- /dev/null +++ b/bbmri/modules/teiler-setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +if [ "$ENABLE_TEILER" == true ];then + log INFO "Teiler setup detected -- will start Teiler services." + OVERRIDE+=" -f ./$PROJECT/modules/teiler-compose.yml" + TEILER_DEFAULT_LANGUAGE=EN + TEILER_DEFAULT_LANGUAGE_LOWER_CASE=${TEILER_DEFAULT_LANGUAGE,,} +fi diff --git a/bbmri/vars b/bbmri/vars index 248fbee4..6f1dbd7a 100644 --- a/bbmri/vars +++ b/bbmri/vars @@ -1,3 +1,9 @@ +BROKER_ID=broker-test.bbmri-test.samply.de +BROKER_URL=https://${BROKER_ID} +PROXY_ID=${SITE_ID}.${BROKER_ID} +PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem +BROKER_URL_FOR_PREREQ=$BROKER_URL + # Makes sense for all European Biobanks : ${ENABLE_ERIC:=true} @@ -5,7 +11,6 @@ : ${ENABLE_GBN:=false} FOCUS_RETRY_COUNT=${FOCUS_RETRY_COUNT:-64} -PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem for module in $PROJECT/modules/*.sh do diff --git a/bridgehead b/bridgehead index cbe75275..09b46f59 100755 --- a/bridgehead +++ b/bridgehead @@ -35,6 +35,9 @@ case "$PROJECT" in cce) #nothing extra to do ;; + pscc) + #nothing extra to do + ;; itcc) #nothing extra to do ;; @@ -44,6 +47,9 @@ case "$PROJECT" in dhki) #nothing extra to do ;; + nngm) + #nothing extra to do + ;; minimal) #nothing extra to do ;; @@ -69,7 +75,7 @@ loadVars() { if [ "$(git rev-parse --abbrev-ref HEAD)" == "main" ]; then ENVIRONMENT="production" else - ENVIRONMENT="test" + ENVIRONMENT="test" # we have acceptance environment in BBMRI ERIC and it would be more appropriate to default to that one in case the data they have in BH is real, but I'm gonna leave it as is for backward compatibility fi fi # Source the versions of the images components @@ -80,6 +86,9 @@ loadVars() { "test") source ./versions/test ;; + "acceptance") + source ./versions/acceptance + ;; *) report_error 7 "Environment \"$ENVIRONMENT\" is unknown. Assuming production. FIX THIS!" source ./versions/prod @@ -143,6 +152,10 @@ case "$ACTION" in loadVars exec ./lib/update-bridgehead.sh $PROJECT ;; + check) + loadVars &> /dev/null + exec ./lib/check-bridgehead.sh $PROJECT + ;; install) source ./lib/prepare-system.sh NODEV loadVars @@ -159,7 +172,7 @@ case "$ACTION" in adduser) loadVars log "INFO" "Adding encrypted credentials in /etc/bridgehead/$PROJECT.local.conf" - read -p "Please choose the component (LDM_AUTH|NNGM_AUTH) you want to add a user to : " COMPONENT + read -p "Please choose the component (LDM_AUTH|NNGM_AUTH|EXPORTER_USER) you want to add a user to : " COMPONENT read -p "Please enter a username: " USER read -s -p "Please enter a password (will not be echoed): "$'\n' PASSWORD add_basic_auth_user $USER $PASSWORD $COMPONENT $PROJECT diff --git a/cce/docker-compose.yml b/cce/docker-compose.yml index 0641af70..99039e7b 100644 --- a/cce/docker-compose.yml +++ b/cce/docker-compose.yml @@ -2,13 +2,14 @@ version: "3.7" services: blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.31 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-cce-blaze environment: BASE_URL: "http://bridgehead-cce-blaze:8080" JAVA_TOOL_OPTIONS: "-Xmx${BLAZE_MEMORY_CAP:-4096}m" DB_RESOURCE_CACHE_SIZE: ${BLAZE_RESOURCE_CACHE_CAP:-2500000} - DB_BLOCK_CACHE_SIZE: $BLAZE_MEMORY_CAP + DB_BLOCK_CACHE_SIZE: ${BLAZE_MEMORY_CAP} + CQL_EXPR_CACHE_SIZE: ${BLAZE_CQL_CACHE_CAP:-32} ENFORCE_REFERENTIAL_INTEGRITY: "false" volumes: - "blaze-data:/app/data" @@ -31,6 +32,10 @@ services: BEAM_PROXY_URL: http://beam-proxy:8081 RETRY_COUNT: ${FOCUS_RETRY_COUNT} EPSILON: 0.28 + QUERIES_TO_CACHE: '/queries_to_cache.conf' + ENDPOINT_TYPE: ${FOCUS_ENDPOINT_TYPE:-blaze} + volumes: + - /srv/docker/bridgehead/cce/queries_to_cache.conf:/queries_to_cache.conf:ro depends_on: - "beam-proxy" - "blaze" diff --git a/cce/modules/exporter-compose.yml b/cce/modules/exporter-compose.yml new file mode 100644 index 00000000..af314659 --- /dev/null +++ b/cce/modules/exporter-compose.yml @@ -0,0 +1,87 @@ +version: "3.7" + +services: + + exporter: + image: docker.verbis.dkfz.de/ccp/dktk-exporter:latest + container_name: bridgehead-cce-exporter + environment: + JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" + LOG_LEVEL: "INFO" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" # Set in exporter-setup.sh + CROSS_ORIGINS: "https://${HOST}" + EXPORTER_DB_USER: "exporter" + EXPORTER_DB_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh + EXPORTER_DB_URL: "jdbc:postgresql://exporter-db:5432/exporter" + HTTP_RELATIVE_PATH: "/cce-exporter" + SITE: "${SITE_ID}" + HTTP_SERVLET_REQUEST_SCHEME: "https" + OPAL_PASSWORD: "${EXPORTER_OPAL_PASSWORD}" + labels: + - "traefik.enable=true" + - "traefik.http.routers.exporter_cce.rule=PathPrefix(`/cce-exporter`)" + - "traefik.http.services.exporter_cce.loadbalancer.server.port=8092" + - "traefik.http.routers.exporter_cce.tls=true" + - "traefik.http.middlewares.exporter_cce_strip.stripprefix.prefixes=/cce-exporter" + - "traefik.http.routers.exporter_cce.middlewares=exporter_cce_strip" + # Main router + - "traefik.http.routers.exporter_cce.priority=20" + + # API router + - "traefik.http.routers.exporter_cce_api.middlewares=exporter_cce_strip,exporter_auth" + - "traefik.http.routers.exporter_cce_api.rule=PathRegexp(`/cce-exporter/.+`)" + - "traefik.http.routers.exporter_cce_api.tls=true" + - "traefik.http.routers.exporter_cce_api.priority=25" + + # Shared middlewares + - "traefik.http.middlewares.exporter_auth.basicauth.users=${EXPORTER_USER}" + + volumes: + - "/var/cache/bridgehead/cce/exporter-files:/app/exporter-files/output" + + exporter-db: + image: docker.verbis.dkfz.de/cache/postgres:${POSTGRES_TAG} + container_name: bridgehead-cce-exporter-db + environment: + POSTGRES_USER: "exporter" + POSTGRES_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh + POSTGRES_DB: "exporter" + volumes: + # Consider removing this volume once we find a solution to save Lens-queries to be executed in the explorer. + - "/var/cache/bridgehead/cce/exporter-db:/var/lib/postgresql/data" + + reporter: + image: docker.verbis.dkfz.de/ccp/dktk-reporter:latest + container_name: bridgehead-cce-reporter + environment: + JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" + LOG_LEVEL: "INFO" + CROSS_ORIGINS: "https://${HOST}" + HTTP_RELATIVE_PATH: "/cce-reporter" + SITE: "${SITE_ID}" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" # Set in exporter-setup.sh + EXPORTER_URL: "http://exporter:8092" + LOG_FHIR_VALIDATION: "false" + HTTP_SERVLET_REQUEST_SCHEME: "https" + + # In this initial development state of the bridgehead, we are trying to have so many volumes as possible. + # However, in the first executions in the cce sites, this volume seems to be very important. A report is + # a process that can take several hours, because it depends on the exporter. + # There is a risk that the bridgehead restarts, losing the already created export. + + volumes: + - "/var/cache/bridgehead/cce/reporter-files:/app/reports" + labels: + - "traefik.enable=true" + - "traefik.http.routers.reporter_cce.rule=PathPrefix(`/cce-reporter`)" + - "traefik.http.services.reporter_cce.loadbalancer.server.port=8095" + - "traefik.http.routers.reporter_cce.tls=true" + - "traefik.http.middlewares.reporter_cce_strip.stripprefix.prefixes=/cce-reporter" + - "traefik.http.routers.reporter_cce.middlewares=reporter_cce_strip" + - "traefik.http.routers.reporter_cce.priority=20" + + - "traefik.http.routers.reporter_cce_api.middlewares=reporter_cce_strip,exporter_auth" + - "traefik.http.routers.reporter_cce_api.rule=PathRegexp(`/cce-reporter/.+`)" + - "traefik.http.routers.reporter_cce_api.tls=true" + - "traefik.http.routers.reporter_cce_api.priority=25" + diff --git a/cce/modules/exporter-setup.sh b/cce/modules/exporter-setup.sh new file mode 100644 index 00000000..9b947a60 --- /dev/null +++ b/cce/modules/exporter-setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +if [ "$ENABLE_EXPORTER" == true ]; then + log INFO "Exporter setup detected -- will start Exporter service." + OVERRIDE+=" -f ./$PROJECT/modules/exporter-compose.yml" + EXPORTER_DB_PASSWORD="$(echo \"This is a salt string to generate one consistent password for the exporter. It is not required to be secret.\" | sha1sum | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 30)" + EXPORTER_API_KEY="$(echo \"This is a salt string to generate one consistent API KEY for the exporter. It is not required to be secret.\" | sha1sum | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 64)" +fi diff --git a/cce/modules/lens-compose.yml b/cce/modules/lens-compose.yml index 12b95cea..59d707ca 100644 --- a/cce/modules/lens-compose.yml +++ b/cce/modules/lens-compose.yml @@ -1,33 +1,46 @@ version: "3.7" services: - landing: + lens: container_name: lens_federated-search - image: docker.verbis.dkfz.de/ccp/lens:${SITE_ID} + image: samply/cce-explorer:main + environment: + PUBLIC_SPOT_URL: https://${HOST}/prod labels: + - "traefik.http.services.lens.loadbalancer.server.port=3000" - "traefik.enable=true" - - "traefik.http.routers.landing.rule=PathPrefix(`/`)" - - "traefik.http.services.landing.loadbalancer.server.port=80" - - "traefik.http.routers.landing.tls=true" + - "traefik.http.routers.lens.rule=Host(`${HOST}`)" + - "traefik.http.routers.lens.tls=true" spot: - image: docker.verbis.dkfz.de/ccp-private/central-spot + image: samply/rustyspot:latest environment: + HTTP_PROXY: ${HTTP_PROXY_URL} + HTTPS_PROXY: ${HTTPS_PROXY_URL} + NO_PROXY: beam-proxy BEAM_SECRET: "${FOCUS_BEAM_SECRET_SHORT}" - BEAM_URL: http://beam-proxy:8081 - BEAM_PROXY_ID: ${SITE_ID} - BEAM_BROKER_ID: ${BROKER_ID} - BEAM_APP_ID: "focus" - PROJECT_METADATA: "cce_supervisors" + BEAM_PROXY_URL: http://beam-proxy:8081 + BEAM_APP_ID: "spot.${SITE_ID}.${BROKER_ID}" + CORS_ORIGIN: "https://${HOST}" + SITES: ${SITES} + TRANSFORM: LENS + PROJECT: cce + BIND_ADDR: 0.0.0.0:8055 depends_on: - "beam-proxy" labels: - "traefik.enable=true" - - "traefik.http.services.spot.loadbalancer.server.port=8080" + - "traefik.http.services.spot.loadbalancer.server.port=8055" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowmethods=GET,OPTIONS,POST" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowheaders=content-type" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolalloworiginlist=https://${HOST}" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowcredentials=true" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolmaxage=-1" - - "traefik.http.routers.spot.rule=Host(`${HOST}`) && PathPrefix(`/backend`)" - - "traefik.http.middlewares.stripprefix_spot.stripprefix.prefixes=/backend" + - "traefik.http.routers.spot.rule=Host(`${HOST}`) && PathPrefix(`/prod`)" + - "traefik.http.middlewares.stripprefix_spot.stripprefix.prefixes=/prod" - "traefik.http.routers.spot.tls=true" - - "traefik.http.routers.spot.middlewares=corsheaders2,stripprefix_spot" + - "traefik.http.routers.spot.middlewares=corsheaders2,stripprefix_spot,auth" + + beam-proxy: + environment: + APP_spot_KEY: ${FOCUS_BEAM_SECRET_SHORT} + \ No newline at end of file diff --git a/cce/modules/teiler-compose.yml b/cce/modules/teiler-compose.yml new file mode 100644 index 00000000..5bfcd2dc --- /dev/null +++ b/cce/modules/teiler-compose.yml @@ -0,0 +1,64 @@ +version: "3.7" + +services: + + teiler-orchestrator: + image: docker.verbis.dkfz.de/cache/samply/teiler-orchestrator:latest + container_name: bridgehead-teiler-orchestrator + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_orchestrator_cce.rule=PathPrefix(`/cce-teiler`)" + - "traefik.http.services.teiler_orchestrator_cce.loadbalancer.server.port=9000" + - "traefik.http.routers.teiler_orchestrator_cce.tls=true" + - "traefik.http.middlewares.teiler_orchestrator_cce_strip.stripprefix.prefixes=/cce-teiler" + - "traefik.http.routers.teiler_orchestrator_cce.middlewares=teiler_orchestrator_cce_strip" + environment: + TEILER_BACKEND_URL: "/cce-teiler-backend" + TEILER_DASHBOARD_URL: "/cce-teiler-dashboard" + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE_LOWER_CASE}" + HTTP_RELATIVE_PATH: "/cce-teiler" + + teiler-dashboard: + image: docker.verbis.dkfz.de/cache/samply/teiler-dashboard:${TEILER_DASHBOARD_TAG} + container_name: bridgehead-teiler-dashboard + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_dashboard_cce.rule=PathPrefix(`/cce-teiler-dashboard`)" + - "traefik.http.services.teiler_dashboard_cce.loadbalancer.server.port=80" + - "traefik.http.routers.teiler_dashboard_cce.tls=true" + - "traefik.http.middlewares.teiler_dashboard_cce_strip.stripprefix.prefixes=/cce-teiler-dashboard" + - "traefik.http.routers.teiler_dashboard_cce.middlewares=teiler_dashboard_cce_strip" + environment: + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" + TEILER_BACKEND_URL: "/cce-teiler-backend" + TEILER_DASHBOARD_URL: "/cce-teiler-dashboard" + TEILER_ADMIN_NAME: "${OPERATOR_FIRST_NAME} ${OPERATOR_LAST_NAME}" + TEILER_ADMIN_EMAIL: "${OPERATOR_EMAIL}" + TEILER_ADMIN_PHONE: "${OPERATOR_PHONE}" + TEILER_PROJECT: "${PROJECT}" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" + TEILER_ORCHESTRATOR_URL: "/cce-teiler" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/cce-teiler" + REPORTER_DEFAULT_TEMPLATE_ID: "cce-qb" + EXPORTER_DEFAULT_TEMPLATE_ID: "cce" + + + teiler-backend: + image: docker.verbis.dkfz.de/ccp/cce-teiler-backend:latest + container_name: bridgehead-teiler-backend + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_backend_cce.rule=PathPrefix(`/cce-teiler-backend`)" + - "traefik.http.services.teiler_backend_cce.loadbalancer.server.port=8085" + - "traefik.http.routers.teiler_backend_cce.tls=true" + - "traefik.http.middlewares.teiler_backend_cce_strip.stripprefix.prefixes=/cce-teiler-backend" + - "traefik.http.routers.teiler_backend_cce.middlewares=teiler_backend_cce_strip" + environment: + LOG_LEVEL: "INFO" + APPLICATION_PORT: "8085" + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/cce-teiler" + TEILER_ORCHESTRATOR_URL: "/cce-teiler" + TEILER_DASHBOARD_DE_URL: "/cce-teiler-dashboard/de" + TEILER_DASHBOARD_EN_URL: "/cce-teiler-dashboard/en" + HTTP_PROXY: "http://forward_proxy:3128" diff --git a/cce/modules/teiler-setup.sh b/cce/modules/teiler-setup.sh new file mode 100644 index 00000000..dc8dfc47 --- /dev/null +++ b/cce/modules/teiler-setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +if [ "$ENABLE_TEILER" == true ];then + log INFO "Teiler setup detected -- will start Teiler services." + OVERRIDE+=" -f ./$PROJECT/modules/teiler-compose.yml" + TEILER_DEFAULT_LANGUAGE=EN + TEILER_DEFAULT_LANGUAGE_LOWER_CASE=${TEILER_DEFAULT_LANGUAGE,,} +fi diff --git a/cce/queries_to_cache.conf b/cce/queries_to_cache.conf new file mode 100644 index 00000000..8606e7c0 --- /dev/null +++ b/cce/queries_to_cache.conf @@ -0,0 +1,2 @@ +bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwpjb2Rlc3lzdGVtIFNhbXBsZU1hdGVyaWFsVHlwZTogJ2h0dHBzOi8vZmhpci5iYm1yaS5kZS9Db2RlU3lzdGVtL1NhbXBsZU1hdGVyaWFsVHlwZScKCmNvZGVzeXN0ZW0gbG9pbmM6ICdodHRwOi8vbG9pbmMub3JnJwoKY29udGV4dCBQYXRpZW50CgpES1RLX1NUUkFUX0dFTkRFUl9TVFJBVElGSUVSCgpES1RLX1NUUkFUX0FHRV9TVFJBVElGSUVSCgpES1RLX1NUUkFUX0RFQ0VBU0VEX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfRElBR05PU0lTX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfU1BFQ0lNRU5fU1RSQVRJRklFUgoKREtUS19TVFJBVF9QUk9DRURVUkVfU1RSQVRJRklFUgoKREtUS19TVFJBVF9NRURJQ0FUSU9OX1NUUkFUSUZJRVIKREtUS19TVFJBVF9ERUZfSU5fSU5JVElBTF9QT1BVTEFUSU9OCnRydWU= +bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwpjb2Rlc3lzdGVtIFNhbXBsZU1hdGVyaWFsVHlwZTogJ2h0dHBzOi8vZmhpci5iYm1yaS5kZS9Db2RlU3lzdGVtL1NhbXBsZU1hdGVyaWFsVHlwZScKCmNvZGVzeXN0ZW0gbG9pbmM6ICdodHRwOi8vbG9pbmMub3JnJwpjb2Rlc3lzdGVtIGljZDEwOiAnaHR0cDovL2ZoaXIuZGUvQ29kZVN5c3RlbS9iZmFybS9pY2QtMTAtZ20nCmNvZGVzeXN0ZW0gbW9ycGg6ICd1cm46b2lkOjIuMTYuODQwLjEuMTEzODgzLjYuNDMuMScKCmNvbnRleHQgUGF0aWVudAoKREtUS19TVFJBVF9HRU5ERVJfU1RSQVRJRklFUgoKREtUS19TVFJBVF9BR0VfU1RSQVRJRklFUgoKREtUS19TVFJBVF9ERUNFQVNFRF9TVFJBVElGSUVSCgpES1RLX1NUUkFUX0RJQUdOT1NJU19TVFJBVElGSUVSCgpES1RLX1NUUkFUX1NQRUNJTUVOX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfUFJPQ0VEVVJFX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfTUVESUNBVElPTl9TVFJBVElGSUVSCkRLVEtfU1RSQVRfREVGX0lOX0lOSVRJQUxfUE9QVUxBVElPTgooKGV4aXN0cyBbQ29uZGl0aW9uOiBDb2RlICdDNjEnIGZyb20gaWNkMTBdKSBhbmQKKChleGlzdHMgZnJvbSBbT2JzZXJ2YXRpb246IENvZGUgJzU5ODQ3LTQnIGZyb20gbG9pbmNdIE8Kd2hlcmUgTy52YWx1ZS5jb2RpbmcuY29kZSBjb250YWlucyAnODE0MC8zJykgb3IKKGV4aXN0cyBmcm9tIFtPYnNlcnZhdGlvbjogQ29kZSAnNTk4NDctNCcgZnJvbSBsb2luY10gTwp3aGVyZSBPLnZhbHVlLmNvZGluZy5jb2RlIGNvbnRhaW5zICc4MTQ3LzMnKSBvcgooZXhpc3RzIGZyb20gW09ic2VydmF0aW9uOiBDb2RlICc1OTg0Ny00JyBmcm9tIGxvaW5jXSBPCndoZXJlIE8udmFsdWUuY29kaW5nLmNvZGUgY29udGFpbnMgJzg0ODAvMycpIG9yCihleGlzdHMgZnJvbSBbT2JzZXJ2YXRpb246IENvZGUgJzU5ODQ3LTQnIGZyb20gbG9pbmNdIE8Kd2hlcmUgTy52YWx1ZS5jb2RpbmcuY29kZSBjb250YWlucyAnODUwMC8zJykpKQ== diff --git a/cce/vars b/cce/vars index 7d0c1a3c..9338bd56 100644 --- a/cce/vars +++ b/cce/vars @@ -11,4 +11,4 @@ for module in $PROJECT/modules/*.sh do log DEBUG "sourcing $module" source $module -done +done \ No newline at end of file diff --git a/ccp/docker-compose.yml b/ccp/docker-compose.yml index 871eec2b..4fe0a681 100644 --- a/ccp/docker-compose.yml +++ b/ccp/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.31 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-ccp-blaze environment: BASE_URL: "http://bridgehead-ccp-blaze:8080" @@ -22,20 +22,21 @@ services: - "traefik.http.routers.blaze_ccp.tls=true" focus: - image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG}-dktk + image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG} container_name: bridgehead-focus environment: - API_KEY: ${FOCUS_BEAM_SECRET_SHORT} - BEAM_APP_ID_LONG: focus.${PROXY_ID} - PROXY_ID: ${PROXY_ID} - BLAZE_URL: "http://bridgehead-ccp-blaze:8080/fhir/" - BEAM_PROXY_URL: http://beam-proxy:8081 - RETRY_COUNT: ${FOCUS_RETRY_COUNT} - EPSILON: 0.28 - QUERIES_TO_CACHE: '/queries_to_cache.conf' - ENDPOINT_TYPE: ${FOCUS_ENDPOINT_TYPE:-blaze} + - API_KEY=${FOCUS_BEAM_SECRET_SHORT} + - BEAM_APP_ID_LONG=focus.${PROXY_ID} + - PROXY_ID=${PROXY_ID} + - BLAZE_URL=http://bridgehead-ccp-blaze:8080/fhir/ + - BEAM_PROXY_URL=http://beam-proxy:8081 + - RETRY_COUNT=${FOCUS_RETRY_COUNT} + - EPSILON=0.28 + - QUERIES_TO_CACHE=/queries_to_cache.conf + - ENDPOINT_TYPE=${FOCUS_ENDPOINT_TYPE:-blaze} + - CQL_PROJECTS_ENABLED volumes: - - /srv/docker/bridgehead/ccp/queries_to_cache.conf:/queries_to_cache.conf + - /srv/docker/bridgehead/ccp/queries_to_cache.conf:/queries_to_cache.conf:ro depends_on: - "beam-proxy" - "blaze" diff --git a/ccp/modules/blaze-secondary-compose.yml b/ccp/modules/blaze-secondary-compose.yml index ad748a66..c60ebf57 100644 --- a/ccp/modules/blaze-secondary-compose.yml +++ b/ccp/modules/blaze-secondary-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: blaze-secondary: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.31 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-ccp-blaze-secondary environment: BASE_URL: "http://bridgehead-ccp-blaze-secondary:8080" diff --git a/ccp/modules/datashield-compose.yml b/ccp/modules/datashield-compose.yml index 404cda96..2e8c945f 100644 --- a/ccp/modules/datashield-compose.yml +++ b/ccp/modules/datashield-compose.yml @@ -1,25 +1,6 @@ version: "3.7" services: - rstudio: - container_name: bridgehead-rstudio - image: docker.verbis.dkfz.de/ccp/dktk-rstudio:latest - environment: - #DEFAULT_USER: "rstudio" # This line is kept for informational purposes - PASSWORD: "${RSTUDIO_ADMIN_PASSWORD}" # It is required, even if the authentication is disabled - DISABLE_AUTH: "true" # https://rocker-project.org/images/versioned/rstudio.html#how-to-use - HTTP_RELATIVE_PATH: "/rstudio" - ALL_PROXY: "http://forward_proxy:3128" # https://rocker-project.org/use/networking.html - labels: - - "traefik.enable=true" - - "traefik.http.routers.rstudio_ccp.rule=PathPrefix(`/rstudio`)" - - "traefik.http.services.rstudio_ccp.loadbalancer.server.port=8787" - - "traefik.http.middlewares.rstudio_ccp_strip.stripprefix.prefixes=/rstudio" - - "traefik.http.routers.rstudio_ccp.tls=true" - - "traefik.http.routers.rstudio_ccp.middlewares=oidcAuth,rstudio_ccp_strip" - networks: - - rstudio - opal: container_name: bridgehead-opal image: docker.verbis.dkfz.de/ccp/dktk-opal:latest @@ -44,8 +25,7 @@ services: APP_CONTEXT_PATH: "/opal" OPAL_PRIVATE_KEY: "/run/secrets/opal-key.pem" OPAL_CERTIFICATE: "/run/secrets/opal-cert.pem" - OIDC_URL: "${OIDC_URL}" - OIDC_REALM: "${OIDC_REALM}" + OIDC_URL: "${OIDC_PRIVATE_URL}" OIDC_CLIENT_ID: "${OIDC_PRIVATE_CLIENT_ID}" OIDC_CLIENT_SECRET: "${OIDC_CLIENT_SECRET}" OIDC_ADMIN_GROUP: "${OIDC_ADMIN_GROUP}" @@ -93,79 +73,14 @@ services: - beam-proxy volumes: - /tmp/bridgehead/opal-map/:/map/:ro - networks: - - default - - rstudio - - traefik: - labels: - - "traefik.http.middlewares.oidcAuth.forwardAuth.address=http://oauth2-proxy:4180/" - - "traefik.http.middlewares.oidcAuth.forwardAuth.trustForwardHeader=true" - - "traefik.http.middlewares.oidcAuth.forwardAuth.authResponseHeaders=X-Auth-Request-Access-Token,Authorization" - networks: - - default - - rstudio - forward_proxy: - networks: - - default - - rstudio beam-proxy: environment: APP_datashield-connect_KEY: ${DATASHIELD_CONNECT_SECRET} APP_token-manager_KEY: ${TOKEN_MANAGER_SECRET} - # TODO: Allow users of group /DataSHIELD and OIDC_USER_GROUP at the same time: - # Maybe a solution would be (https://oauth2-proxy.github.io/oauth2-proxy/configuration/oauth_provider): - # --allowed-groups=/DataSHIELD,OIDC_USER_GROUP - oauth2-proxy: - image: docker.verbis.dkfz.de/cache/oauth2-proxy/oauth2-proxy:latest - container_name: bridgehead-oauth2proxy - command: >- - --allowed-group=DataSHIELD - --oidc-groups-claim=${OIDC_GROUP_CLAIM} - --auth-logging=true - --whitelist-domain=${HOST} - --http-address="0.0.0.0:4180" - --reverse-proxy=true - --upstream="static://202" - --email-domain="*" - --cookie-name="_BRIDGEHEAD_oauth2" - --cookie-secret="${OAUTH2_PROXY_SECRET}" - --cookie-expire="12h" - --cookie-secure="true" - --cookie-httponly="true" - #OIDC settings - --provider="keycloak-oidc" - --provider-display-name="VerbIS Login" - --client-id="${OIDC_PRIVATE_CLIENT_ID}" - --client-secret="${OIDC_CLIENT_SECRET}" - --redirect-url="https://${HOST}${OAUTH2_CALLBACK}" - --oidc-issuer-url="${OIDC_ISSUER_URL}" - --scope="openid email profile" - --code-challenge-method="S256" - --skip-provider-button=true - #X-Forwarded-Header settings - true/false depending on your needs - --pass-basic-auth=true - --pass-user-headers=false - --pass-access-token=false - labels: - - "traefik.enable=true" - - "traefik.http.routers.oauth2_proxy.rule=PathPrefix(`/oauth2`)" - - "traefik.http.services.oauth2_proxy.loadbalancer.server.port=4180" - - "traefik.http.routers.oauth2_proxy.tls=true" - environment: - http_proxy: "http://forward_proxy:3128" - https_proxy: "http://forward_proxy:3128" - depends_on: - forward_proxy: - condition: service_healthy - secrets: opal-cert.pem: file: /tmp/bridgehead/opal-cert.pem opal-key.pem: file: /tmp/bridgehead/opal-key.pem - -networks: - rstudio: diff --git a/ccp/modules/datashield-setup.sh b/ccp/modules/datashield-setup.sh index 9692fb9f..e5f7f611 100644 --- a/ccp/modules/datashield-setup.sh +++ b/ccp/modules/datashield-setup.sh @@ -5,17 +5,12 @@ if [ "$ENABLE_DATASHIELD" == true ]; then if [ -z "${ENABLE_EXPORTER}" ] || [ "${ENABLE_EXPORTER}" != "true" ]; then log WARN "The ENABLE_EXPORTER variable is either not set or not set to 'true'." fi - OAUTH2_CALLBACK=/oauth2/callback - OAUTH2_PROXY_SECRET="$(echo \"This is a salt string to generate one consistent encryption key for the oauth2_proxy. It is not required to be secret.\" | sha1sum | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 32)" - add_private_oidc_redirect_url "${OAUTH2_CALLBACK}" - log INFO "DataSHIELD setup detected -- will start DataSHIELD services." OVERRIDE+=" -f ./$PROJECT/modules/datashield-compose.yml" EXPORTER_OPAL_PASSWORD="$(generate_password \"exporter in Opal\")" TOKEN_MANAGER_OPAL_PASSWORD="$(generate_password \"Token Manager in Opal\")" OPAL_DB_PASSWORD="$(echo \"Opal DB\" | generate_simple_password)" OPAL_ADMIN_PASSWORD="$(generate_password \"admin password for Opal\")" - RSTUDIO_ADMIN_PASSWORD="$(generate_password \"admin password for R-Studio\")" DATASHIELD_CONNECT_SECRET="$(echo \"DataShield Connect\" | generate_simple_password)" TOKEN_MANAGER_SECRET="$(echo \"Token Manager\" | generate_simple_password)" if [ ! -e /tmp/bridgehead/opal-cert.pem ]; then @@ -23,18 +18,13 @@ if [ "$ENABLE_DATASHIELD" == true ]; then openssl req -x509 -newkey rsa:4096 -nodes -keyout /tmp/bridgehead/opal-key.pem -out /tmp/bridgehead/opal-cert.pem -days 3650 -subj "/CN=opal/C=DE" fi mkdir -p /tmp/bridgehead/opal-map - sites="$(cat ./$PROJECT/modules/datashield-sites.json)" - echo "$sites" | docker_jq -n --args '{"sites": input | map({ - "name": ., - "id": ., - "virtualhost": "\(.):443", - "beamconnect": "datashield-connect.\(.).'"$BROKER_ID"'" - })}' $sites >/tmp/bridgehead/opal-map/central.json - echo "$sites" | docker_jq -n --args '[{ - "external": "'"$SITE_ID"':443", + echo '{"sites": []}' >/tmp/bridgehead/opal-map/central.json + # Only allow connections from the central beam proxy that is used by all coder workspaces + echo '[{ + "external": "'$SITE_ID':443", "internal": "opal:8443", - "allowed": input | map("\(.).'"$BROKER_ID"'") - }]' >/tmp/bridgehead/opal-map/local.json + "allowed": ["central-ds-orchestrator.'$BROKER_ID'"] + }]' > /tmp/bridgehead/opal-map/local.json if [ "$USER" == "root" ]; then chown -R bridgehead:docker /tmp/bridgehead chmod g+wr /tmp/bridgehead/opal-map/* diff --git a/ccp/modules/datashield-sites.json b/ccp/modules/datashield-sites.json deleted file mode 100644 index 600534d8..00000000 --- a/ccp/modules/datashield-sites.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - "berlin", - "muenchen-lmu", - "dresden", - "freiburg", - "muenchen-tum", - "tuebingen", - "mainz", - "frankfurt", - "essen", - "dktk-datashield-test", - "dktk-test", - "mannheim", - "central-ds-orchestrator" -] diff --git a/ccp/modules/dnpm-node-compose.yml b/ccp/modules/dnpm-node-compose.yml index c1f7ddef..0a7bcd31 100644 --- a/ccp/modules/dnpm-node-compose.yml +++ b/ccp/modules/dnpm-node-compose.yml @@ -43,7 +43,7 @@ services: - "traefik.http.routers.dnpm-auth.tls=true" dnpm-portal: - image: ghcr.io/dnpm-dip/portal:latest + image: ghcr.io/dnpm-dip/portal:${DNPM_IMAGE_TAG:-latest} container_name: bridgehead-dnpm-portal environment: - NUXT_API_URL=http://dnpm-backend:9000/ @@ -58,7 +58,7 @@ services: dnpm-backend: container_name: bridgehead-dnpm-backend - image: ghcr.io/dnpm-dip/backend:latest + image: ghcr.io/dnpm-dip/api-gateway:latest environment: - LOCAL_SITE=${ZPM_SITE}:${SITE_NAME} # Format: {Site-ID}:{Site-name}, e.g. UKT:Tübingen - RD_RANDOM_DATA=${DNPM_SYNTH_NUM:--1} @@ -66,6 +66,7 @@ services: - HATEOAS_HOST=https://${HOST} - CONNECTOR_TYPE=broker - AUTHUP_URL=robot://system:${DNPM_AUTHUP_SECRET}@http://dnpm-authup:3000 + - TZ=Europe/Berlin volumes: - /etc/bridgehead/dnpm/config:/dnpm_config - /var/cache/bridgehead/dnpm/backend-data:/dnpm_data diff --git a/ccp/modules/exporter.md b/ccp/modules/exporter.md deleted file mode 100644 index 24e81b05..00000000 --- a/ccp/modules/exporter.md +++ /dev/null @@ -1,15 +0,0 @@ -# Exporter and Reporter - - -## Exporter -The exporter is a REST API that exports the data of the different databases of the bridgehead in a set of tables. -It can accept different output formats as CSV, Excel, JSON or XML. It can also export data into Opal. - -## Exporter-DB -It is a database to save queries for its execution in the exporter. -The exporter manages also the different executions of the same query in through the database. - -## Reporter -This component is a plugin of the exporter that allows to create more complex Excel reports described in templates. -It is compatible with different template engines as Groovy, Thymeleaf,... -It is perfect to generate a document as our traditional CCP quality report. diff --git a/ccp/modules/id-management-compose.yml b/ccp/modules/id-management-compose.yml index 4e3e90a9..86c6a965 100644 --- a/ccp/modules/id-management-compose.yml +++ b/ccp/modules/id-management-compose.yml @@ -14,6 +14,7 @@ services: MAGICPL_CONNECTOR_APIKEY: ${IDMANAGER_READ_APIKEY} MAGICPL_CENTRAL_PATIENTLIST_APIKEY: ${IDMANAGER_CENTRAL_PATIENTLIST_APIKEY} MAGICPL_CONTROLNUMBERGENERATOR_APIKEY: ${IDMANAGER_CONTROLNUMBERGENERATOR_APIKEY} + MAGICPL_OIDC_PROVIDER: ${OIDC_PRIVATE_URL} depends_on: - patientlist - traefik-forward-auth @@ -71,12 +72,14 @@ services: - https_proxy=http://forward_proxy:3128 - OAUTH2_PROXY_PROVIDER=oidc - OAUTH2_PROXY_SKIP_PROVIDER_BUTTON=true - - OAUTH2_PROXY_OIDC_ISSUER_URL=https://login.verbis.dkfz.de/realms/master - - OAUTH2_PROXY_CLIENT_ID=bridgehead-${SITE_ID} - - OAUTH2_PROXY_CLIENT_SECRET=${IDMANAGER_AUTH_CLIENT_SECRET} + - OAUTH2_PROXY_OIDC_ISSUER_URL=${OIDC_PRIVATE_URL} + - OAUTH2_PROXY_CLIENT_ID=${OIDC_PRIVATE_CLIENT_ID} + - OAUTH2_PROXY_CLIENT_SECRET=${OIDC_CLIENT_SECRET} - OAUTH2_PROXY_COOKIE_SECRET=${IDMANAGER_AUTH_COOKIE_SECRET} - OAUTH2_PROXY_COOKIE_NAME=_BRIDGEHEAD_oauth2_idm - OAUTH2_PROXY_COOKIE_DOMAINS=.${HOST} + - OAUTH2_PROXY_COOKIE_REFRESH=4m + - OAUTH2_PROXY_COOKIE_EXPIRE=24h - OAUTH2_PROXY_HTTP_ADDRESS=:4180 - OAUTH2_PROXY_REVERSE_PROXY=true - OAUTH2_PROXY_WHITELIST_DOMAINS=.${HOST} @@ -87,8 +90,8 @@ services: - OAUTH2_PROXY_SET_AUTHORIZATION_HEADER=true - OAUTH2_PROXY_SET_XAUTHREQUEST=true # Keycloak has an expiration time of 60s therefore oauth2-proxy needs to refresh after that - - OAUTH2_PROXY_COOKIE_REFRESH=60s - - OAUTH2_PROXY_ALLOWED_GROUPS=DKTK-CCP-PPSN + - OAUTH2_PROXY_ALLOWED_GROUPS=${OIDC_PSP_GROUP} + - OAUTH2_PROXY_OIDC_GROUPS_CLAIM=${OIDC_GROUP_CLAIM} - OAUTH2_PROXY_PROXY_PREFIX=/oauth2-idm labels: - "traefik.enable=true" diff --git a/ccp/modules/id-management-setup.sh b/ccp/modules/id-management-setup.sh index 333b5125..a7644591 100644 --- a/ccp/modules/id-management-setup.sh +++ b/ccp/modules/id-management-setup.sh @@ -14,6 +14,8 @@ function idManagementSetup() { # Ensure old ids are working !!! export IDMANAGEMENT_FRIENDLY_ID=$(legacyIdMapping "$SITE_ID") + + add_private_oidc_redirect_url "/oauth2-idm/callback" fi } diff --git a/ccp/modules/mtba-compose.yml b/ccp/modules/mtba-compose.yml index 56bb015a..6bf6dc44 100644 --- a/ccp/modules/mtba-compose.yml +++ b/ccp/modules/mtba-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: mtba: - image: docker.verbis.dkfz.de/cache/samply/mtba:develop + image: docker.verbis.dkfz.de/cache/samply/mtba:${MTBA_TAG} container_name: bridgehead-mtba environment: BLAZE_STORE_URL: http://blaze:8080 @@ -22,9 +22,14 @@ services: HTTP_RELATIVE_PATH: "/mtba" OIDC_ADMIN_GROUP: "${OIDC_ADMIN_GROUP}" OIDC_CLIENT_ID: "${OIDC_PRIVATE_CLIENT_ID}" - OIDC_CLIENT_SECRET: "${OIDC_CLIENT_SECRET}" - OIDC_REALM: "${OIDC_REALM}" - OIDC_URL: "${OIDC_URL}" + # TODO: Add following variables after moving to Authentik: + #OIDC_CLIENT_SECRET: "${OIDC_CLIENT_SECRET}" + #OIDC_URL: "${OIDC_URL}" + # TODO: Remove following variables after moving to Authentik: + # Please add KECLOAK_CLIENT_SECRET in ccp.conf + OIDC_CLIENT_SECRET: "${KEYCLOAK_CLIENT_SECRET}" + OIDC_URL: "https://login.verbis.dkfz.de/realms/test-realm-01" + OIDC_ADMIN_URL: "https://login.verbis.dkfz.de/admin/realms/test-realm-01" labels: - "traefik.enable=true" diff --git a/ccp/modules/obds2fhir-rest-compose.yml b/ccp/modules/obds2fhir-rest-compose.yml index 833580d1..ec1737c8 100644 --- a/ccp/modules/obds2fhir-rest-compose.yml +++ b/ccp/modules/obds2fhir-rest-compose.yml @@ -3,7 +3,7 @@ version: "3.7" services: obds2fhir-rest: container_name: bridgehead-obds2fhir-rest - image: docker.verbis.dkfz.de/ccp/obds2fhir-rest:main + image: docker.verbis.dkfz.de/samply/obds2fhir-rest:main environment: IDTYPE: BK_${IDMANAGEMENT_FRIENDLY_ID}_L-ID MAINZELLISTE_APIKEY: ${IDMANAGER_LOCAL_PATIENTLIST_APIKEY} diff --git a/ccp/modules/teiler-compose.yml b/ccp/modules/teiler-compose.yml index f415ee97..b6fc4c6a 100644 --- a/ccp/modules/teiler-compose.yml +++ b/ccp/modules/teiler-compose.yml @@ -13,13 +13,13 @@ services: - "traefik.http.middlewares.teiler_orchestrator_ccp_strip.stripprefix.prefixes=/ccp-teiler" - "traefik.http.routers.teiler_orchestrator_ccp.middlewares=teiler_orchestrator_ccp_strip" environment: - TEILER_BACKEND_URL: "https://${HOST}/ccp-teiler-backend" - TEILER_DASHBOARD_URL: "https://${HOST}/ccp-teiler-dashboard" + TEILER_BACKEND_URL: "/ccp-teiler-backend" + TEILER_DASHBOARD_URL: "/ccp-teiler-dashboard" DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE_LOWER_CASE}" HTTP_RELATIVE_PATH: "/ccp-teiler" teiler-dashboard: - image: docker.verbis.dkfz.de/cache/samply/teiler-dashboard:develop + image: docker.verbis.dkfz.de/cache/samply/teiler-dashboard:${TEILER_DASHBOARD_TAG} container_name: bridgehead-teiler-dashboard labels: - "traefik.enable=true" @@ -30,9 +30,9 @@ services: - "traefik.http.routers.teiler_dashboard_ccp.middlewares=teiler_dashboard_ccp_strip" environment: DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" - TEILER_BACKEND_URL: "https://${HOST}/ccp-teiler-backend" + TEILER_BACKEND_URL: "/ccp-teiler-backend" + TEILER_DASHBOARD_URL: "/ccp-teiler-dashboard" OIDC_URL: "${OIDC_URL}" - OIDC_REALM: "${OIDC_REALM}" OIDC_CLIENT_ID: "${OIDC_PUBLIC_CLIENT_ID}" OIDC_TOKEN_GROUP: "${OIDC_GROUP_CLAIM}" TEILER_ADMIN_NAME: "${OPERATOR_FIRST_NAME} ${OPERATOR_LAST_NAME}" @@ -40,8 +40,7 @@ services: TEILER_ADMIN_PHONE: "${OPERATOR_PHONE}" TEILER_PROJECT: "${PROJECT}" EXPORTER_API_KEY: "${EXPORTER_API_KEY}" - TEILER_ORCHESTRATOR_URL: "https://${HOST}/ccp-teiler" - TEILER_DASHBOARD_HTTP_RELATIVE_PATH: "/ccp-teiler-dashboard" + TEILER_ORCHESTRATOR_URL: "/ccp-teiler" TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/ccp-teiler" TEILER_USER: "${OIDC_USER_GROUP}" TEILER_ADMIN: "${OIDC_ADMIN_GROUP}" @@ -62,20 +61,12 @@ services: environment: LOG_LEVEL: "INFO" APPLICATION_PORT: "8085" - APPLICATION_ADDRESS: "${HOST}" DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" - CONFIG_ENV_VAR_PATH: "/run/secrets/ccp.conf" TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/ccp-teiler" - TEILER_ORCHESTRATOR_URL: "https://${HOST}/ccp-teiler" - TEILER_DASHBOARD_DE_URL: "https://${HOST}/ccp-teiler-dashboard/de" - TEILER_DASHBOARD_EN_URL: "https://${HOST}/ccp-teiler-dashboard/en" - CENTRAX_URL: "${CENTRAXX_URL}" + TEILER_ORCHESTRATOR_URL: "/ccp-teiler" + TEILER_DASHBOARD_DE_URL: "/ccp-teiler-dashboard/de" + TEILER_DASHBOARD_EN_URL: "/ccp-teiler-dashboard/en" HTTP_PROXY: "http://forward_proxy:3128" ENABLE_MTBA: "${ENABLE_MTBA}" ENABLE_DATASHIELD: "${ENABLE_DATASHIELD}" - secrets: - - ccp.conf - -secrets: - ccp.conf: - file: /etc/bridgehead/ccp.conf + IDMANAGER_UPLOAD_APIKEY: "${IDMANAGER_UPLOAD_APIKEY}" # Only used to check if the ID Manager is active diff --git a/ccp/modules/teiler.md b/ccp/modules/teiler.md deleted file mode 100644 index 51e94e46..00000000 --- a/ccp/modules/teiler.md +++ /dev/null @@ -1,19 +0,0 @@ -# Teiler -This module orchestrates the different microfrontends of the bridgehead as a single page application. - -## Teiler Orchestrator -Single SPA component that consists on the root HTML site of the single page application and a javascript code that -gets the information about the microfrontend calling the teiler backend and is responsible for registering them. With the -resulting mapping, it can initialize, mount and unmount the required microfrontends on the fly. - -The microfrontends run independently in different containers and can be based on different frameworks (Angular, Vue, React,...) -This microfrontends can run as single alone but need an extension with Single-SPA (https://single-spa.js.org/docs/ecosystem). -There are also available three templates (Angular, Vue, React) to be directly extended to be used directly in the teiler. - -## Teiler Dashboard -It consists on the main dashboard and a set of embedded services. -### Login -user and password in ccp.local.conf - -## Teiler Backend -In this component, the microfrontends are configured. diff --git a/ccp/queries_to_cache.conf b/ccp/queries_to_cache.conf index b9503125..24ccd956 100644 --- a/ccp/queries_to_cache.conf +++ b/ccp/queries_to_cache.conf @@ -1,2 +1,3 @@ -bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwoKY29kZXN5c3RlbSBsb2luYzogJ2h0dHA6Ly9sb2luYy5vcmcnCgpjb250ZXh0IFBhdGllbnQKCgpES1RLX1NUUkFUX0dFTkRFUl9TVFJBVElGSUVSCgpES1RLX1NUUkFUX1BSSU1BUllfRElBR05PU0lTX05PX1NPUlRfU1RSQVRJRklFUgpES1RLX1NUUkFUX0FHRV9DTEFTU19TVFJBVElGSUVSCgpES1RLX1NUUkFUX0RFQ0VBU0VEX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfRElBR05PU0lTX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfU1BFQ0lNRU5fU1RSQVRJRklFUgoKREtUS19TVFJBVF9QUk9DRURVUkVfU1RSQVRJRklFUgoKREtUS19TVFJBVF9NRURJQ0FUSU9OX1NUUkFUSUZJRVIKCiAgREtUS19TVFJBVF9ISVNUT0xPR1lfU1RSQVRJRklFUgpES1RLX1NUUkFUX0RFRl9JTl9JTklUSUFMX1BPUFVMQVRJT04KdHJ1ZQ== -bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwoKY29kZXN5c3RlbSBsb2luYzogJ2h0dHA6Ly9sb2luYy5vcmcnCmNvZGVzeXN0ZW0gaWNkMTA6ICdodHRwOi8vZmhpci5kZS9Db2RlU3lzdGVtL2JmYXJtL2ljZC0xMC1nbScKY29kZXN5c3RlbSBtb3JwaDogJ3VybjpvaWQ6Mi4xNi44NDAuMS4xMTM4ODMuNi40My4xJwoKY29udGV4dCBQYXRpZW50CgoKREtUS19TVFJBVF9HRU5ERVJfU1RSQVRJRklFUgoKREtUS19TVFJBVF9QUklNQVJZX0RJQUdOT1NJU19OT19TT1JUX1NUUkFUSUZJRVIKREtUS19TVFJBVF9BR0VfQ0xBU1NfU1RSQVRJRklFUgoKREtUS19TVFJBVF9ERUNFQVNFRF9TVFJBVElGSUVSCgpES1RLX1NUUkFUX0RJQUdOT1NJU19TVFJBVElGSUVSCgpES1RLX1NUUkFUX1NQRUNJTUVOX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfUFJPQ0VEVVJFX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfTUVESUNBVElPTl9TVFJBVElGSUVSCgogIERLVEtfU1RSQVRfSElTVE9MT0dZX1NUUkFUSUZJRVIKREtUS19TVFJBVF9ERUZfSU5fSU5JVElBTF9QT1BVTEFUSU9OKGV4aXN0cyBbQ29uZGl0aW9uOiBDb2RlICdDNjEnIGZyb20gaWNkMTBdKSBhbmQgCigoZXhpc3RzIGZyb20gW09ic2VydmF0aW9uOiBDb2RlICc1OTg0Ny00JyBmcm9tIGxvaW5jXSBPCndoZXJlIE8udmFsdWUuY29kaW5nLmNvZGUgY29udGFpbnMgJzgxNDAvMycpIG9yIAooZXhpc3RzIGZyb20gW09ic2VydmF0aW9uOiBDb2RlICc1OTg0Ny00JyBmcm9tIGxvaW5jXSBPCndoZXJlIE8udmFsdWUuY29kaW5nLmNvZGUgY29udGFpbnMgJzgxNDcvMycpIG9yIAooZXhpc3RzIGZyb20gW09ic2VydmF0aW9uOiBDb2RlICc1OTg0Ny00JyBmcm9tIGxvaW5jXSBPCndoZXJlIE8udmFsdWUuY29kaW5nLmNvZGUgY29udGFpbnMgJzg0ODAvMycpIG9yIAooZXhpc3RzIGZyb20gW09ic2VydmF0aW9uOiBDb2RlICc1OTg0Ny00JyBmcm9tIGxvaW5jXSBPCndoZXJlIE8udmFsdWUuY29kaW5nLmNvZGUgY29udGFpbnMgJzg1MDAvMycpKQ== \ No newline at end of file +bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwoKY29kZXN5c3RlbSBsb2luYzogJ2h0dHA6Ly9sb2luYy5vcmcnCgpjb250ZXh0IFBhdGllbnQKCgpES1RLX1NUUkFUX0dFTkRFUl9TVFJBVElGSUVSCgpES1RLX1NUUkFUX1BSSU1BUllfRElBR05PU0lTX05PX1NPUlRfU1RSQVRJRklFUgpES1RLX1NUUkFUX0FHRV9DTEFTU19TVFJBVElGSUVSCgpES1RLX1NUUkFUX0RFQ0VBU0VEX1NUUkFUSUZJRVIKCkRLVEtfU1RSQVRfRElBR05PU0lTX1NUUkFUSUZJRVIKCkRLVEtfUkVQTEFDRV9TUEVDSU1FTl9TVFJBVElGSUVSaWYgSW5Jbml0aWFsUG9wdWxhdGlvbiB0aGVuIFtTcGVjaW1lbl0gZWxzZSB7fSBhcyBMaXN0PFNwZWNpbWVuPgpES1RLX1NUUkFUX1BST0NFRFVSRV9TVFJBVElGSUVSCgpES1RLX1NUUkFUX01FRElDQVRJT05fU1RSQVRJRklFUgoKICBES1RLX1JFUExBQ0VfSElTVE9MT0dZX1NUUkFUSUZJRVIKIGlmIGhpc3RvLmNvZGUuY29kaW5nLndoZXJlKGNvZGUgPSAnNTk4NDctNCcpLmNvZGUuZmlyc3QoKSBpcyBudWxsIHRoZW4gMCBlbHNlIDEKREtUS19TVFJBVF9ERUZfSU5fSU5JVElBTF9QT1BVTEFUSU9OCnRydWU= +bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwoKY29kZXN5c3RlbSBsb2luYzogJ2h0dHA6Ly9sb2luYy5vcmcnCmNvZGVzeXN0ZW0gaWNkMTA6ICdodHRwOi8vZmhpci5kZS9Db2RlU3lzdGVtL2JmYXJtL2ljZC0xMC1nbScKY29kZXN5c3RlbSBtb3JwaDogJ3VybjpvaWQ6Mi4xNi44NDAuMS4xMTM4ODMuNi40My4xJwoKY29udGV4dCBQYXRpZW50CgoKREtUS19TVFJBVF9HRU5ERVJfU1RSQVRJRklFUgoKREtUS19TVFJBVF9QUklNQVJZX0RJQUdOT1NJU19OT19TT1JUX1NUUkFUSUZJRVIKREtUS19TVFJBVF9BR0VfQ0xBU1NfU1RSQVRJRklFUgoKREtUS19TVFJBVF9ERUNFQVNFRF9TVFJBVElGSUVSCgpES1RLX1NUUkFUX0RJQUdOT1NJU19TVFJBVElGSUVSCgpES1RLX1JFUExBQ0VfU1BFQ0lNRU5fU1RSQVRJRklFUmlmIEluSW5pdGlhbFBvcHVsYXRpb24gdGhlbiBbU3BlY2ltZW5dIGVsc2Uge30gYXMgTGlzdDxTcGVjaW1lbj4KREtUS19TVFJBVF9QUk9DRURVUkVfU1RSQVRJRklFUgoKREtUS19TVFJBVF9NRURJQ0FUSU9OX1NUUkFUSUZJRVIKCiAgREtUS19SRVBMQUNFX0hJU1RPTE9HWV9TVFJBVElGSUVSCiBpZiBoaXN0by5jb2RlLmNvZGluZy53aGVyZShjb2RlID0gJzU5ODQ3LTQnKS5jb2RlLmZpcnN0KCkgaXMgbnVsbCB0aGVuIDAgZWxzZSAxCkRLVEtfU1RSQVRfREVGX0lOX0lOSVRJQUxfUE9QVUxBVElPTihleGlzdHMgW0NvbmRpdGlvbjogQ29kZSAnQzYxJyBmcm9tIGljZDEwXSkgYW5kIAooKGV4aXN0cyBmcm9tIFtPYnNlcnZhdGlvbjogQ29kZSAnNTk4NDctNCcgZnJvbSBsb2luY10gTwp3aGVyZSBPLnZhbHVlLmNvZGluZy5jb2RlIGNvbnRhaW5zICc4MTQwLzMnKSBvciAKKGV4aXN0cyBmcm9tIFtPYnNlcnZhdGlvbjogQ29kZSAnNTk4NDctNCcgZnJvbSBsb2luY10gTwp3aGVyZSBPLnZhbHVlLmNvZGluZy5jb2RlIGNvbnRhaW5zICc4MTQ3LzMnKSBvciAKKGV4aXN0cyBmcm9tIFtPYnNlcnZhdGlvbjogQ29kZSAnNTk4NDctNCcgZnJvbSBsb2luY10gTwp3aGVyZSBPLnZhbHVlLmNvZGluZy5jb2RlIGNvbnRhaW5zICc4NDgwLzMnKSBvciAKKGV4aXN0cyBmcm9tIFtPYnNlcnZhdGlvbjogQ29kZSAnNTk4NDctNCcgZnJvbSBsb2luY10gTwp3aGVyZSBPLnZhbHVlLmNvZGluZy5jb2RlIGNvbnRhaW5zICc4NTAwLzMnKSk= +ORGANOID_DASHBOARD_PUBLIC diff --git a/ccp/vars b/ccp/vars index 0900914b..2377b836 100644 --- a/ccp/vars +++ b/ccp/vars @@ -10,16 +10,13 @@ BROKER_URL_FOR_PREREQ=$BROKER_URL OIDC_USER_GROUP="DKTK_CCP_$(capitalize_first_letter ${SITE_ID})" OIDC_ADMIN_GROUP="DKTK_CCP_$(capitalize_first_letter ${SITE_ID})_Verwalter" +OIDC_PSP_GROUP="DKTK_CCP_$(capitalize_first_letter ${SITE_ID})_PSP" OIDC_PRIVATE_CLIENT_ID=${SITE_ID}-private OIDC_PUBLIC_CLIENT_ID=${SITE_ID}-public -# Use "test-realm-01" for testing -OIDC_REALM="${OIDC_REALM:-master}" -OIDC_URL="https://login.verbis.dkfz.de" -OIDC_ISSUER_URL="${OIDC_URL}/realms/${OIDC_REALM}" +OIDC_URL="https://sso.verbis.dkfz.de/application/o/${OIDC_PUBLIC_CLIENT_ID}/" +OIDC_PRIVATE_URL="https://sso.verbis.dkfz.de/application/o/${OIDC_PRIVATE_CLIENT_ID}/" OIDC_GROUP_CLAIM="groups" -POSTGRES_TAG=15.6-alpine - for module in $PROJECT/modules/*.sh do log DEBUG "sourcing $module" @@ -29,4 +26,12 @@ done idManagementSetup mtbaSetup obds2fhirRestSetup -blazeSecondarySetup \ No newline at end of file +blazeSecondarySetup + +for module in modules/*.sh +do + log DEBUG "sourcing $module" + source $module +done + +transfairSetup \ No newline at end of file diff --git a/dhki/docker-compose.yml b/dhki/docker-compose.yml index d37f1a25..17d33eba 100644 --- a/dhki/docker-compose.yml +++ b/dhki/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.31 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-dhki-blaze environment: BASE_URL: "http://bridgehead-dhki-blaze:8080" @@ -33,13 +33,13 @@ services: EPSILON: 0.28 QUERIES_TO_CACHE: '/queries_to_cache.conf' volumes: - - /srv/docker/bridgehead/dhki/queries_to_cache.conf:/queries_to_cache.conf + - /srv/docker/bridgehead/dhki/queries_to_cache.conf:/queries_to_cache.conf:ro depends_on: - "beam-proxy" - "blaze" beam-proxy: - image: docker.verbis.dkfz.de/cache/samply/beam-proxy:develop + image: docker.verbis.dkfz.de/cache/samply/beam-proxy:${BEAM_TAG} container_name: bridgehead-beam-proxy environment: BROKER_URL: ${BROKER_URL} diff --git a/dhki/vars b/dhki/vars index d043dd26..fcaa83b9 100644 --- a/dhki/vars +++ b/dhki/vars @@ -8,8 +8,6 @@ PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem BROKER_URL_FOR_PREREQ=$BROKER_URL -POSTGRES_TAG=15.6-alpine - for module in ccp/modules/*.sh do log DEBUG "sourcing $module" @@ -25,4 +23,5 @@ do source $module done -transfairSetup \ No newline at end of file +transfairSetup +scoutSetup \ No newline at end of file diff --git a/docs/exporter-templates.md b/docs/exporter-templates.md new file mode 100644 index 00000000..09cbc690 --- /dev/null +++ b/docs/exporter-templates.md @@ -0,0 +1,381 @@ +# Exporter Templates + +An exporter template describes the **structure** and **content** of the **export output**. + +## Main Elements + +* **converter**: Defines the **export job**, specifying **output** filenames and **data sources**. +* **container**: Represents a logical grouping of data rows (like a **table**). +* **attribute**: Defines individual data fields/**columns** extracted from the data source. + +## Other Elements + +* **cql**: Contains Clinical Quality Language metadata used to enrich or filter data. +* **fhir-rev-include**: Defines FHIR reverse includes to fetch related resources. +* **fhir-package**: Defines a FHIR package to be included in the FHIR query. +* **fhir-terminology-server**: FHIR terminology server for validation support. + +## Example Snippet + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Patient + + + + + + { + "resourceType": "Parameters", + "parameter": [ + { + "name": "periodStart", + "valueDate": "2000" + }, + { + "name": "periodEnd", + "valueDate": "2030" + }, + { + "name": "reportType", + "valueCode": "subject-list" + } + ] + } + + + + + + Observation:patient + Condition:patient + ClinicalImpression:patient + MedicationStatement:patient + Procedure:patient + Specimen:patient + AdverseEvent:subject + CarePlan:patient + + +``` + +--- + +## 1. **Converter** + +Main tag of an exporter template. The Exporter functions as a flexible system of converters. Given a specific input format and desired output, the exporter determines the optimal chain of converters to transform the input into the required output. + +Each converter template provides essential details that help the exporter build the correct conversion chain and produce the final export. The template includes the following components: + +- **Source**: The data source from which information is read. Sources are defined in converter.xml, and each template refers to a source by its ID. + +- **Information to Export**: Specifies which elements from the source should be included in the output. + +- **Metadata**: Defines output structure elements such as header names, column titles, sheet names, etc. + +- **Additional Query Information**: Contains any extra data needed to complete and refine the user's query. + +| Tag | Description | +| ------------- | --------------------------------------------------------------------------------------------- | +| `` | Main tag for exporter template containing sources, metadata, and additional query information | + +| Attribute | Description | Example | Default | +| ------------------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------- | ------- | +| id | ID to reference a template | `id="ccp-opal"` | — | +| default-name | Default name when output is in a single file format (no extension; added automatically) | — | — | +| ignore | Deactivate template but keep accessible | `ignore="true"` | false | +| excel-filename | Name of the Excel output file (supports variables `${SITE}`, `${TIMESTAMP}`) | `excel-filename="Export-${SITE}-${TIMESTAMP}.xlsx"` | — | +| csv-separator | CSV separator character | — | `"\t"` | +| source-id | ID of the data source | `source-id="blaze-store"` | — | +| target-id | ID of a target server for file transfer (e.g., Opal for DataSHIELD) | `target-id="opal"` | — | +| opal-project | Opal-specific: name of project | — | — | +| opal-permission-type | Opal permission type (`user` or `group`) | — | — | +| opal-permission-subjects | Opal permission subjects | — | — | +| opal-permission | Opal permission (`administrate` or `use`) | — | — | + +**Notes:** +* You can use variables such as `${SITE}`, `${TIMESTAMP}`, and other environment variables within tags. +* To define environment variables for a specific export, use the HTTP parameter **`CONTEXT`**. + The value must be a Base64-encoded string containing comma-separated key-value pairs. +* **Example:** + Plain: `KEY1=VALUE1,KEY2=VALUE2` + Base64: `S0VZMT1WQUxVRTEsS0VZMj1WQUxVRTI=` + +**Allowed child elements:** + +* ``, ``, ``, ``, `` + +--- + +## 2. **Container** + +Represents a data table with columns (attributes). + +| Tag | Description | +| ------------- | --------------------------------------------------- | +| `` | Defines a container/table with attributes (columns) | + +| Attribute | Description | Example | Default | +| ---------------- | ------------------------------------------------------------ | --------------------------------------------- | ------- | +| id | Container ID to reference | — | — | +| default-name | Name of Excel sheet/file (no extension, added automatically) | — | — | +| csv-filename | Name of CSV file | `csv-filename="Diagnosis-${TIMESTAMP}.csv"` | — | +| json-filename | Name of JSON file | `json-filename="diagnosis-${TIMESTAMP}.json"` | — | +| xml-filename | Name of XML file | `xml-filename="diagnosis-${TIMESTAMP}.xml"` | — | +| xml-root-element | Root element name in XML | `xml-root-element="diagnoses"` | — | +| xml-element | Element name for each entry in XML | `xml-element="diagnosis"` | — | +| excel-sheet | Excel sheet name | `excel-sheet="diagnosis-${TIMESTAMP}.xlsx"` | — | +| opal-table | Opal table name | `opal-name="Diagnosis"` | — | +| opal-entity-type | Opal entity type | — | — | + +### Note +The following attributes can be used to define the name of the output file: + +- **default-name**: Used as a fallback name if no specific filename is provided for the selected output format. + +- **csv-filename**: Specifies the filename for CSV output. + +- **xml-filename**: Specifies the filename for XML output. + +... and so on for other supported formats. + +If the user selects an output format that does not have a specifically defined filename, the default-name will be used as the base, with the appropriate file extension automatically appended. +If neither a format-specific filename nor a default-name is provided, a filename will be automatically generated using a UUID and the correct extension. +--- + +## 3. **Attribute** + +Represents a column in a container/table. + +| Tag | Description | +| ------------- | --------------------------- | +| `` | Defines an attribute/column | + +| Attribute | Description | Example | Default | +| ------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------- | +| id | Attribute ID | `id="Patient-ID"` | — | +| default-name | Default name of the attribute (used if no output-specific name provided) | — | — | +| link | Reference to an attribute of another container (format: `.`) | `link="Patient.Patient-ID"` | — | +| csv-column | Name of the CSV column | — | — | +| excel-column | Name of the Excel column | — | — | +| json-key | JSON key | — | — | +| xml-element | XML element name | — | — | +| opal-value-type | Opal-specific value type | — | — | +| opal-script | Script to be applied to the field in Opal | — | — | +| primary-key | Marks attribute as primary key | `primary-key="true"` | false | +| validation | Marks attribute as syntactic validation field (ends with `-Validation` in DKTK/BBMRI reporter) | `validation="true"` | false | +| val-fhir-path | FHIR path to extract value (if source is a FHIR server) | `val-fhir-path="Patient.gender.value"` | — | +| join-fhir-path | FHIR path for joining secondary resources to main resource | `join-fhir-path="/AdverseEvent.suspectEntity.instance.reference.where(value.startsWith('Procedure')).value"` | — | +| condition-value-fhir-path | Condition filtering for complex value extraction (FHIR path syntax) | `condition-value-fhir-path="Patient.birthDate <= today() - 18 'years'"` | — | +| anonym | Anonymization prefix; replaces real value with `anonym` + number | `anonym="Pat"` | — | +| mdr | Metadata repository ID in DKTK context | `mdr="dktk:dataelement:20:3"` | — | +| op | Operation applied on value (e.g., `EXTRACT_RELATIVE_ID`) | `op="EXTRACT_RELATIVE_ID"` | — | + +--- + +### Notes on **join-fhir-path** + +* Used to join resources in FHIR queries when container references multiple resources. +* Two join types: + + * **Direct:** main resource points to secondary resource in a **parent to child** relationship. + * **Indirect:** secondary resource points back to main resource (path begins with `/`) in a **child to parent** relationship. +* Joins can chain multiple resources, e.g., `R1 -> R2 -> R3`, with commas separating joins. +* It is even possible to combine direct and indirect references: `R1 -> R2 <- R3`: ` R2>,/ R2>` + + +*Examples*: + +* Example of a **direct relationship**: +```xml + + + + ... + + +``` +Here, the main observation Observation.where(code.coding.code = '59847-4') contains a reference to the secondary observation Observation.where(code.coding.code = '59542-1'), where we can find the value that we are looking for. + +* Example of an **indirect relationship**: +```xml + + + + ... + + +``` + +--- + +### Note +The following attributes define the name of a column or field in the output: + +- **default-name**: A general fallback name used when no format-specific name is provided. + +- **csv-column**: Name used for the CSV output. + +- **excel-column**: Name used for Excel output. + +- **json-key**: Name used for JSON output. + +- **xml-element**: Name used for XML output. + +If a format-specific name is not defined for a given output, the default-name will be used. +If default-name is also missing, a UUID will be generated and used as the name. + +--- + +## 4. **CQL** + +Contains metadata and details important for handling CQL queries. + +| Tag | Description | +| ------- | ---------------------------------------------------------------- | +| `` | Container for CQL query metadata including tokens and parameters | + +**Allowed child elements:** + +* ``, ``, `` + +--- + +## 5. **Token (CQL)** + +Replaces keys in CQL queries with specific values (commonly used for stratifiers). + +| Tag | Description | +| --------- | ------------------------------------- | +| `` | Contains `key` and `value` attributes | + +| Attribute | Description | Example | +| --------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| key | Key to replace in CQL | `key="DKTK_STRAT_MEDICATION_STRATIFIER"` | +| value | CQL code snippet that replaces key | `value="define MedicationStatement: if InInitialPopulation then [MedicationStatement] else {} as List "` | + +--- + +## 6. **Measure Parameters (CQL)** + +Parameters for a CQL measure query, typically in JSON format. + +| Tag | Description | +| ---------------------- | ----------------------------------------------------------- | +| `` | Parameters such as `periodStart`, `periodEnd`, `reportType` | + +*Example*: +```xml + + { + "resourceType": "Parameters", + "parameter": [ + { + "name": "periodStart", + "valueDate": "2000" + }, + { + "name": "periodEnd", + "valueDate": "2030" + }, + { + "name": "reportType", + "valueCode": "subject-list" + } + ] + } + +``` + +--- + +## 7. **Default FHIR Search Query (CQL)** + +FHIR search query applied after obtaining measure reports from CQL. + +| Tag | Description | Example | +| ----------------------------- | ----------------------------------------------------- | --------- | +| `` | Defines a FHIR resource type to query (e.g., Patient) | `Patient` | + +CQL (Clinical Quality Language) queries are primarily used to generate MeasureReports. However, in some cases, it is more useful to extract the underlying data used to build those MeasureReports. + +In this context, the CQL query acts as a filtering mechanism—more expressive and powerful than a standard FHIR search query. When the Exporter processes a CQL input, it sends the query to the FHIR server along with the relevant MeasureReport request. The FHIR server responds with a reference to a subset of resources, typically a list of patient IDs. This subset serves as a filter for subsequent data extraction. + +The behavior depends on the selected input format: + +- **CQL**: The Exporter returns the MeasureReports resulting from the execution of the CQL query. + +- **CQL_DATA**: After obtaining the list of matching resource references from the FHIR server, the Exporter performs a second request—a standard FHIR search query—on that filtered list to retrieve the actual data resources (e.g., Patients, Observations, etc.). + +The default FHIR search query is applied to get the resources from the FHIR server after getting the list of patients. + +--- + +## 8. **FHIR Reverse Include** + +Defines which resources should be reverse-included when using FHIR search as input or CQL\_DATA. + +| Tag | Description | +| -------------------- | ------------------------------------------------------------ | +| `` | Specifies reverse include resources to simplify FHIR queries | + +This tag allows users to simplify the FHIR search query by only specifying the search criteria. The specific FHIR resources to be retrieved are defined in the template, not in the user’s query. + +This design shifts responsibility: +- The user focuses on defining what to filter (e.g., patients with a certain condition). +- The template defines what information will be extracted from each matching FHIR resource (e.g., which fields from Patient, Observation, etc.). + +By separating concerns in this way, the template ensures consistent and controlled data extraction while keeping the user's input simple. + +*Example*: +```xml +Observation:patient +Condition:patient +ClinicalImpression:patient +MedicationStatement:patient +Procedure:patient +Specimen:patient +AdverseEvent:subject +CarePlan:patient +``` diff --git a/docs/exporter.md b/docs/exporter.md new file mode 100644 index 00000000..0810e7cd --- /dev/null +++ b/docs/exporter.md @@ -0,0 +1,91 @@ +# Exporter and Reporter + +--- + +## Exporter + +**GitHub:** [https://github.com/samply/exporter](https://github.com/samply/exporter) + +The Exporter is a **REST API** that enables the export of data from various **bridgehead databases** as **structured tables**. It currently supports only **FHIR sources** such as **Blaze**, but it is designed to be extended to **other types** of data sources. The Exporter provides multiple output formats, including **CSV, Excel, JSON, and XML**, and can also export data directly into **Opal (DataSHIELD)**. + +### How it works + +The **user** submits a **query** and specifies the desired **export template** and **output format**. The **query** acts like the `WHERE` clause in SQL, filtering data, while the **template** defines what data to select and how to format it, similar to the `SELECT` clause. The Exporter then processes this to generate the export files. + +### Exporter Templates +[For further information](exporter-templates.md) + + +### Environment Variables + +Below is a list of configurable environment variables used by the Exporter: + +| Variable | Default | Description | +| --------------------------------------------------------- | ------------------------------------------- | ---------------------------------------------------------- | +| APPLICATION\_PORT | 8092 | Port on which the application runs. | +| ARCHIVE\_EXPIRED\_QUERIES\_CRON\_EXPRESSION | `0 0 2 * * *` | Cron expression for archiving expired queries. | +| CLEAN\_TEMP\_FILES\_CRON\_EXPRESSION | `0 0 1 * * *` | Cron expression for cleaning temporary files. | +| CLEAN\_WRITE\_FILES\_CRON\_EXPRESSION | `0 0 2 * * *` | Cron expression for cleaning written files. | +| CONVERTER\_TEMPLATE\_DIRECTORY | | Directory containing conversion templates. | +| CONVERTER\_XML\_APPLICATION\_CONTEXT\_PATH | | Path to the XML application context used by the converter. | +| CROSS\_ORIGINS | | Allowed CORS origins (comma-separated). | +| CSV\_SEPARATOR\_REPLACEMENT | | Character to replace CSV separators within values. | +| EXCEL\_WORKBOOK\_WINDOW | 30000000 | Memory window size for Excel workbook processing. | +| EXPORTER\_API\_KEY | | API key for authenticating access to the exporter. | +| EXPORTER\_DB\_FLYWAY\_MIGRATION\_ENABLED | true | Enable Flyway DB migrations on startup. | +| EXPORTER\_DB\_PASSWORD | | Password for exporter database. | +| EXPORTER\_DB\_URL | `jdbc:postgresql://localhost:5432/exporter` | JDBC URL for exporter DB. | +| EXPORTER\_DB\_USER | | Username for exporter DB. | +| FHIR\_PACKAGES\_DIRECTORY | | Directory where FHIR packages are stored. | +| HAPI\_FHIR\_CLIENT\_LOG\_LEVEL | OFF | Log level for HAPI FHIR client. | +| HIBERNATE\_LOG | false | Enable Hibernate SQL logging. | +| HTTP\_RELATIVE\_PATH | | Relative base path for HTTP endpoints. | +| HTTP\_SERVLET\_REQUEST\_SCHEME | http | Default HTTP scheme. | +| LOG\_FHIR\_VALIDATION | | Enable logging of FHIR validation results. | +| LOG\_LEVEL | INFO | Application log level. | +| MAX\_NUMBER\_OF\_EXCEL\_ROWS\_IN\_A\_SHEET | 100000 | Max rows per Excel sheet. | +| MAX\_NUMBER\_OF\_RETRIES | 10 | Max retry attempts. | +| MERGE\_FILENAME | | Name of merged output file. | +| SITE | | Site identifier for filenames/logs. | +| TEMP\_FILES\_LIFETIME\_IN\_DAYS | 1 | Lifetime of temporary files (days). | +| TEMPORAL\_FILE\_DIRECTORY | | Directory for temporary files. | +| TIMEOUT\_IN\_SECONDS | 10 | Default timeout (seconds). | +| TIMESTAMP\_FORMAT | | Timestamp format string. | +| WEBCLIENT\_BUFFER\_SIZE\_IN\_BYTES | 8192 | Buffer size for web client. | +| WEBCLIENT\_CONNECTION\_TIMEOUT\_IN\_SECONDS | 5 | Connection timeout (seconds). | +| WEBCLIENT\_MAX\_NUMBER\_OF\_RETRIES | 10 | Max retries for web client. | +| WEBCLIENT\_REQUEST\_TIMEOUT\_IN\_SECONDS | 10 | Request timeout (seconds). | +| WEBCLIENT\_TCP\_KEEP\_CONNECTION\_NUMBER\_OF\_TRIES | 3 | TCP keepalive retry attempts. | +| WEBCLIENT\_TCP\_KEEP\_IDLE\_IN\_SECONDS | 30 | TCP keepalive idle time (seconds). | +| WEBCLIENT\_TCP\_KEEP\_INTERVAL\_IN\_SECONDS | 10 | TCP keepalive probe interval (seconds). | +| WEBCLIENT\_TIME\_IN\_SECONDS\_AFTER\_RETRY\_WITH\_FAILURE | 1 | Wait time after failed retry (seconds). | +| WRITE\_FILE\_DIRECTORY | | Directory for final output files. | +| WRITE\_FILES\_LIFETIME\_IN\_DAYS | 30 | Lifetime of written files (days). | +| XML\_FILE\_MERGER\_ROOT\_ELEMENT | Containers | Root element for XML file merging. | +| ZIP\_FILENAME | `exporter-files-${SITE}-${TIMESTAMP}.zip` | Pattern for ZIP archive naming. | + +--- + +### About Cron Expressions in Spring + +Cron expressions configure scheduled tasks and consist of six space-separated fields representing second, minute, hour, day of month, month, and day of week. For example, the default `0 0 2 * * *` means “at 2:00 AM every day.” These expressions allow precise scheduling for maintenance tasks such as cleaning files or archiving data. + +--- + +## Exporter-DB + +**GitHub:** [https://github.com/samply/exporter-db](https://github.com/samply/exporter-db) (If exists; if not, just remove or adjust accordingly) + +The Exporter-DB stores queries for execution by the Exporter and tracks multiple executions of the same query, managing versioning and scheduling. + +--- + +## Reporter + +**GitHub:** [https://github.com/samply/reporter](https://github.com/samply/reporter) + +The Reporter is a **plugin for the Exporter** designed for generating **complex Excel reports** based on **customizable templates**. It supports various template engines like **Groovy** and **Thymeleaf**, making it ideal for producing detailed documents such as the traditional CCP **data quality report**. + +--- + + diff --git a/docs/teiler.md b/docs/teiler.md new file mode 100644 index 00000000..2d05d91f --- /dev/null +++ b/docs/teiler.md @@ -0,0 +1,287 @@ +# Teiler + +**Teiler** is the central frontend of the **bridgehead system**. It brings together multiple independent tools—each built as a **microfrontend**—into a single, unified web application. + +Users interact with Teiler as one coherent interface, but behind the scenes, it dynamically integrates and displays self-contained modules developed with different technologies (**Angular**, **Vue**, **React**, etc.). This modular approach makes Teiler highly flexible, allowing teams to develop, deploy, and maintain features independently. + +Teiler ensures: + +* **A consistent look and feel** across tools. +* **Smooth navigation** between components. +* **Seamless user authentication** across the entire interface. + +Each independent tool integrated into Teiler is called a **bridgehead app**. A bridgehead app can be: + +- A fully standalone microfrontend with its own frontend and backend services. +- An embedded service inside the Teiler Dashboard. +- An external link to another service, possibly hosted on a central server or elsewhere in the federated research network. + +The modularity of Teiler enables it to adapt easily to the evolving needs of the research federated network by simply adding, updating, or removing bridgehead apps. + +Below is a breakdown of Teiler's internal components that make this orchestration possible. + +- [Teiler Orchestrator](#teiler-orchestrator) +- [Teiler Dashboard](#teiler-dashboard) +- [Teiler Backend](#teiler-backend) + +--- + +## Teiler Orchestrator + +**GitHub repository:** [https://github.com/samply/teiler-orchestrator](https://github.com/samply/teiler-orchestrator) + +The **Teiler Orchestrator** is the entry point of the **Single Page Application (SPA)**. It consists of: + +- An **HTML root page**. +- A **JavaScript layer** that: + - **Retrieves microfrontend configurations** from the backend. + - **Registers and manages** the microfrontends using [**Single-SPA**](https://single-spa.js.org/), the framework Teiler uses to create and coordinate its microfrontend environment. + +Using this information, the orchestrator dynamically **loads the correct microfrontend** for a given route and manages its **lifecycle** (*init*, *mount*, *unmount*) in real time. + +**Microfrontends** run in their own containers and can be implemented with any major frontend framework. To be compatible with Teiler, they must integrate with **Single-SPA**. + +To encourage developers to create their own microfrontends and integrate them into Teiler, we provide **starter templates** for **Angular**, **Vue**, and **React**. Developing a new microfrontend is straightforward: + +1. Use one of the templates. +2. Extend it with your own functionality. +3. Add its configuration in the **Teiler Backend**. + +This modular approach accelerates development and fosters collaboration. + + +--- + +## Teiler Dashboard + +**GitHub repository:** [https://github.com/samply/teiler-dashboard](https://github.com/samply/teiler-dashboard) + +The **Teiler Dashboard** is the unified interface users interact with after logging in. It provides: + +- A **single point of access** where various bridgehead apps are embedded as microfrontends. +- **Central navigation** and **session management** for a smooth user experience. + +### Authentication and Authorization + +Teiler uses **OpenID Connect (OIDC)** for user authentication, accessible via the **top navigation bar**. + +We consider three possible **application roles**: + +| Role | Description | +|--------|-----------------------------------------------------------| +| Public | Accessible by any user without the need to log in | +| User | Normal users working with various bridgehead applications | +| Admin | Bridgehead system administrators | + +It is possible to **deactivate OIDC authentication** entirely. In such cases, **all apps must have at least the public role** to allow access. While this may be suitable for development or testing, we **strongly encourage** at least some external authentication mechanism or network-level access control to secure the bridgehead environment. + +Alternatively, basic authentication can be enforced through the existing **Traefik infrastructure** integrated with the bridgehead. + +--- + +## Teiler Backend + +**GitHub repository:** [https://github.com/samply/teiler-backend](https://github.com/samply/teiler-backend) + +The **Teiler Backend** serves as the central configuration hub for all microfrontends and bridgehead apps. It defines: + +- Which bridgehead apps are available. +- Their loading URLs and routes. +- Optional metadata such as display names, icons, roles, and activation status. + +It enables the orchestrator to remain **generic and flexible**, adapting dynamically to whatever apps are defined in the backend configuration. + +### Assets Directory + +There is an **assets** directory where you can save images and other static files to be accessible to your microfrontends. This helps configure and customize apps more easily and quickly. + +Assets can be referenced via: + +``` +/assets/ +``` + +### App Configuration via Environment Variables + +Apps are configured using environment variables with the following structure: + +``` +TEILER_APP_ +Optional: TEILER_APP__ +``` + +- The **number** is just for grouping variables for a single app and has no intrinsic meaning. +- The **app** is the unit within Teiler, shown as a box in the dashboard. +- Apps can be: + - Embedded apps inside the Teiler Dashboard (there is a helper Python script for generating embedded apps: [create-embedded-app.py](https://github.com/samply/teiler-dashboard/blob/main/create-embedded-app.py)) + - External links (e.g., central services outside the local bridgehead instance) +- An app's frontend (microfrontend or embedded app) can either contain the entire functionality or serve as a frontend communicating with other backend microservices in the bridgehead. + +Currently supported languages in the main projects DKTK and BBMRI are **English (EN)** and **German (DE)**, but the system can be extended to other languages. + +The Teiler Dashboard requests variables from the backend for each app and passes the desired language code. If a language-specific variable is unavailable, the default language value is returned. + +### Internationalization (i18n) +#### ⚠️ Important + +If you make any changes to the **Teiler Dashboard**, and those changes involve text elements (e.g., labels, buttons, messages), you must also update the **English translations**, since the application uses **internationalization (i18n)**. + +The **default language** of the project is **German**, so any new text must be manually translated into English after extracting the updated i18n entries. + +To extract new translation entries, run the following command: + +```bash +ng extract-i18n --output-path src/i18n --format=xlf2 +```` + +This will generate or update the file: +`src/i18n/messages.xlf` + +--- + +#### ✅ Requirements to Run the Extraction Command + +| Program | Purpose | Linux Shell (Ubuntu/Debian) | Windows PowerShell | +| -------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| **Node.js** | JavaScript runtime required by Angular and npm | `sudo apt update && sudo apt install nodejs npm`
**or**
[Use NodeSource setup](https://github.com/nodesource/distributions) (recommended) | [Download from nodejs.org](https://nodejs.org) and install manually | +| **npm** | Node package manager (comes with Node.js) | *(Included with Node.js)* | *(Included with Node.js)* | +| **Angular CLI** | Command-line interface for Angular tooling | `npm install -g @angular/cli` | `npm install -g @angular/cli` | +| **Angular project dependencies** | Required packages from `package.json` | `npm install` | `npm install` | + +--- + +#### ✏️ Updating the English Translation + +After running the extraction command, the file `src/i18n/messages.xlf` will contain any newly added i18n entries. + +To provide English translations: + +1. Open `src/i18n/messages.en.xlf`. +2. Compare it with the updated `messages.xlf` to identify any new entries. +3. Copy the new `` blocks from `messages.xlf` into `messages.en.xlf`. +4. For each entry, add the English translation inside the `` tag (in `messages.en.xlf`): + + ```xml + + Willkommen + Welcome + + ``` + +### App Availability Monitoring + +The Teiler Backend regularly **pings apps** to check availability and displays status messages such as: + +- "Frontend not available" +- "Backend not available" +- "Frontend and Backend not available" + +### Accepted TEILER_APP Variable Suffixes + +| Suffix | Description | +|------------------|---------------------------------------------------------------------------------------------------------------| +| NAME | Identifier of the app (no spaces). For embedded apps, must match the identifier defined in Teiler Dashboard. | +| TITLE | Display title shown to users. | +| DESCRIPTION | Brief description of the app. | +| BACKENDURL | URL of the backend microservice (if applicable). | +| BACKENDCHECKURL | URL that the backend pings to verify backend availability. Defaults to BACKENDURL if not specified. | +| SOURCEURL | URL of the microfrontend or external link (not used for embedded apps). | +| SOURCECHECKURL | URL to ping to check microfrontend or external link availability. Defaults to SOURCEURL if not specified. | +| ROLES | Comma-separated roles allowed: `TEILER_PUBLIC`, `TEILER_USER`, `TEILER_ADMIN`. | +| ISACTIVATED | `true` or `false`. Used to temporarily deactivate an app without deleting its config. | +| ICONCLASS | Bootstrap icon class to display in app box (e.g., `"bi bi-search"`). | +| ICONSOURCEURL | URL to an image icon. Prefer using local assets instead of external URLs. | +| ORDER | Relative display order of the app in the dashboard. | +| ISEXTERNALLINK | `true` or `false`. Indicates if the app is an external link outside the local bridgehead. | +| ISLOCAL | `true` or `false`. Indicates if the app runs locally within the bridgehead site or on a central server. | + +*Note:* Embedded apps often have many of these variables preconfigured and may not require manual specification. See the [Teiler Dashboard documentation](https://github.com/samply/teiler-dashboard) for details. + +### Additional Teiler Backend Variables for Dashboard Configuration + +| Variable Prefix | Description | +|------------------------------------|--------------------------------------------------------------------------------------------------------------| +| TEILER_DASHBOARD_ | General configuration of the dashboard. | +| TEILER_DASHBOARD_<LangCode>_ | Language-specific configuration overrides. | + +Important suffixes include: + +| Suffix | Description | +|------------------|-------------------------------------------------------------------------------------------| +| WELCOME_TITLE | Title shown on the initial screen before login. | +| WELCOME_TEXT | Welcome message or instructions before login. | +| FURTHER_INFO | Additional informational text or links. | +| BACKGROUND_IMAGE_URL | URL to a background image (SVG recommended for scalability). | +| LOGO_URL | URL to the project or bridgehead logo. | +| LOGO_HEIGHT | Height of the displayed logo. | +| LOGO_TEXT | Title text of the bridgehead (e.g., "DKTK Bridgehead"). | +| COLOR_PALETTE | JSON link to color palettes for text, lines, icons, and background (especially for SVGs). | +| COLOR_PROFILE | Selected color profile from the palette. (color palette name) | +| FONT | Font family for the dashboard text. | + +### 🎨 Color Palette + +Below is an example of a **color palette** definition in JSON format: + +```json +{ + "color-palettes": [ + { + "name": "Grey", + "colors": { + "text": "grey", + "line": "grey", + "icon": "grey", + "background": "grey" + } + }, + { + "name": "Black", + "colors": { + "text": "black", + "line": "black", + "icon": "black", + "background": "#F7ADAD" + } + } + ] +} +``` + +Each palette contains a unique `name` and a set of color values for different UI elements. + +#### 🔍 Palette Elements + +| **Variable** | **Description** | +| ------------ | --------------------------------------------------- | +| `name` | Identifier of the color palette | +| `text` | Color used for text | +| `line` | Color used for lines (e.g., borders, dividers) | +| `icon` | Color used for icons | +| `background` | Background color (especially useful for SVG images) | + + +--- + +### 🚀 Ready to Extend Teiler? + +If you want to create your own **bridgehead app** and integrate it into **Teiler**, start by: + +1. Selecting a template **or** +2. Building a microfrontend compatible with [Single-SPA](https://single-spa.js.org/). + +Then, register your app’s configuration in the **Teiler Backend** as described above. + +> 💡 **Tip:** This flexible, modular design makes it easy to plug in your own features and services. + +--- + +### 🔧 Build & Contribute Your App! + +🧩 **Join the ecosystem!** +Add your app to Teiler and expand its functionality for everyone. + +Whether it’s a visualization tool, a data processing module, or a custom UI component — your contribution can help grow the platform. 💪 + +> 👉 **Get started today and shape the future of Teiler!** + diff --git a/itcc/docker-compose.yml b/itcc/docker-compose.yml index c9bce0cb..49edff0a 100644 --- a/itcc/docker-compose.yml +++ b/itcc/docker-compose.yml @@ -2,19 +2,20 @@ version: "3.7" services: blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.31 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-itcc-blaze environment: BASE_URL: "http://bridgehead-itcc-blaze:8080" JAVA_TOOL_OPTIONS: "-Xmx${BLAZE_MEMORY_CAP:-4096}m" DB_RESOURCE_CACHE_SIZE: ${BLAZE_RESOURCE_CACHE_CAP:-2500000} - DB_BLOCK_CACHE_SIZE: $BLAZE_MEMORY_CAP + DB_BLOCK_CACHE_SIZE: ${BLAZE_MEMORY_CAP} + CQL_EXPR_CACHE_SIZE: ${BLAZE_CQL_CACHE_CAP:-32} ENFORCE_REFERENTIAL_INTEGRITY: "false" volumes: - "blaze-data:/app/data" labels: - "traefik.enable=true" - - "traefik.http.routers.blaze_itcc.rule=PathPrefix(`/itcc-localdatamanagement`)" + - "traefik.http.routers.blaze_itcc.rule=Host(`${HOST}`) && PathPrefix(`/itcc-localdatamanagement`)" - "traefik.http.middlewares.itcc_b_strip.stripprefix.prefixes=/itcc-localdatamanagement" - "traefik.http.services.blaze_itcc.loadbalancer.server.port=8080" - "traefik.http.routers.blaze_itcc.middlewares=itcc_b_strip,auth" @@ -31,6 +32,10 @@ services: BEAM_PROXY_URL: http://beam-proxy:8081 RETRY_COUNT: ${FOCUS_RETRY_COUNT} EPSILON: 0.28 + QUERIES_TO_CACHE: '/queries_to_cache.conf' + ENDPOINT_TYPE: ${FOCUS_ENDPOINT_TYPE:-blaze} + volumes: + - /srv/docker/bridgehead/itcc/queries_to_cache.conf:/queries_to_cache.conf:ro depends_on: - "beam-proxy" - "blaze" diff --git a/itcc/modules/itcc-omics-ingest.sh b/itcc/modules/itcc-omics-ingest.sh new file mode 100644 index 00000000..a078140a --- /dev/null +++ b/itcc/modules/itcc-omics-ingest.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +if [ -n "$ENABLE_OMICS" ];then + OVERRIDE+=" -f ./$PROJECT/modules/itcc-omics-ingest.yaml" + GENERATE_API_KEY="$(generate_simple_password 'omics')" +fi \ No newline at end of file diff --git a/itcc/modules/itcc-omics-ingest.yaml b/itcc/modules/itcc-omics-ingest.yaml new file mode 100644 index 00000000..81084331 --- /dev/null +++ b/itcc/modules/itcc-omics-ingest.yaml @@ -0,0 +1,14 @@ +services: + omics-endpoint: + image: ghcr.io/samply/itcc-omics-ingest:main + environment: + - API_KEY=${GENERATE_API_KEY} + volumes: + - /var/cache/bridgehead/omics/data:/data/uploads + labels: + - "traefik.http.routers.omics.rule=Host(`${HOST}`) && PathPrefix(`/api/omics`)" + - "traefik.enable=true" + - "traefik.http.services.omics.loadbalancer.server.port=6080" + - "traefik.http.routers.omics.tls=true" + - "traefik.http.middlewares.omics-stripprefix.stripprefix.prefixes=/api" + - "traefik.http.routers.omics.middlewares=omics-stripprefix" diff --git a/itcc/modules/lens-compose.yml b/itcc/modules/lens-compose.yml index 2bbddbe5..5a5b78cc 100644 --- a/itcc/modules/lens-compose.yml +++ b/itcc/modules/lens-compose.yml @@ -1,33 +1,47 @@ version: "3.7" services: - landing: - container_name: lens_federated-search - image: docker.verbis.dkfz.de/ccp/lens:${SITE_ID} + itcc-explorer: + container_name: lens_itcc_explorer + image: samply/itcc-explorer:main + environment: + HOST: "0.0.0.0" + BIND_ADDR: "0.0.0.0:3000" + PUBLIC_ENVIRONMENT: ${PUBLIC_ENVIRONMENT} labels: - "traefik.enable=true" - - "traefik.http.routers.landing.rule=PathPrefix(`/`)" - - "traefik.http.services.landing.loadbalancer.server.port=80" - - "traefik.http.routers.landing.tls=true" + - "traefik.http.routers.itcc.rule=Host(`${HOST}`) && PathPrefix(`/`)" + - "traefik.http.routers.itcc.entrypoints=websecure" + - "traefik.http.services.itcc.loadbalancer.server.port=3000" + - "traefik.http.routers.itcc.tls=true" spot: - image: docker.verbis.dkfz.de/ccp-private/central-spot + image: samply/rustyspot:latest environment: BEAM_SECRET: "${FOCUS_BEAM_SECRET_SHORT}" - BEAM_URL: http://beam-proxy:8081 + BEAM_PROXY_URL: http://beam-proxy:8081 BEAM_PROXY_ID: ${SITE_ID} BEAM_BROKER_ID: ${BROKER_ID} - BEAM_APP_ID: "focus" - PROJECT_METADATA: "dktk_supervisors" + BEAM_APP_ID: "spot.${SITE_ID}.${BROKER_ID}" + CORS_ORIGIN: "https://${HOST}" + SITES: ${SITES} + TRANSFORM: LENS + PROJECT: "itcc" + BIND_ADDR: 0.0.0.0:8055 depends_on: - "beam-proxy" labels: - "traefik.enable=true" - - "traefik.http.services.spot.loadbalancer.server.port=8080" + - "traefik.http.services.spot.loadbalancer.server.port=8055" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowmethods=GET,OPTIONS,POST" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowheaders=content-type" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolalloworiginlist=https://${HOST}" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowcredentials=true" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolmaxage=-1" - - "traefik.http.routers.spot.rule=Host(`${HOST}`) && PathPrefix(`/backend`)" - - "traefik.http.middlewares.stripprefix_spot.stripprefix.prefixes=/backend" + - "traefik.http.routers.spot.rule=Host(`${HOST}`) && PathPrefix(`/prod`)" + - "traefik.http.middlewares.stripprefix_spot.stripprefix.prefixes=/prod" - "traefik.http.routers.spot.tls=true" - - "traefik.http.routers.spot.middlewares=corsheaders2,stripprefix_spot" + - "traefik.http.routers.spot.middlewares=corsheaders2,stripprefix_spot,auth" + + beam-proxy: + environment: + APP_spot_KEY: ${FOCUS_BEAM_SECRET_SHORT} diff --git a/itcc/queries_to_cache.conf b/itcc/queries_to_cache.conf new file mode 100644 index 00000000..9935a47f --- /dev/null +++ b/itcc/queries_to_cache.conf @@ -0,0 +1,2 @@ +bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwpjb2Rlc3lzdGVtIFNhbXBsZU1hdGVyaWFsVHlwZTogJ2h0dHBzOi8vZmhpci5iYm1yaS5kZS9Db2RlU3lzdGVtL1NhbXBsZU1hdGVyaWFsVHlwZScKCmNvZGVzeXN0ZW0gbG9pbmM6ICdodHRwOi8vbG9pbmMub3JnJwoKY29udGV4dCBQYXRpZW50CkRLVEtfU1RSQVRfR0VOREVSX1NUUkFUSUZJRVIKICBES1RLX1NUUkFUX0RJQUdOT1NJU19TVFJBVElGSUVSCiAgSVRDQ19TVFJBVF9BR0VfQ0xBU1NfU1RSQVRJRklFUgogIERLVEtfU1RSQVRfREVGX0lOX0lOSVRJQUxfUE9QVUxBVElPTgp0cnVl +bGlicmFyeSBSZXRyaWV2ZQp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4wJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4wJwpjb2Rlc3lzdGVtIFNhbXBsZU1hdGVyaWFsVHlwZTogJ2h0dHBzOi8vZmhpci5iYm1yaS5kZS9Db2RlU3lzdGVtL1NhbXBsZU1hdGVyaWFsVHlwZScKCmNvZGVzeXN0ZW0gbG9pbmM6ICdodHRwOi8vbG9pbmMub3JnJwpjb2Rlc3lzdGVtIG1vbGVjdWxhck1hcmtlcjogJ2h0dHA6Ly93d3cuZ2VuZW5hbWVzLm9yZycKCmNvbnRleHQgUGF0aWVudApES1RLX1NUUkFUX0dFTkRFUl9TVFJBVElGSUVSCiAgREtUS19TVFJBVF9ESUFHTk9TSVNfU1RSQVRJRklFUgogIElUQ0NfU1RSQVRfQUdFX0NMQVNTX1NUUkFUSUZJRVIKICBES1RLX1NUUkFUX0RFRl9JTl9JTklUSUFMX1BPUFVMQVRJT04KKGV4aXN0cyBmcm9tIFtPYnNlcnZhdGlvbjogQ29kZSAnNjk1NDgtNicgZnJvbSBsb2luY10gTwp3aGVyZSBPLmNvbXBvbmVudC53aGVyZShjb2RlLmNvZGluZyBjb250YWlucyBDb2RlICc0ODAxOC02JyBmcm9tIGxvaW5jKS52YWx1ZS5jb2RpbmcgY29udGFpbnMgQ29kZSAnQlJBRicgZnJvbSBtb2xlY3VsYXJNYXJrZXIp diff --git a/itcc/vars b/itcc/vars index b03403b8..3eee6525 100644 --- a/itcc/vars +++ b/itcc/vars @@ -6,6 +6,7 @@ FOCUS_RETRY_COUNT=${FOCUS_RETRY_COUNT:-64} SUPPORT_EMAIL=arturo.macias@dkfz-heidelberg.de PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem BROKER_URL_FOR_PREREQ=$BROKER_URL +PUBLIC_ENVIRONMENT=prod for module in $PROJECT/modules/*.sh do diff --git a/kr/docker-compose.yml b/kr/docker-compose.yml index 17b36b7a..3da9e53a 100644 --- a/kr/docker-compose.yml +++ b/kr/docker-compose.yml @@ -6,13 +6,14 @@ services: replicas: 0 #deactivate landing page blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.31 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-kr-blaze environment: BASE_URL: "http://bridgehead-kr-blaze:8080" JAVA_TOOL_OPTIONS: "-Xmx${BLAZE_MEMORY_CAP:-4096}m" DB_RESOURCE_CACHE_SIZE: ${BLAZE_RESOURCE_CACHE_CAP:-2500000} - DB_BLOCK_CACHE_SIZE: $BLAZE_MEMORY_CAP + DB_BLOCK_CACHE_SIZE: ${BLAZE_MEMORY_CAP} + CQL_EXPR_CACHE_SIZE: ${BLAZE_CQL_CACHE_CAP:-32} ENFORCE_REFERENTIAL_INTEGRITY: "false" volumes: - "blaze-data:/app/data" @@ -40,7 +41,7 @@ services: - "blaze" beam-proxy: - image: docker.verbis.dkfz.de/cache/samply/beam-proxy:develop + image: docker.verbis.dkfz.de/cache/samply/beam-proxy:${BEAM_TAG} container_name: bridgehead-beam-proxy environment: BROKER_URL: ${BROKER_URL} diff --git a/kr/modules/export-and-qb.curl-templates b/kr/modules/export-and-qb.curl-templates deleted file mode 100644 index 739c5af6..00000000 --- a/kr/modules/export-and-qb.curl-templates +++ /dev/null @@ -1,6 +0,0 @@ -# Full Excel Export -curl --location --request POST 'https://${HOST}/ccp-exporter/request?query=Patient&query-format=FHIR_PATH&template-id=ccp&output-format=EXCEL' \ ---header 'x-api-key: ${EXPORT_API_KEY}' - -# QB -curl --location --request POST 'https://${HOST}/ccp-reporter/generate?template-id=ccp' diff --git a/kr/modules/exporter-compose.yml b/kr/modules/exporter-compose.yml index d5eb2274..c5e69843 100644 --- a/kr/modules/exporter-compose.yml +++ b/kr/modules/exporter-compose.yml @@ -1,9 +1,10 @@ version: "3.7" services: + exporter: image: docker.verbis.dkfz.de/ccp/dktk-exporter:latest - container_name: bridgehead-ccp-exporter + container_name: bridgehead-kr-exporter environment: JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" LOG_LEVEL: "INFO" @@ -12,39 +13,51 @@ services: EXPORTER_DB_USER: "exporter" EXPORTER_DB_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh EXPORTER_DB_URL: "jdbc:postgresql://exporter-db:5432/exporter" - HTTP_RELATIVE_PATH: "/ccp-exporter" + HTTP_RELATIVE_PATH: "/kr-exporter" SITE: "${SITE_ID}" HTTP_SERVLET_REQUEST_SCHEME: "https" OPAL_PASSWORD: "${EXPORTER_OPAL_PASSWORD}" labels: - "traefik.enable=true" - - "traefik.http.routers.exporter_ccp.rule=PathPrefix(`/ccp-exporter`)" - - "traefik.http.services.exporter_ccp.loadbalancer.server.port=8092" - - "traefik.http.routers.exporter_ccp.tls=true" - - "traefik.http.middlewares.exporter_ccp_strip.stripprefix.prefixes=/ccp-exporter" - - "traefik.http.routers.exporter_ccp.middlewares=exporter_ccp_strip" + - "traefik.http.routers.exporter_kr.rule=PathPrefix(`/kr-exporter`)" + - "traefik.http.services.exporter_kr.loadbalancer.server.port=8092" + - "traefik.http.routers.exporter_kr.tls=true" + - "traefik.http.middlewares.exporter_kr_strip.stripprefix.prefixes=/kr-exporter" + - "traefik.http.routers.exporter_kr.middlewares=exporter_kr_strip" + # Main router + - "traefik.http.routers.exporter_kr.priority=20" + + # API router + - "traefik.http.routers.exporter_kr_api.middlewares=exporter_kr_strip,exporter_auth" + - "traefik.http.routers.exporter_kr_api.rule=PathRegexp(`/kr-exporter/.+`)" + - "traefik.http.routers.exporter_kr_api.tls=true" + - "traefik.http.routers.exporter_kr_api.priority=25" + + # Shared middlewares + - "traefik.http.middlewares.exporter_auth.basicauth.users=${EXPORTER_USER}" + volumes: - - "/var/cache/bridgehead/ccp/exporter-files:/app/exporter-files/output" + - "/var/cache/bridgehead/kr/exporter-files:/app/exporter-files/output" exporter-db: image: docker.verbis.dkfz.de/cache/postgres:${POSTGRES_TAG} - container_name: bridgehead-ccp-exporter-db + container_name: bridgehead-kr-exporter-db environment: POSTGRES_USER: "exporter" POSTGRES_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh POSTGRES_DB: "exporter" volumes: # Consider removing this volume once we find a solution to save Lens-queries to be executed in the explorer. - - "/var/cache/bridgehead/ccp/exporter-db:/var/lib/postgresql/data" + - "/var/cache/bridgehead/kr/exporter-db:/var/lib/postgresql/data" reporter: image: docker.verbis.dkfz.de/ccp/dktk-reporter:latest - container_name: bridgehead-ccp-reporter + container_name: bridgehead-kr-reporter environment: JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" LOG_LEVEL: "INFO" CROSS_ORIGINS: "https://${HOST}" - HTTP_RELATIVE_PATH: "/ccp-reporter" + HTTP_RELATIVE_PATH: "/kr-reporter" SITE: "${SITE_ID}" EXPORTER_API_KEY: "${EXPORTER_API_KEY}" # Set in exporter-setup.sh EXPORTER_URL: "http://exporter:8092" @@ -52,16 +65,23 @@ services: HTTP_SERVLET_REQUEST_SCHEME: "https" # In this initial development state of the bridgehead, we are trying to have so many volumes as possible. - # However, in the first executions in the CCP sites, this volume seems to be very important. A report is + # However, in the first executions in the kr sites, this volume seems to be very important. A report is # a process that can take several hours, because it depends on the exporter. # There is a risk that the bridgehead restarts, losing the already created export. volumes: - - "/var/cache/bridgehead/ccp/reporter-files:/app/reports" + - "/var/cache/bridgehead/kr/reporter-files:/app/reports" labels: - "traefik.enable=true" - - "traefik.http.routers.reporter_ccp.rule=PathPrefix(`/ccp-reporter`)" - - "traefik.http.services.reporter_ccp.loadbalancer.server.port=8095" - - "traefik.http.routers.reporter_ccp.tls=true" - - "traefik.http.middlewares.reporter_ccp_strip.stripprefix.prefixes=/ccp-reporter" - - "traefik.http.routers.reporter_ccp.middlewares=reporter_ccp_strip" + - "traefik.http.routers.reporter_kr.rule=PathPrefix(`/kr-reporter`)" + - "traefik.http.services.reporter_kr.loadbalancer.server.port=8095" + - "traefik.http.routers.reporter_kr.tls=true" + - "traefik.http.middlewares.reporter_kr_strip.stripprefix.prefixes=/kr-reporter" + - "traefik.http.routers.reporter_kr.middlewares=reporter_kr_strip" + - "traefik.http.routers.reporter_kr.priority=20" + + - "traefik.http.routers.reporter_kr_api.middlewares=reporter_kr_strip,exporter_auth" + - "traefik.http.routers.reporter_kr_api.rule=PathRegexp(`/kr-reporter/.+`)" + - "traefik.http.routers.reporter_kr_api.tls=true" + - "traefik.http.routers.reporter_kr_api.priority=25" + diff --git a/kr/modules/exporter.md b/kr/modules/exporter.md deleted file mode 100644 index 24e81b05..00000000 --- a/kr/modules/exporter.md +++ /dev/null @@ -1,15 +0,0 @@ -# Exporter and Reporter - - -## Exporter -The exporter is a REST API that exports the data of the different databases of the bridgehead in a set of tables. -It can accept different output formats as CSV, Excel, JSON or XML. It can also export data into Opal. - -## Exporter-DB -It is a database to save queries for its execution in the exporter. -The exporter manages also the different executions of the same query in through the database. - -## Reporter -This component is a plugin of the exporter that allows to create more complex Excel reports described in templates. -It is compatible with different template engines as Groovy, Thymeleaf,... -It is perfect to generate a document as our traditional CCP quality report. diff --git a/kr/modules/lens-compose.yml b/kr/modules/lens-compose.yml index b0b4573d..ea2b98d7 100644 --- a/kr/modules/lens-compose.yml +++ b/kr/modules/lens-compose.yml @@ -4,32 +4,41 @@ services: deploy: replicas: 1 #reactivate if lens is in use container_name: lens_federated-search - image: docker.verbis.dkfz.de/ccp/lens:${SITE_ID} + image: docker.verbis.dkfz.de/ccp/kr-explorer:main + environment: + PUBLIC_SPOT_URL: https://${HOST}/prod labels: + - "traefik.http.services.lens.loadbalancer.server.port=3000" - "traefik.enable=true" - - "traefik.http.routers.landing.rule=PathPrefix(`/`)" - - "traefik.http.services.landing.loadbalancer.server.port=80" - - "traefik.http.routers.landing.tls=true" + - "traefik.http.routers.lens.rule=Host(`${HOST}`)" + - "traefik.http.routers.lens.tls=true" spot: - image: docker.verbis.dkfz.de/ccp-private/central-spot + image: samply/rustyspot:latest environment: BEAM_SECRET: "${FOCUS_BEAM_SECRET_SHORT}" - BEAM_URL: http://beam-proxy:8081 - BEAM_PROXY_ID: ${SITE_ID} - BEAM_BROKER_ID: ${BROKER_ID} - BEAM_APP_ID: "focus" - PROJECT_METADATA: "kr_supervisors" + BEAM_PROXY_URL: http://beam-proxy:8081 + BEAM_APP_ID: "spot.${SITE_ID}.${BROKER_ID}" + CORS_ORIGIN: "https://${HOST}" + SITES: ${SITES} + TRANSFORM: LENS + PROJECT: kr + BIND_ADDR: 0.0.0.0:8055 depends_on: - "beam-proxy" labels: - "traefik.enable=true" - - "traefik.http.services.spot.loadbalancer.server.port=8080" + - "traefik.http.services.spot.loadbalancer.server.port=8055" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowmethods=GET,OPTIONS,POST" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowheaders=content-type" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolalloworiginlist=https://${HOST}" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowcredentials=true" - "traefik.http.middlewares.corsheaders2.headers.accesscontrolmaxage=-1" - - "traefik.http.routers.spot.rule=Host(`${HOST}`) && PathPrefix(`/backend`)" - - "traefik.http.middlewares.stripprefix_spot.stripprefix.prefixes=/backend" + - "traefik.http.routers.spot.rule=Host(`${HOST}`) && PathPrefix(`/prod`)" + - "traefik.http.middlewares.stripprefix_spot.stripprefix.prefixes=/prod" - "traefik.http.routers.spot.tls=true" - - "traefik.http.routers.spot.middlewares=corsheaders2,stripprefix_spot" + - "traefik.http.routers.spot.middlewares=corsheaders2,stripprefix_spot,auth" + + beam-proxy: + environment: + APP_spot_KEY: ${FOCUS_BEAM_SECRET_SHORT} diff --git a/kr/modules/obds2fhir-rest-compose.yml b/kr/modules/obds2fhir-rest-compose.yml index 833580d1..ec1737c8 100644 --- a/kr/modules/obds2fhir-rest-compose.yml +++ b/kr/modules/obds2fhir-rest-compose.yml @@ -3,7 +3,7 @@ version: "3.7" services: obds2fhir-rest: container_name: bridgehead-obds2fhir-rest - image: docker.verbis.dkfz.de/ccp/obds2fhir-rest:main + image: docker.verbis.dkfz.de/samply/obds2fhir-rest:main environment: IDTYPE: BK_${IDMANAGEMENT_FRIENDLY_ID}_L-ID MAINZELLISTE_APIKEY: ${IDMANAGER_LOCAL_PATIENTLIST_APIKEY} diff --git a/kr/modules/teiler-compose.yml b/kr/modules/teiler-compose.yml index f415ee97..fd71514e 100644 --- a/kr/modules/teiler-compose.yml +++ b/kr/modules/teiler-compose.yml @@ -7,75 +7,58 @@ services: container_name: bridgehead-teiler-orchestrator labels: - "traefik.enable=true" - - "traefik.http.routers.teiler_orchestrator_ccp.rule=PathPrefix(`/ccp-teiler`)" - - "traefik.http.services.teiler_orchestrator_ccp.loadbalancer.server.port=9000" - - "traefik.http.routers.teiler_orchestrator_ccp.tls=true" - - "traefik.http.middlewares.teiler_orchestrator_ccp_strip.stripprefix.prefixes=/ccp-teiler" - - "traefik.http.routers.teiler_orchestrator_ccp.middlewares=teiler_orchestrator_ccp_strip" + - "traefik.http.routers.teiler_orchestrator_kr.rule=PathPrefix(`/kr-teiler`)" + - "traefik.http.services.teiler_orchestrator_kr.loadbalancer.server.port=9000" + - "traefik.http.routers.teiler_orchestrator_kr.tls=true" + - "traefik.http.middlewares.teiler_orchestrator_kr_strip.stripprefix.prefixes=/kr-teiler" + - "traefik.http.routers.teiler_orchestrator_kr.middlewares=teiler_orchestrator_kr_strip" environment: - TEILER_BACKEND_URL: "https://${HOST}/ccp-teiler-backend" - TEILER_DASHBOARD_URL: "https://${HOST}/ccp-teiler-dashboard" + TEILER_BACKEND_URL: "/kr-teiler-backend" + TEILER_DASHBOARD_URL: "/kr-teiler-dashboard" DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE_LOWER_CASE}" - HTTP_RELATIVE_PATH: "/ccp-teiler" + HTTP_RELATIVE_PATH: "/kr-teiler" teiler-dashboard: - image: docker.verbis.dkfz.de/cache/samply/teiler-dashboard:develop + image: docker.verbis.dkfz.de/cache/samply/teiler-dashboard:${TEILER_DASHBOARD_TAG} container_name: bridgehead-teiler-dashboard labels: - "traefik.enable=true" - - "traefik.http.routers.teiler_dashboard_ccp.rule=PathPrefix(`/ccp-teiler-dashboard`)" - - "traefik.http.services.teiler_dashboard_ccp.loadbalancer.server.port=80" - - "traefik.http.routers.teiler_dashboard_ccp.tls=true" - - "traefik.http.middlewares.teiler_dashboard_ccp_strip.stripprefix.prefixes=/ccp-teiler-dashboard" - - "traefik.http.routers.teiler_dashboard_ccp.middlewares=teiler_dashboard_ccp_strip" + - "traefik.http.routers.teiler_dashboard_kr.rule=PathPrefix(`/kr-teiler-dashboard`)" + - "traefik.http.services.teiler_dashboard_kr.loadbalancer.server.port=80" + - "traefik.http.routers.teiler_dashboard_kr.tls=true" + - "traefik.http.middlewares.teiler_dashboard_kr_strip.stripprefix.prefixes=/kr-teiler-dashboard" + - "traefik.http.routers.teiler_dashboard_kr.middlewares=teiler_dashboard_kr_strip" environment: DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" - TEILER_BACKEND_URL: "https://${HOST}/ccp-teiler-backend" - OIDC_URL: "${OIDC_URL}" - OIDC_REALM: "${OIDC_REALM}" - OIDC_CLIENT_ID: "${OIDC_PUBLIC_CLIENT_ID}" - OIDC_TOKEN_GROUP: "${OIDC_GROUP_CLAIM}" + TEILER_BACKEND_URL: "/kr-teiler-backend" + TEILER_DASHBOARD_URL: "/kr-teiler-dashboard" TEILER_ADMIN_NAME: "${OPERATOR_FIRST_NAME} ${OPERATOR_LAST_NAME}" TEILER_ADMIN_EMAIL: "${OPERATOR_EMAIL}" TEILER_ADMIN_PHONE: "${OPERATOR_PHONE}" TEILER_PROJECT: "${PROJECT}" EXPORTER_API_KEY: "${EXPORTER_API_KEY}" - TEILER_ORCHESTRATOR_URL: "https://${HOST}/ccp-teiler" - TEILER_DASHBOARD_HTTP_RELATIVE_PATH: "/ccp-teiler-dashboard" - TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/ccp-teiler" - TEILER_USER: "${OIDC_USER_GROUP}" - TEILER_ADMIN: "${OIDC_ADMIN_GROUP}" + TEILER_ORCHESTRATOR_URL: "/kr-teiler" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/kr-teiler" REPORTER_DEFAULT_TEMPLATE_ID: "ccp-qb" EXPORTER_DEFAULT_TEMPLATE_ID: "ccp" teiler-backend: - image: docker.verbis.dkfz.de/ccp/dktk-teiler-backend:latest + image: docker.verbis.dkfz.de/ccp/kr-teiler-backend:latest container_name: bridgehead-teiler-backend labels: - "traefik.enable=true" - - "traefik.http.routers.teiler_backend_ccp.rule=PathPrefix(`/ccp-teiler-backend`)" - - "traefik.http.services.teiler_backend_ccp.loadbalancer.server.port=8085" - - "traefik.http.routers.teiler_backend_ccp.tls=true" - - "traefik.http.middlewares.teiler_backend_ccp_strip.stripprefix.prefixes=/ccp-teiler-backend" - - "traefik.http.routers.teiler_backend_ccp.middlewares=teiler_backend_ccp_strip" + - "traefik.http.routers.teiler_backend_kr.rule=PathPrefix(`/kr-teiler-backend`)" + - "traefik.http.services.teiler_backend_kr.loadbalancer.server.port=8085" + - "traefik.http.routers.teiler_backend_kr.tls=true" + - "traefik.http.middlewares.teiler_backend_kr_strip.stripprefix.prefixes=/kr-teiler-backend" + - "traefik.http.routers.teiler_backend_kr.middlewares=teiler_backend_kr_strip" environment: LOG_LEVEL: "INFO" APPLICATION_PORT: "8085" - APPLICATION_ADDRESS: "${HOST}" DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" - CONFIG_ENV_VAR_PATH: "/run/secrets/ccp.conf" - TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/ccp-teiler" - TEILER_ORCHESTRATOR_URL: "https://${HOST}/ccp-teiler" - TEILER_DASHBOARD_DE_URL: "https://${HOST}/ccp-teiler-dashboard/de" - TEILER_DASHBOARD_EN_URL: "https://${HOST}/ccp-teiler-dashboard/en" - CENTRAX_URL: "${CENTRAXX_URL}" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/kr-teiler" + TEILER_ORCHESTRATOR_URL: "/kr-teiler" + TEILER_DASHBOARD_DE_URL: "/kr-teiler-dashboard/de" + TEILER_DASHBOARD_EN_URL: "/kr-teiler-dashboard/en" HTTP_PROXY: "http://forward_proxy:3128" - ENABLE_MTBA: "${ENABLE_MTBA}" - ENABLE_DATASHIELD: "${ENABLE_DATASHIELD}" - secrets: - - ccp.conf - -secrets: - ccp.conf: - file: /etc/bridgehead/ccp.conf diff --git a/kr/modules/teiler-setup.sh b/kr/modules/teiler-setup.sh index eed3f81f..dc8dfc47 100644 --- a/kr/modules/teiler-setup.sh +++ b/kr/modules/teiler-setup.sh @@ -3,7 +3,6 @@ if [ "$ENABLE_TEILER" == true ];then log INFO "Teiler setup detected -- will start Teiler services." OVERRIDE+=" -f ./$PROJECT/modules/teiler-compose.yml" - TEILER_DEFAULT_LANGUAGE=DE + TEILER_DEFAULT_LANGUAGE=EN TEILER_DEFAULT_LANGUAGE_LOWER_CASE=${TEILER_DEFAULT_LANGUAGE,,} - add_public_oidc_redirect_url "/ccp-teiler/*" fi diff --git a/kr/modules/teiler.md b/kr/modules/teiler.md deleted file mode 100644 index 51e94e46..00000000 --- a/kr/modules/teiler.md +++ /dev/null @@ -1,19 +0,0 @@ -# Teiler -This module orchestrates the different microfrontends of the bridgehead as a single page application. - -## Teiler Orchestrator -Single SPA component that consists on the root HTML site of the single page application and a javascript code that -gets the information about the microfrontend calling the teiler backend and is responsible for registering them. With the -resulting mapping, it can initialize, mount and unmount the required microfrontends on the fly. - -The microfrontends run independently in different containers and can be based on different frameworks (Angular, Vue, React,...) -This microfrontends can run as single alone but need an extension with Single-SPA (https://single-spa.js.org/docs/ecosystem). -There are also available three templates (Angular, Vue, React) to be directly extended to be used directly in the teiler. - -## Teiler Dashboard -It consists on the main dashboard and a set of embedded services. -### Login -user and password in ccp.local.conf - -## Teiler Backend -In this component, the microfrontends are configured. diff --git a/kr/vars b/kr/vars index d4e5a27a..f5c1e65e 100644 --- a/kr/vars +++ b/kr/vars @@ -3,7 +3,7 @@ BROKER_URL=https://${BROKER_ID} PROXY_ID=${SITE_ID}.${BROKER_ID} FOCUS_BEAM_SECRET_SHORT="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 20)" FOCUS_RETRY_COUNT=${FOCUS_RETRY_COUNT:-64} -SUPPORT_EMAIL=arturo.macias@dkfz-heidelberg.de +SUPPORT_EMAIL=p.delpy@dkfz-heidelberg.de PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem BROKER_URL_FOR_PREREQ=$BROKER_URL diff --git a/lib/check-bridgehead.sh b/lib/check-bridgehead.sh new file mode 100755 index 00000000..57ca537c --- /dev/null +++ b/lib/check-bridgehead.sh @@ -0,0 +1,74 @@ +#!/bin/bash +source lib/functions.sh + +log INFO "Running Bridgehead checks..." + +# Directory ownership +log INFO "Checking directory ownership..." +OWNERSHIP_OK=true +if ! checkOwner /srv/docker/bridgehead bridgehead &> /dev/null; then + log ERROR "Wrong ownership for /srv/docker/bridgehead." + log INFO "Hint: Run 'sudo chown -R bridgehead /srv/docker/bridgehead'." + OWNERSHIP_OK=false +fi +if ! checkOwner /etc/bridgehead bridgehead &> /dev/null; then + log ERROR "Wrong ownership for /etc/bridgehead." + log INFO "Hint: Run 'sudo chown -R bridgehead /etc/bridgehead'." + OWNERSHIP_OK=false +fi + +if [ "$OWNERSHIP_OK" = true ]; then + log INFO "Directory ownership is correct." +fi + +# Git repository status +log INFO "Checking Git repository status..." +GIT_OK=true +if [ -d "/etc/bridgehead/.git" ]; then + if [ -n "$(git -C "/etc/bridgehead" status --porcelain)" ]; then + log ERROR "The config repo at /etc/bridgehead is modified.\n$(git -C /etc/bridgehead status -s)" + log INFO "Hint: Review your changes with git diff if they are already upstreamed use git stash and git pull to update the repo" + GIT_OK=false + fi +fi +if [ -n "$(git -C "$(pwd)" status --porcelain)" ]; then + log ERROR "$(pwd) is modified. \n$(git -C "$(pwd)" status -s)" + log INFO "Hint: If these are site specific changes to docker compose files consider moving them to $PROJECT/docker-compose.override.yml which is ignored by git." + log INFO " If they are already upstreamed use git stash and git pull to update the repo" + GIT_OK=false +fi + +if [ "$GIT_OK" = true ]; then + log INFO "Git repositories are clean." +fi + +# Git remote connection +log INFO "Checking Git remote connection..." +GIT_REMOTE_OK=true +if [ -d "/etc/bridgehead/.git" ]; then + if ! git -C "/etc/bridgehead" fetch --dry-run >/dev/null 2>&1; then + log ERROR "Cannot connect to the Git remote for /etc/bridgehead." + log INFO "Hint: Check your network connection and Git remote configuration for /etc/bridgehead." + GIT_REMOTE_OK=false + fi +fi +if [ -d "$(pwd)/.git" ]; then + if ! git -C "$(pwd)" fetch --dry-run >/dev/null 2>&1; then + log ERROR "Cannot connect to the Git remote for $(pwd)." + log INFO "Hint: Check your network connection and Git remote configuration for $(pwd)." + GIT_REMOTE_OK=false + fi +fi + +if [ "$GIT_REMOTE_OK" = true ]; then + log INFO "Git remote connection successful." +fi + +if [ "$OWNERSHIP_OK" = true ] && [ "$GIT_OK" = true ] && [ "$GIT_REMOTE_OK" = true ]; then + log INFO "All checks passed." + exit 0 +else + log ERROR "Some checks failed. Please review the hints and fix the issues." + log ERROR "Without fixing these issues bridgehead updates may not work correctly." + exit 1 +fi diff --git a/lib/functions.sh b/lib/functions.sh index ffdc2342..520d86aa 100644 --- a/lib/functions.sh +++ b/lib/functions.sh @@ -53,8 +53,8 @@ checkOwner(){ } printUsage() { - echo "Usage: bridgehead start|stop|logs|docker-logs|is-running|update|install|uninstall|adduser|enroll PROJECTNAME" - echo "PROJECTNAME should be one of ccp|bbmri|cce|itcc|kr|dhki" + echo "Usage: bridgehead start|stop|logs|docker-logs|is-running|update|check|install|uninstall|adduser|enroll PROJECTNAME" + echo "PROJECTNAME should be one of ccp|bbmri|cce|itcc|kr|dhki|nngm" } checkRequirements() { @@ -301,19 +301,33 @@ function sync_secrets() { if [[ $secret_sync_args == "" ]]; then return fi + + if [ "$PROJECT" == "bbmri" ]; then + # If the project is BBMRI, use the BBMRI-ERIC broker and not the GBN broker + proxy_id=$ERIC_PROXY_ID + broker_url=$ERIC_BROKER_URL + broker_id=$ERIC_BROKER_ID + root_crt_file="/srv/docker/bridgehead/bbmri/modules/${ERIC_ROOT_CERT}.root.crt.pem" + else + proxy_id=$PROXY_ID + broker_url=$BROKER_URL + broker_id=$BROKER_ID + root_crt_file="/srv/docker/bridgehead/$PROJECT/root.crt.pem" + fi + mkdir -p /var/cache/bridgehead/secrets/ || fail_and_report 1 "Failed to create '/var/cache/bridgehead/secrets/'. Please run sudo './bridgehead install $PROJECT' again." touch /var/cache/bridgehead/secrets/oidc docker run --rm \ -v /var/cache/bridgehead/secrets/oidc:/usr/local/cache \ -v $PRIVATEKEYFILENAME:/run/secrets/privkey.pem:ro \ - -v /srv/docker/bridgehead/$PROJECT/root.crt.pem:/run/secrets/root.crt.pem:ro \ + -v $root_crt_file:/run/secrets/root.crt.pem:ro \ -v /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro \ -e TLS_CA_CERTIFICATES_DIR=/conf/trusted-ca-certs \ -e NO_PROXY=localhost,127.0.0.1 \ -e ALL_PROXY=$HTTPS_PROXY_FULL_URL \ - -e PROXY_ID=$PROXY_ID \ - -e BROKER_URL=$BROKER_URL \ - -e OIDC_PROVIDER=secret-sync-central.oidc-client-enrollment.$BROKER_ID \ + -e PROXY_ID=$proxy_id \ + -e BROKER_URL=$broker_url \ + -e OIDC_PROVIDER=secret-sync-central.central-secret-sync.$broker_id \ -e SECRET_DEFINITIONS=$secret_sync_args \ docker.verbis.dkfz.de/cache/samply/secret-sync-local:latest @@ -322,6 +336,77 @@ function sync_secrets() { set +a # Export variables in the regular way } +function secret_sync_gitlab_token() { + if [[ "$PROJECT" != "dktk" && "$PROJECT" != "bbmri" ]]; then + log "INFO" "Not running Secret Sync for project minimal" + return + fi + # Map the origin of the git repository /etc/bridgehead to the prefix recognized by Secret Sync + local gitlab + case "$(git -C /etc/bridgehead remote get-url origin)" in + *git.verbis.dkfz.de*) gitlab=verbis;; + *gitlab.bbmri-eric.eu*) gitlab=bbmri;; + *) + log "WARN" "Not running Secret Sync because the git repository /etc/bridgehead has unknown origin" + return + ;; + esac + + if [ "$PROJECT" == "bbmri" ]; then + # If the project is BBMRI, use the BBMRI-ERIC broker and not the GBN broker + proxy_id=$ERIC_PROXY_ID + broker_url=$ERIC_BROKER_URL + broker_id=$ERIC_BROKER_ID + root_crt_file="/srv/docker/bridgehead/bbmri/modules/${ERIC_ROOT_CERT}.root.crt.pem" + else + proxy_id=$PROXY_ID + broker_url=$BROKER_URL + broker_id=$BROKER_ID + root_crt_file="/srv/docker/bridgehead/$PROJECT/root.crt.pem" + fi + + # Create a temporary directory for Secret Sync that is valid per boot + secret_sync_tempdir="/tmp/bridgehead/secret-sync.boot-$(cat /proc/sys/kernel/random/boot_id)" + mkdir -p $secret_sync_tempdir + + # Use Secret Sync to validate the GitLab token in $secret_sync_tempdir/cache. + # If it is missing or expired, Secret Sync will create a new token and write it to the file. + # The git credential helper reads the token from the file during git pull. + log "INFO" "Running Secret Sync for the GitLab token (gitlab=$gitlab)" + docker pull docker.verbis.dkfz.de/cache/samply/secret-sync-local:latest # make sure we have the latest image + docker run --rm \ + -v $PRIVATEKEYFILENAME:/run/secrets/privkey.pem:ro \ + -v $root_crt_file:/run/secrets/root.crt.pem:ro \ + -v /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro \ + -v $secret_sync_tempdir:/secret-sync/ \ + -e CACHE_PATH=/secret-sync/gitlab-token \ + -e TLS_CA_CERTIFICATES_DIR=/conf/trusted-ca-certs \ + -e NO_PROXY=localhost,127.0.0.1 \ + -e ALL_PROXY=$HTTPS_PROXY_FULL_URL \ + -e PROXY_ID=$proxy_id \ + -e BROKER_URL=$broker_url \ + -e GITLAB_PROJECT_ACCESS_TOKEN_PROVIDER=secret-sync-central.central-secret-sync.$broker_id \ + -e SECRET_DEFINITIONS=GitLabProjectAccessToken:BRIDGEHEAD_CONFIG_REPO_TOKEN:$gitlab \ + docker.verbis.dkfz.de/cache/samply/secret-sync-local:latest + if [ $? -eq 0 ]; then + log "INFO" "Secret Sync was successful" + # In the past we used to hardcode tokens into the repository URL. We have to remove those now for the git credential helper to become effective. + CLEAN_REPO="$(git -C /etc/bridgehead remote get-url origin | sed -E 's|https://[^@]+@|https://|')" + git -C /etc/bridgehead remote set-url origin "$CLEAN_REPO" + # Set the git credential helper + git -C /etc/bridgehead config credential.helper /srv/docker/bridgehead/lib/gitlab-token-helper.sh + else + log "WARN" "Secret Sync failed" + # Remove the git credential helper + git -C /etc/bridgehead config --unset credential.helper + fi + + # In the past the git credential helper was also set for /srv/docker/bridgehead but never used. + # Let's remove it to avoid confusion. This line can be removed at some point the future when we + # believe that it was removed on all/most production servers. + git -C /srv/docker/bridgehead config --unset credential.helper +} + capitalize_first_letter() { input="$1" capitalized="$(tr '[:lower:]' '[:upper:]' <<< ${input:0:1})${input:1}" @@ -369,7 +454,3 @@ generate_simple_password(){ local combined_text="This is a salt string to generate one consistent password for ${seed_text}. It is not required to be secret." echo "${combined_text}" | sha1sum | openssl pkeyutl -sign -inkey "/etc/bridgehead/pki/${SITE_ID}.priv.pem" 2> /dev/null | base64 | head -c 26 | sed 's/[+\/]/A/g' } - -docker_jq() { - docker run --rm -i docker.verbis.dkfz.de/cache/jqlang/jq:latest "$@" -} diff --git a/lib/gitlab-token-helper.sh b/lib/gitlab-token-helper.sh index e6180292..1aa7a625 100755 --- a/lib/gitlab-token-helper.sh +++ b/lib/gitlab-token-helper.sh @@ -2,7 +2,7 @@ [ "$1" = "get" ] || exit -source /var/cache/bridgehead/secrets/gitlab_token +source "/tmp/bridgehead/secret-sync.boot-$(cat /proc/sys/kernel/random/boot_id)/gitlab-token" # Any non-empty username works, only the token matters cat << EOF diff --git a/lib/install-bridgehead.sh b/lib/install-bridgehead.sh index 38c38749..f7121537 100755 --- a/lib/install-bridgehead.sh +++ b/lib/install-bridgehead.sh @@ -41,6 +41,20 @@ if [ ! -z "$NNGM_CTS_APIKEY" ] && [ -z "$NNGM_AUTH" ]; then add_basic_auth_user "nngm" $generated_passwd "NNGM_AUTH" $PROJECT fi +if [ -z "$TRANSFAIR_AUTH" ]; then + if [[ -n "$TTP_URL" || -n "$EXCHANGE_ID_SYSTEM" ]]; then + log "INFO" "Now generating basic auth user for transfair API (see adduser in bridgehead for more information). " + generated_passwd="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32)" + add_basic_auth_user "transfair" $generated_passwd "TRANSFAIR_AUTH" $PROJECT + fi +fi + +if [ "$ENABLE_EXPORTER" == "true" ] && [ -z "$EXPORTER_USER" ]; then + log "INFO" "Now generating basic auth for the exporter and reporter (see adduser in bridgehead for more information)." + generated_passwd="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32)" + add_basic_auth_user $PROJECT $generated_passwd "EXPORTER_USER" $PROJECT +fi + log "INFO" "Registering system units for bridgehead and bridgehead-update" cp -v \ lib/systemd/bridgehead\@.service \ diff --git a/lib/prepare-system.sh b/lib/prepare-system.sh index b6aba52b..6ff4eb73 100755 --- a/lib/prepare-system.sh +++ b/lib/prepare-system.sh @@ -55,6 +55,9 @@ case "$PROJECT" in cce) site_configuration_repository_middle="git.verbis.dkfz.de/cce-sites/" ;; + pscc) + site_configuration_repository_middle="git.verbis.dkfz.de/pscc-sites/" + ;; itcc) site_configuration_repository_middle="git.verbis.dkfz.de/itcc-sites/" ;; @@ -67,6 +70,9 @@ case "$PROJECT" in dhki) site_configuration_repository_middle="git.verbis.dkfz.de/dhki/" ;; + nngm) + site_configuration_repository_middle="git.verbis.dkfz.de/nngm/" + ;; minimal) site_configuration_repository_middle="git.verbis.dkfz.de/minimal-bridgehead-configs/" ;; diff --git a/lib/update-bridgehead.sh b/lib/update-bridgehead.sh index ae097168..8ae3bde7 100755 --- a/lib/update-bridgehead.sh +++ b/lib/update-bridgehead.sh @@ -33,43 +33,7 @@ export SITE_ID checkOwner /srv/docker/bridgehead bridgehead || fail_and_report 1 "Update failed: Wrong permissions in /srv/docker/bridgehead" checkOwner /etc/bridgehead bridgehead || fail_and_report 1 "Update failed: Wrong permissions in /etc/bridgehead" -# Use Secret Sync to validate the GitLab token in /var/cache/bridgehead/secrets/gitlab_token. -# If it is missing or expired, Secret Sync will create a new token and write it to the file. -# The git credential helper reads the token from the file during git pull. -mkdir -p /var/cache/bridgehead/secrets -touch /var/cache/bridgehead/secrets/gitlab_token # the file has to exist to be mounted correctly in the Docker container -log "INFO" "Running Secret Sync for the GitLab token" -docker pull docker.verbis.dkfz.de/cache/samply/secret-sync-local:latest # make sure we have the latest image -docker run --rm \ - -v /var/cache/bridgehead/secrets/gitlab_token:/usr/local/cache \ - -v $PRIVATEKEYFILENAME:/run/secrets/privkey.pem:ro \ - -v /srv/docker/bridgehead/$PROJECT/root.crt.pem:/run/secrets/root.crt.pem:ro \ - -v /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro \ - -e TLS_CA_CERTIFICATES_DIR=/conf/trusted-ca-certs \ - -e NO_PROXY=localhost,127.0.0.1 \ - -e ALL_PROXY=$HTTPS_PROXY_FULL_URL \ - -e PROXY_ID=$PROXY_ID \ - -e BROKER_URL=$BROKER_URL \ - -e GITLAB_PROJECT_ACCESS_TOKEN_PROVIDER=secret-sync-central.oidc-client-enrollment.$BROKER_ID \ - -e SECRET_DEFINITIONS=GitLabProjectAccessToken:BRIDGEHEAD_CONFIG_REPO_TOKEN: \ - docker.verbis.dkfz.de/cache/samply/secret-sync-local:latest -if [ $? -eq 0 ]; then - log "INFO" "Secret Sync was successful" - # In the past we used to hardcode tokens into the repository URL. We have to remove those now for the git credential helper to become effective. - CLEAN_REPO="$(git -C /etc/bridgehead remote get-url origin | sed -E 's|https://[^@]+@|https://|')" - git -C /etc/bridgehead remote set-url origin "$CLEAN_REPO" - # Set the git credential helper - git -C /etc/bridgehead config credential.helper /srv/docker/bridgehead/lib/gitlab-token-helper.sh -else - log "WARN" "Secret Sync failed" - # Remove the git credential helper - git -C /etc/bridgehead config --unset credential.helper -fi - -# In the past the git credential helper was also set for /srv/docker/bridgehead but never used. -# Let's remove it to avoid confusion. This line can be removed at some point the future when we -# believe that it was removed on all/most production servers. -git -C /srv/docker/bridgehead config --unset credential.helper +secret_sync_gitlab_token CHANGES="" diff --git a/minimal/modules/dnpm-node-compose.yml b/minimal/modules/dnpm-node-compose.yml index 8c2b146e..938cc9ca 100644 --- a/minimal/modules/dnpm-node-compose.yml +++ b/minimal/modules/dnpm-node-compose.yml @@ -43,7 +43,7 @@ services: - "traefik.http.routers.dnpm-auth.tls=true" dnpm-portal: - image: ghcr.io/dnpm-dip/portal:latest + image: ghcr.io/dnpm-dip/portal:${DNPM_IMAGE_TAG:-latest} container_name: bridgehead-dnpm-portal environment: - NUXT_API_URL=http://dnpm-backend:9000/ @@ -58,7 +58,7 @@ services: dnpm-backend: container_name: bridgehead-dnpm-backend - image: ghcr.io/dnpm-dip/backend:latest + image: ghcr.io/dnpm-dip/api-gateway:latest environment: - LOCAL_SITE=${ZPM_SITE}:${SITE_NAME} # Format: {Site-ID}:{Site-name}, e.g. UKT:Tübingen - RD_RANDOM_DATA=${DNPM_SYNTH_NUM:--1} @@ -66,6 +66,7 @@ services: - HATEOAS_HOST=https://${HOST} - CONNECTOR_TYPE=broker - AUTHUP_URL=robot://system:${DNPM_AUTHUP_SECRET}@http://dnpm-authup:3000 + - TZ=Europe/Berlin volumes: - /etc/bridgehead/dnpm/config:/dnpm_config - /var/cache/bridgehead/dnpm/backend-data:/dnpm_data diff --git a/modules/scout-compose.yml b/modules/scout-compose.yml new file mode 100644 index 00000000..e3e7e83f --- /dev/null +++ b/modules/scout-compose.yml @@ -0,0 +1,40 @@ +volumes: + scout-blaze-data: + +services: + traefik: + labels: + - "traefik.http.middlewares.additional-users-auth.basicauth.users=${SCOUT_BASIC_AUTH_USERS}" + + scout-blaze: + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} + container_name: bridgehead-scout-blaze + environment: + BASE_URL: "http://bridgehead-scout-blaze:8080" + ENFORCE_REFERENTIAL_INTEGRITY: "false" + volumes: + - "scout-blaze-data:/app/data" + labels: + - "traefik.enable=true" + - "traefik.http.routers.scout-blaze.rule=PathPrefix(`/scout-blaze`)" + - "traefik.http.middlewares.scout-blaze-stripprefix.stripprefix.prefixes=/scout-blaze" + - "traefik.http.services.scout-blaze.loadbalancer.server.port=8080" + - "traefik.http.routers.scout-blaze.middlewares=scout-blaze-stripprefix,additional-users-auth" + - "traefik.http.routers.scout-blaze.tls=true" + + scout: + image: samply/scout:main + container_name: bridgehead-scout + configs: + - scout.toml + labels: + - "traefik.enable=true" + - "traefik.http.routers.scout.rule=PathPrefix(`/scout`)" + - "traefik.http.services.scout.loadbalancer.server.port=8080" + - "traefik.http.routers.scout.middlewares=additional-users-auth" + - "traefik.http.routers.scout.tls=true" + +configs: + scout.toml: + content: | + fhir_base_url = "http://scout-blaze:8080/fhir" diff --git a/modules/scout-setup.sh b/modules/scout-setup.sh new file mode 100644 index 00000000..5a3b0d96 --- /dev/null +++ b/modules/scout-setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +function scoutSetup() { + if [[ -n "$ENABLE_SCOUT" && -n "$SCOUT_BASIC_AUTH_USERS" ]]; then + echo "Starting scout." + OVERRIDE+=" -f ./modules/scout-compose.yml" + fi +} diff --git a/modules/ssh-tunnel-compose.yml b/modules/ssh-tunnel-compose.yml new file mode 100644 index 00000000..5ca99895 --- /dev/null +++ b/modules/ssh-tunnel-compose.yml @@ -0,0 +1,17 @@ +version: "3.7" + +services: + ssh-tunnel: + image: docker.verbis.dkfz.de/cache/samply/ssh-tunnel + container_name: bridgehead-ccp-ssh-tunnel + environment: + SSH_TUNNEL_USERNAME: "${SSH_TUNNEL_USERNAME}" + SSH_TUNNEL_HOST: "${SSH_TUNNEL_HOST}" + SSH_TUNNEL_PORT: "${SSH_TUNNEL_PORT:-22}" + volumes: + - "/etc/bridgehead/ssh-tunnel.conf:/ssh-tunnel.conf:ro" + secrets: + - privkey +secrets: + privkey: + file: /etc/bridgehead/pki/ssh-tunnel.priv.pem diff --git a/modules/ssh-tunnel-setup.sh b/modules/ssh-tunnel-setup.sh new file mode 100644 index 00000000..4ecf5a33 --- /dev/null +++ b/modules/ssh-tunnel-setup.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +if [ -n "$ENABLE_SSH_TUNNEL" ]; then + log INFO "SSH Tunnel setup detected -- will start SSH Tunnel." + OVERRIDE+=" -f ./modules/ssh-tunnel-compose.yml" +fi diff --git a/modules/ssh-tunnel.md b/modules/ssh-tunnel.md new file mode 100644 index 00000000..4240b195 --- /dev/null +++ b/modules/ssh-tunnel.md @@ -0,0 +1,19 @@ +# SSH Tunnel Module + +This module enables SSH tunneling capabilities for the Bridgehead installation. +The primary use case for this is to connect bridgehead components that are hosted externally due to security concerns. +To connect the new components to the locally running bridgehead infra one is supposed to write a docker-compose.override.yml changing the urls to point to the corresponding forwarded port of the ssh-tunnel container. + +## Configuration Variables + +- `ENABLE_SSH_TUNNEL`: Required to enable the module +- `SSH_TUNNEL_USERNAME`: Username for SSH connection +- `SSH_TUNNEL_HOST`: Target host for SSH tunnel +- `SSH_TUNNEL_PORT`: SSH port (defaults to 22) + +## Configuration Files + +The module requires the following files to be present: + +- `/etc/bridgehead/ssh-tunnel.conf`: SSH tunnel configuration file. Detailed information can be found [here](https://github.com/samply/ssh-tunnel?tab=readme-ov-file#configuration). +- `/etc/bridgehead/pki/ssh-tunnel.priv.pem`: The SSH private key used to connect to the `SSH_TUNNEL_HOST`. **Passphrases for the key are not supported!** \ No newline at end of file diff --git a/modules/transfair-compose.yml b/modules/transfair-compose.yml index 9af09a6c..e827603e 100644 --- a/modules/transfair-compose.yml +++ b/modules/transfair-compose.yml @@ -5,8 +5,13 @@ services: container_name: bridgehead-transfair environment: # NOTE: Those 3 variables need only to be passed if their set, otherwise transfair will complain about empty url values - - INSTITUTE_TTP_URL - - INSTITUTE_TTP_API_KEY + - TTP_URL + - TTP_ML_API_KEY + - TTP_GW_SOURCE + - TTP_GW_EPIX_DOMAIN + - TTP_GW_GPAS_DOMAIN + - TTP_GW_GPAS_URL + - TTP_AUTH - PROJECT_ID_SYSTEM - FHIR_REQUEST_URL=${FHIR_REQUEST_URL} - FHIR_INPUT_URL=${FHIR_INPUT_URL} @@ -17,11 +22,28 @@ services: - EXCHANGE_ID_SYSTEM=${EXCHANGE_ID_SYSTEM:-SESSION_ID} - DATABASE_URL=sqlite://transfair/data_requests.sql?mode=rwc - RUST_LOG=${RUST_LOG:-info} + - TLS_CA_CERTIFICATES_DIR=/conf/trusted-ca-certs + - TLS_DISABLE=${TRANSFAIR_TLS_DISABLE:-false} + - NO_PROXY=${TRANSFAIR_NO_PROXIES} + - ALL_PROXY=http://forward_proxy:3128 + command: dic ${TTP_TYPE} volumes: - /var/cache/bridgehead/${PROJECT}/transfair:/transfair + - /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro + labels: + - "traefik.enable=true" + - "traefik.http.middlewares.transfair-strip.stripprefix.prefixes=/transfair" + - "traefik.http.routers.transfair.middlewares=transfair-strip,transfair-auth" + - "traefik.http.routers.transfair.rule=PathPrefix(`/transfair`)" + - "traefik.http.services.transfair.loadbalancer.server.port=8080" + - "traefik.http.routers.transfair.tls=true" + + traefik: + labels: + - "traefik.http.middlewares.transfair-auth.basicauth.users=${TRANSFAIR_AUTH}" transfair-input-blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.28 + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} container_name: bridgehead-transfair-input-blaze environment: BASE_URL: "http://bridgehead-transfair-input-blaze:8080" @@ -32,12 +54,19 @@ services: volumes: - "transfair-input-blaze-data:/app/data" profiles: ["transfair-input-blaze"] + labels: + - "traefik.enable=true" + - "traefik.http.routers.transfair-input-blaze.rule=PathPrefix(`/data-delivery`)" + - "traefik.http.middlewares.transfair-input-strip.stripprefix.prefixes=/data-delivery" + - "traefik.http.services.transfair-input-blaze.loadbalancer.server.port=8080" + - "traefik.http.routers.transfair-input-blaze.middlewares=transfair-input-strip,transfair-auth" + - "traefik.http.routers.transfair-input-blaze.tls=true" transfair-request-blaze: - image: docker.verbis.dkfz.de/cache/samply/blaze:0.28 - container_name: bridgehead-transfair-requests-blaze + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} + container_name: bridgehead-transfair-request-blaze environment: - BASE_URL: "http://bridgehead-transfair-requests-blaze:8080" + BASE_URL: "http://bridgehead-transfair-request-blaze:8080" JAVA_TOOL_OPTIONS: "-Xmx1024m" DB_BLOCK_CACHE_SIZE: 1024 CQL_EXPR_CACHE_SIZE: 8 @@ -45,6 +74,13 @@ services: volumes: - "transfair-request-blaze-data:/app/data" profiles: ["transfair-request-blaze"] + labels: + - "traefik.enable=true" + - "traefik.http.routers.transfair-request-blaze.rule=PathPrefix(`/data-requests`)" + - "traefik.http.middlewares.transfair-request-strip.stripprefix.prefixes=/data-requests" + - "traefik.http.services.transfair-request-blaze.loadbalancer.server.port=8080" + - "traefik.http.routers.transfair-request-blaze.middlewares=transfair-request-strip,transfair-auth" + - "traefik.http.routers.transfair-request-blaze.tls=true" volumes: transfair-input-blaze-data: diff --git a/modules/transfair-setup.sh b/modules/transfair-setup.sh index 58f7331b..8a36ee17 100755 --- a/modules/transfair-setup.sh +++ b/modules/transfair-setup.sh @@ -1,7 +1,7 @@ #!/bin/bash -e function transfairSetup() { - if [[ -n "$INSTITUTE_TTP_URL" || -n "$EXCHANGE_ID_SYSTEM" ]]; then + if [[ -n "$TTP_URL" || -n "$EXCHANGE_ID_SYSTEM" ]]; then echo "Starting transfair." OVERRIDE+=" -f ./modules/transfair-compose.yml" if [ -n "$FHIR_INPUT_URL" ]; then @@ -15,8 +15,21 @@ function transfairSetup() { log INFO "TransFAIR request fhir store set to external $FHIR_REQUEST_URL" else log INFO "TransFAIR request fhir store not set writing to internal blaze" - FHIR_REQUEST_URL="http://transfair-requests-blaze:8080" + FHIR_REQUEST_URL="http://transfair-request-blaze:8080" OVERRIDE+=" --profile transfair-request-blaze" fi + if [ -n "$TTP_GW_SOURCE" ]; then + log INFO "TransFAIR configured with greifswald as ttp" + TTP_TYPE="greifswald" + elif [ -n "$TTP_ML_API_KEY" ]; then + log INFO "TransFAIR configured with mainzelliste as ttp" + TTP_TYPE="mainzelliste" + else + log INFO "TransFAIR configured without ttp" + fi + TRANSFAIR_NO_PROXIES="transfair-input-blaze,blaze,transfair-requests-blaze" + if [ -n "${TRANSFAIR_NO_PROXY}" ]; then + TRANSFAIR_NO_PROXIES+=",${TRANSFAIR_NO_PROXY}" + fi fi } diff --git a/nngm/docker-compose.yml b/nngm/docker-compose.yml new file mode 100644 index 00000000..69cbabef --- /dev/null +++ b/nngm/docker-compose.yml @@ -0,0 +1,65 @@ +version: "3.7" + +services: + blaze: + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} + container_name: bridgehead-nngm-blaze + environment: + BASE_URL: "http://bridgehead-nngm-blaze:8080" + JAVA_TOOL_OPTIONS: "-Xmx${BLAZE_MEMORY_CAP:-4096}m" + DB_RESOURCE_CACHE_SIZE: ${BLAZE_RESOURCE_CACHE_CAP:-2500000} + DB_BLOCK_CACHE_SIZE: ${BLAZE_MEMORY_CAP} + CQL_EXPR_CACHE_SIZE: ${BLAZE_CQL_CACHE_CAP:-32} + ENFORCE_REFERENTIAL_INTEGRITY: "false" + volumes: + - "blaze-data:/app/data" + labels: + - "traefik.enable=true" + - "traefik.http.routers.blaze_nngm.rule=PathPrefix(`/nngm-localdatamanagement`)" + - "traefik.http.middlewares.nngm_b_strip.stripprefix.prefixes=/nngm-localdatamanagement" + - "traefik.http.services.blaze_nngm.loadbalancer.server.port=8080" + - "traefik.http.routers.blaze_nngm.middlewares=nngm_b_strip,auth" + - "traefik.http.routers.blaze_nngm.tls=true" + + focus: + image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG} + container_name: bridgehead-focus + environment: + - API_KEY=${FOCUS_BEAM_SECRET_SHORT} + - BEAM_APP_ID_LONG=focus.${PROXY_ID} + - PROXY_ID=${PROXY_ID} + - BLAZE_URL=http://bridgehead-nngm-blaze:8080/fhir/ + - BEAM_PROXY_URL=http://beam-proxy:8081 + - RETRY_COUNT=${FOCUS_RETRY_COUNT} + - EPSILON=0.28 + - ENDPOINT_TYPE=${FOCUS_ENDPOINT_TYPE:-blaze} + - CQL_PROJECTS_ENABLED + depends_on: + - "beam-proxy" + - "blaze" + + beam-proxy: + image: docker.verbis.dkfz.de/cache/samply/beam-proxy:${BEAM_TAG} + container_name: bridgehead-beam-proxy + environment: + BROKER_URL: ${BROKER_URL} + PROXY_ID: ${PROXY_ID} + APP_focus_KEY: ${FOCUS_BEAM_SECRET_SHORT} + PRIVKEY_FILE: /run/secrets/proxy.pem + ALL_PROXY: http://forward_proxy:3128 + TLS_CA_CERTIFICATES_DIR: /conf/trusted-ca-certs + ROOTCERT_FILE: /conf/root.crt.pem + secrets: + - proxy.pem + depends_on: + - "forward_proxy" + volumes: + - /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro + - /srv/docker/bridgehead/nngm/root.crt.pem:/conf/root.crt.pem:ro + +volumes: + blaze-data: + +secrets: + proxy.pem: + file: /etc/bridgehead/pki/${SITE_ID}.priv.pem diff --git a/nngm/modules/exporter-compose.yml b/nngm/modules/exporter-compose.yml new file mode 100644 index 00000000..6a11353b --- /dev/null +++ b/nngm/modules/exporter-compose.yml @@ -0,0 +1,72 @@ +version: "3.7" + +services: + exporter: + image: docker.verbis.dkfz.de/ccp/dktk-exporter:latest + container_name: bridgehead-nngm-exporter + environment: + JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" + LOG_LEVEL: "INFO" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" # Set in exporter-setup.sh + CROSS_ORIGINS: "https://${HOST}" + EXPORTER_DB_USER: "exporter" + EXPORTER_DB_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh + EXPORTER_DB_URL: "jdbc:postgresql://exporter-db:5432/exporter" + HTTP_RELATIVE_PATH: "/nngm-exporter" + SITE: "${SITE_ID}" + HTTP_SERVLET_REQUEST_SCHEME: "https" + OPAL_PASSWORD: "${EXPORTER_OPAL_PASSWORD}" + labels: + - "traefik.enable=true" + - "traefik.http.routers.exporter_nngm.rule=PathPrefix(`/nngm-exporter`)" + - "traefik.http.services.exporter_nngm.loadbalancer.server.port=8092" + - "traefik.http.routers.exporter_nngm.tls=true" + - "traefik.http.middlewares.exporter_nngm_strip.stripprefix.prefixes=/nngm-exporter" + - "traefik.http.routers.exporter_nngm.middlewares=exporter_nngm_strip" + volumes: + - "/var/cache/bridgehead/nngm/exporter-files:/app/exporter-files/output" + + exporter-db: + image: docker.verbis.dkfz.de/cache/postgres:${POSTGRES_TAG} + container_name: bridgehead-nngm-exporter-db + environment: + POSTGRES_USER: "exporter" + POSTGRES_PASSWORD: "${EXPORTER_DB_PASSWORD}" # Set in exporter-setup.sh + POSTGRES_DB: "exporter" + volumes: + # Consider removing this volume once we find a solution to save Lens-queries to be executed in the explorer. + - "/var/cache/bridgehead/nngm/exporter-db:/var/lib/postgresql/data" + + reporter: + image: docker.verbis.dkfz.de/ccp/dktk-reporter:latest + container_name: bridgehead-nngm-reporter + environment: + JAVA_OPTS: "-Xms1G -Xmx8G -XX:+UseG1GC" + LOG_LEVEL: "INFO" + CROSS_ORIGINS: "https://${HOST}" + HTTP_RELATIVE_PATH: "/nngm-reporter" + SITE: "${SITE_ID}" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" # Set in exporter-setup.sh + EXPORTER_URL: "http://exporter:8092" + LOG_FHIR_VALIDATION: "false" + HTTP_SERVLET_REQUEST_SCHEME: "https" + + # In this initial development state of the bridgehead, we are trying to have so many volumes as possible. + # However, in the first executions in the CCP sites, this volume seems to be very important. A report is + # a process that can take several hours, because it depends on the exporter. + # There is a risk that the bridgehead restarts, losing the already created export. + + volumes: + - "/var/cache/bridgehead/nngm/reporter-files:/app/reports" + labels: + - "traefik.enable=true" + - "traefik.http.routers.reporter_nngm.rule=PathPrefix(`/nngm-reporter`)" + - "traefik.http.services.reporter_nngm.loadbalancer.server.port=8095" + - "traefik.http.routers.reporter_nngm.tls=true" + - "traefik.http.middlewares.reporter_nngm_strip.stripprefix.prefixes=/nngm-reporter" + - "traefik.http.routers.reporter_nngm.middlewares=reporter_nngm_strip" + + focus: + environment: + EXPORTER_URL: "http://exporter:8092" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" diff --git a/nngm/modules/exporter-setup.sh b/nngm/modules/exporter-setup.sh new file mode 100644 index 00000000..9b947a60 --- /dev/null +++ b/nngm/modules/exporter-setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +if [ "$ENABLE_EXPORTER" == true ]; then + log INFO "Exporter setup detected -- will start Exporter service." + OVERRIDE+=" -f ./$PROJECT/modules/exporter-compose.yml" + EXPORTER_DB_PASSWORD="$(echo \"This is a salt string to generate one consistent password for the exporter. It is not required to be secret.\" | sha1sum | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 30)" + EXPORTER_API_KEY="$(echo \"This is a salt string to generate one consistent API KEY for the exporter. It is not required to be secret.\" | sha1sum | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 64)" +fi diff --git a/nngm/modules/teiler-compose.yml b/nngm/modules/teiler-compose.yml new file mode 100644 index 00000000..47a2ecc6 --- /dev/null +++ b/nngm/modules/teiler-compose.yml @@ -0,0 +1,73 @@ +version: "3.7" + +services: + + teiler-orchestrator: + image: docker.verbis.dkfz.de/cache/samply/teiler-orchestrator:latest + container_name: bridgehead-teiler-orchestrator + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_orchestrator_nngm.rule=PathPrefix(`/nngm-teiler`)" + - "traefik.http.services.teiler_orchestrator_nngm.loadbalancer.server.port=9000" + - "traefik.http.routers.teiler_orchestrator_nngm.tls=true" + - "traefik.http.middlewares.teiler_orchestrator_nngm_strip.stripprefix.prefixes=/nngm-teiler" + - "traefik.http.routers.teiler_orchestrator_nngm.middlewares=teiler_orchestrator_nngm_strip" + environment: + TEILER_BACKEND_URL: "/nngm-teiler-backend" + TEILER_DASHBOARD_URL: "/nngm-teiler-dashboard" + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE_LOWER_CASE}" + HTTP_RELATIVE_PATH: "/nngm-teiler" + + teiler-dashboard: + image: docker.verbis.dkfz.de/cache/samply/teiler-dashboard:${TEILER_DASHBOARD_TAG} + container_name: bridgehead-teiler-dashboard + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_dashboard_nngm.rule=PathPrefix(`/nngm-teiler-dashboard`)" + - "traefik.http.services.teiler_dashboard_nngm.loadbalancer.server.port=80" + - "traefik.http.routers.teiler_dashboard_nngm.tls=true" + - "traefik.http.middlewares.teiler_dashboard_nngm_strip.stripprefix.prefixes=/nngm-teiler-dashboard" + - "traefik.http.routers.teiler_dashboard_nngm.middlewares=teiler_dashboard_nngm_strip" + environment: + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" + TEILER_BACKEND_URL: "/nngm-teiler-backend" + TEILER_DASHBOARD_URL: "/nngm-teiler-dashboard" + OIDC_URL: "${OIDC_URL}" + OIDC_CLIENT_ID: "${OIDC_PUBLIC_CLIENT_ID}" + OIDC_TOKEN_GROUP: "${OIDC_GROUP_CLAIM}" + TEILER_ADMIN_NAME: "${OPERATOR_FIRST_NAME} ${OPERATOR_LAST_NAME}" + TEILER_ADMIN_EMAIL: "${OPERATOR_EMAIL}" + TEILER_ADMIN_PHONE: "${OPERATOR_PHONE}" + TEILER_PROJECT: "${PROJECT}" + EXPORTER_API_KEY: "${EXPORTER_API_KEY}" + TEILER_ORCHESTRATOR_URL: "/nngm-teiler" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/nngm-teiler" + TEILER_USER: "${OIDC_USER_GROUP}" + TEILER_ADMIN: "${OIDC_ADMIN_GROUP}" + REPORTER_DEFAULT_TEMPLATE_ID: "ccp-qb" + EXPORTER_DEFAULT_TEMPLATE_ID: "ccp" + + +# TODO: Replace dktk-teiler-backend with nngm-teiler-backend + teiler-backend: + image: docker.verbis.dkfz.de/ccp/dktk-teiler-backend:latest + container_name: bridgehead-teiler-backend + labels: + - "traefik.enable=true" + - "traefik.http.routers.teiler_backend_nngm.rule=PathPrefix(`/nngm-teiler-backend`)" + - "traefik.http.services.teiler_backend_nngm.loadbalancer.server.port=8085" + - "traefik.http.routers.teiler_backend_nngm.tls=true" + - "traefik.http.middlewares.teiler_backend_nngm_strip.stripprefix.prefixes=/nngm-teiler-backend" + - "traefik.http.routers.teiler_backend_nngm.middlewares=teiler_backend_nngm_strip" + environment: + LOG_LEVEL: "INFO" + APPLICATION_PORT: "8085" + DEFAULT_LANGUAGE: "${TEILER_DEFAULT_LANGUAGE}" + TEILER_ORCHESTRATOR_HTTP_RELATIVE_PATH: "/nngm-teiler" + TEILER_ORCHESTRATOR_URL: "/nngm-teiler" + TEILER_DASHBOARD_DE_URL: "/nngm-teiler-dashboard/de" + TEILER_DASHBOARD_EN_URL: "/nngm-teiler-dashboard/en" + HTTP_PROXY: "http://forward_proxy:3128" + ENABLE_MTBA: "${ENABLE_MTBA}" + ENABLE_DATASHIELD: "${ENABLE_DATASHIELD}" + IDMANAGER_UPLOAD_APIKEY: "${IDMANAGER_UPLOAD_APIKEY}" # Only used to check if the ID Manager is active diff --git a/nngm/modules/teiler-setup.sh b/nngm/modules/teiler-setup.sh new file mode 100644 index 00000000..e74e429e --- /dev/null +++ b/nngm/modules/teiler-setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e + +if [ "$ENABLE_TEILER" == true ];then + log INFO "Teiler setup detected -- will start Teiler services." + OVERRIDE+=" -f ./$PROJECT/modules/teiler-compose.yml" + TEILER_DEFAULT_LANGUAGE=DE + TEILER_DEFAULT_LANGUAGE_LOWER_CASE=${TEILER_DEFAULT_LANGUAGE,,} +fi diff --git a/nngm/root.crt.pem b/nngm/root.crt.pem new file mode 100644 index 00000000..dbf6fc3b --- /dev/null +++ b/nngm/root.crt.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgIUWHMDQFPJR5y8RKZ5FC72iOOla4kwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAxMLQnJva2VyLVJvb3QwHhcNMjUxMDI3MTQwMjU1WhcNMzUx +MDI1MTQwMzI1WjAWMRQwEgYDVQQDEwtCcm9rZXItUm9vdDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKoghRqAo6s9xjDao+ZC9HpZDBgzOgRMRHrl352k +Y0Gti1p3m8ldwVQV+nlBE6g/Dowo+iaOwUBiHMHOI2BK7vqkGNp0tZ63ZKR4cyOD +hCDOl71lWxjYD5XmF7l/SbrLFfET0EEorhLDDOMuWrNpxKFfKdvhld6K5BZ3oSfH +/5W5y5jWRFWEYRzddzil2GOiU2vzAygA0I1nr5oHCgZoteDDXztAYHJ5vnPA9RNQ +YFoe/5fVOiJo869zYyBwMuY/dV5ff7eIe/HRKzFLZ6iJEOJcBFWx/aWEvj5gSWxS +x4OzkwoHsZOkRN9wSTXvdO5kPFzmPq8Nq7Hmw4tLVzP1eRECAwEAAaN7MHkwDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP9BHa86rz94 +nvMj2JhM5V3L3TWCMB8GA1UdIwQYMBaAFP9BHa86rz94nvMj2JhM5V3L3TWCMBYG +A1UdEQQPMA2CC0Jyb2tlci1Sb290MA0GCSqGSIb3DQEBCwUAA4IBAQCkWBXRUGx5 +XFWEEAVbAMcEuXAr6+HtSs+NTORQ01LhNST8Z9HhOaAjfH/dJiLvOjHvOuiOK9y9 +ZGkIIwqkkbhlv1ZcfQBWXh+xDNbq9Q2MaIWY3ZzPTKFgNkxFcEF43MMB+o5pK1Bf +jJIiSxuEfM0yHg9o+jc3V3XRhU9leXNPkfJezTGfVuWr/B/kTmnQ8zrOCapB+NnX +vuu1ayNyXflDkj8Gg0X4TarxGhSP6Dpxd9ViEQD9DFG8q42bH0mYveHcAIUN0FJX +4F2NChiL7dCSFFe6xKdRFDtNe12JrHRjU1rMAcxhYjBRbqt2o2HfDPajSJrhRheY +T35rRWxDupkP +-----END CERTIFICATE----- \ No newline at end of file diff --git a/nngm/vars b/nngm/vars new file mode 100644 index 00000000..9468feea --- /dev/null +++ b/nngm/vars @@ -0,0 +1,32 @@ +BROKER_ID=broker.nngm.dkfz.de +BROKER_URL=https://${BROKER_ID} +PROXY_ID=${SITE_ID}.${BROKER_ID} +FOCUS_BEAM_SECRET_SHORT="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 20)" +FOCUS_RETRY_COUNT=${FOCUS_RETRY_COUNT:-64} +# TODO: Add real nNGM-Support email +SUPPORT_EMAIL=support-nngm@dkfz-heidelberg.de +PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem + +BROKER_URL_FOR_PREREQ=$BROKER_URL + +# TODO: Replace with nNGM OIDC Server +OIDC_USER_GROUP="NNGM_$(capitalize_first_letter ${SITE_ID})" +OIDC_ADMIN_GROUP="NNGM_$(capitalize_first_letter ${SITE_ID})_Verwalter" +OIDC_PSP_GROUP="NNGM_$(capitalize_first_letter ${SITE_ID})_PSP" +OIDC_PRIVATE_CLIENT_ID=${SITE_ID}-private +OIDC_PUBLIC_CLIENT_ID=${SITE_ID}-public +OIDC_URL="https://sso.verbis.dkfz.de/application/o/${OIDC_PUBLIC_CLIENT_ID}/" +OIDC_PRIVATE_URL="https://sso.verbis.dkfz.de/application/o/${OIDC_PRIVATE_CLIENT_ID}/" +OIDC_GROUP_CLAIM="groups" + +for module in $PROJECT/modules/*.sh +do + log DEBUG "sourcing $module" + source $module +done + +for module in modules/*.sh +do + log DEBUG "sourcing $module" + source $module +done diff --git a/pscc/docker-compose.yml b/pscc/docker-compose.yml new file mode 100644 index 00000000..f3343d4f --- /dev/null +++ b/pscc/docker-compose.yml @@ -0,0 +1,67 @@ +version: "3.7" + +services: + blaze: + image: docker.verbis.dkfz.de/cache/samply/blaze:${BLAZE_TAG} + container_name: bridgehead-pscc-blaze + environment: + BASE_URL: "http://bridgehead-pscc-blaze:8080" + JAVA_TOOL_OPTIONS: "-Xmx${BLAZE_MEMORY_CAP:-4096}m" + DB_RESOURCE_CACHE_SIZE: ${BLAZE_RESOURCE_CACHE_CAP:-2500000} + DB_BLOCK_CACHE_SIZE: ${BLAZE_MEMORY_CAP} + CQL_EXPR_CACHE_SIZE: ${BLAZE_CQL_CACHE_CAP:-32} + ENFORCE_REFERENTIAL_INTEGRITY: "false" + volumes: + - "blaze-data:/app/data" + labels: + - "traefik.enable=true" + - "traefik.http.routers.blaze_pscc.rule=PathPrefix(`/pscc-localdatamanagement`)" + - "traefik.http.middlewares.pscc_b_strip.stripprefix.prefixes=/pscc-localdatamanagement" + - "traefik.http.services.blaze_pscc.loadbalancer.server.port=8080" + - "traefik.http.routers.blaze_pscc.middlewares=pscc_b_strip,auth" + - "traefik.http.routers.blaze_pscc.tls=true" + + focus: + image: docker.verbis.dkfz.de/cache/samply/focus:${FOCUS_TAG} + container_name: bridgehead-focus + environment: + API_KEY: ${FOCUS_BEAM_SECRET_SHORT} + BEAM_APP_ID_LONG: focus.${PROXY_ID} + PROXY_ID: ${PROXY_ID} + BLAZE_URL: "http://bridgehead-pscc-blaze:8080/fhir/" + BEAM_PROXY_URL: http://beam-proxy:8081 + RETRY_COUNT: ${FOCUS_RETRY_COUNT} + EPSILON: 0.28 + ENDPOINT_TYPE: ${FOCUS_ENDPOINT_TYPE:-blaze} + depends_on: + - "beam-proxy" + - "blaze" + + beam-proxy: + image: docker.verbis.dkfz.de/cache/samply/beam-proxy:${BEAM_TAG} + container_name: bridgehead-beam-proxy + environment: + BROKER_URL: ${BROKER_URL} + PROXY_ID: ${PROXY_ID} + APP_focus_KEY: ${FOCUS_BEAM_SECRET_SHORT} + PRIVKEY_FILE: /run/secrets/proxy.pem + ALL_PROXY: http://forward_proxy:3128 + TLS_CA_CERTIFICATES_DIR: /conf/trusted-ca-certs + ROOTCERT_FILE: /conf/root.crt.pem + secrets: + - proxy.pem + depends_on: + - "forward_proxy" + volumes: + - /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro + - /srv/docker/bridgehead/pscc/root.crt.pem:/conf/root.crt.pem:ro + + landing: + profiles: [deactivated] + +volumes: + blaze-data: + +secrets: + proxy.pem: + file: /etc/bridgehead/pki/${SITE_ID}.priv.pem diff --git a/pscc/modules/lens-compose.yml b/pscc/modules/lens-compose.yml new file mode 100644 index 00000000..4571c7b2 --- /dev/null +++ b/pscc/modules/lens-compose.yml @@ -0,0 +1,40 @@ +version: "3.7" +services: + lens: + container_name: lens-federated-search + image: docker.verbis.dkfz.de/ccp/lens:${SITE_ID} + labels: + - "traefik.http.services.lens.loadbalancer.server.port=3000" + - "traefik.enable=true" + - "traefik.http.routers.lens.rule=Host(`${HOST}`)" + - "traefik.http.routers.lens.tls=true" + + spot: + image: samply/rustyspot:latest + platform: linux/amd64 + environment: + HTTP_PROXY: ${HTTP_PROXY_URL} + HTTPS_PROXY: ${HTTPS_PROXY_URL} + NO_PROXY: beam-proxy + BEAM_SECRET: "${FOCUS_BEAM_SECRET_SHORT}" + BEAM_PROXY_URL: http://beam-proxy:8081 + BEAM_APP_ID: "spot.${SITE_ID}.${BROKER_ID}" + CORS_ORIGIN: "https://${HOST}" + SITES: ${SITES} + TRANSFORM: LENS + PROJECT: pscc + BIND_ADDR: 0.0.0.0:8055 + depends_on: + - "beam-proxy" + labels: + - "traefik.enable=true" + - "traefik.http.services.spot.loadbalancer.server.port=8055" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowmethods=GET,OPTIONS,POST" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowheaders=content-type" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolalloworiginlist=https://${HOST}" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolallowcredentials=true" + - "traefik.http.middlewares.corsheaders2.headers.accesscontrolmaxage=-1" + - "traefik.http.routers.spot.rule=Host(`${HOST}`) && PathPrefix(`/prod`)" + - "traefik.http.middlewares.stripprefix_spot.stripprefix.prefixes=/prod" + - "traefik.http.routers.spot.tls=true" + - "traefik.http.routers.spot.middlewares=corsheaders2,stripprefix_spot,auth" \ No newline at end of file diff --git a/pscc/modules/lens-setup.sh b/pscc/modules/lens-setup.sh new file mode 100644 index 00000000..c19dc4bc --- /dev/null +++ b/pscc/modules/lens-setup.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +if [ -n "$ENABLE_LENS" ];then + OVERRIDE+=" -f ./$PROJECT/modules/lens-compose.yml" +fi \ No newline at end of file diff --git a/pscc/modules/osiris2fhir-compose.yml b/pscc/modules/osiris2fhir-compose.yml new file mode 100644 index 00000000..a5fbffbe --- /dev/null +++ b/pscc/modules/osiris2fhir-compose.yml @@ -0,0 +1,13 @@ +services: + osiris2fhir: + container_name: bridgehead-osiris2fhir + image: docker.verbis.dkfz.de/ccp/osiris2fhir:${SITE_ID} + environment: + SALT: ${LOCAL_SALT} + labels: + - "traefik.enable=true" + - "traefik.http.routers.osiris2fhir.rule=PathPrefix(`/osiris2fhir`)" + - "traefik.http.middlewares.osiris2fhir_strip.stripprefix.prefixes=/osiris2fhir" + - "traefik.http.services.osiris2fhir.loadbalancer.server.port=8080" + - "traefik.http.routers.osiris2fhir.tls=true" + - "traefik.http.routers.osiris2fhir.middlewares=osiris2fhir_strip,auth" diff --git a/pscc/modules/osiris2fhir-setup.sh b/pscc/modules/osiris2fhir-setup.sh new file mode 100644 index 00000000..852a3a85 --- /dev/null +++ b/pscc/modules/osiris2fhir-setup.sh @@ -0,0 +1,6 @@ +#!/bin/bash +if [ -n "$ENABLE_OSIRIS2FHIR" ]; then + log INFO "oBDS2FHIR-REST setup detected -- will start osiris2fhir module." + OVERRIDE+=" -f ./pscc/modules/osiris2fhir-compose.yml" + LOCAL_SALT="$(echo \"local-random-salt\" | openssl pkeyutl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 30)" +fi \ No newline at end of file diff --git a/pscc/root.crt.pem b/pscc/root.crt.pem new file mode 100644 index 00000000..1cfd0656 --- /dev/null +++ b/pscc/root.crt.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgIUVC1Y1tx0q5PNR33gArAyyBm8PMQwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAxMLQnJva2VyLVJvb3QwHhcNMjUxMTAzMTQxODQ5WhcNMzUx +MTAxMTQxOTE5WjAWMRQwEgYDVQQDEwtCcm9rZXItUm9vdDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAMB1yd7zkh7Io/ReQYindBcAdA1b4ogdVnrdSLRN +N3zLSh6jN5KIXgs34BdRXx0so0m96q+9xlgacTXGRBn1Tu5SKMRyXdxnCLMzHAYU +rNKhqF5HeZCYkVyh/tsAyFfDwZDVzsdX64V+0r5+raev2X0gJnlgmF83DIKjkVUS +2+c+3BnXa9LOdXks0qygJjvaFyi+5MA3DinLnmMLCQ3yAvaZYWyP3xCnGIoVrZFq +a+YioMCmHrbByuXPoZsXcFY7Z85LQkCtSVt1dH4kkN2/JehXG099nqwMqO8FpLZZ +xG7/U3P/slX1MMLs97nqRCRoW7Cha2ci1NBYLll+34ekhxMCAwEAAaN7MHkwDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJHTpnuyIGHw +yvC/mmh+S/JKYVrAMB8GA1UdIwQYMBaAFJHTpnuyIGHwyvC/mmh+S/JKYVrAMBYG +A1UdEQQPMA2CC0Jyb2tlci1Sb290MA0GCSqGSIb3DQEBCwUAA4IBAQAeDc/k28yb +I5MLC/LdaA+MKsW2FWF9HT+tsbtltTaQIRnnkwfU/40Ius3gzUU5z+kPqq5+kxhy +3T646Rbau85Zw24gdNmiVKAAG5ntKoQ7XnyR/06PYyXNGLqnb6aKvbcIPoWtU/+2 +8f5hHdQ/4271aHws7dKcBNWu9V5WmxMZ3YTfnBR5lEda+DhVwHqtmun8EpSbwthD +aLLIOHJpetr+KWUVFHQdGbO23Qg1Else0Akcn5Gzf/sKkVCVxjHE6jeo4ZwHtstG +KMoff+ETC+DL5kMZ4CV5VaQ4HxVK7N0qiUxmijWe+EyRZseum1c0s2OEi2L52Q9K +P4N3yD4ed4p/ +-----END CERTIFICATE----- \ No newline at end of file diff --git a/pscc/vars b/pscc/vars new file mode 100644 index 00000000..b64965db --- /dev/null +++ b/pscc/vars @@ -0,0 +1,14 @@ +BROKER_ID=broker.pscc.org +BROKER_URL=https://${BROKER_ID} +PROXY_ID=${SITE_ID}.${BROKER_ID} +FOCUS_BEAM_SECRET_SHORT="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 20)" +FOCUS_RETRY_COUNT=${FOCUS_RETRY_COUNT:-64} +SUPPORT_EMAIL=denis.koether@dkfz-heidelberg.de +PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem +BROKER_URL_FOR_PREREQ=$BROKER_URL + +for module in $PROJECT/modules/*.sh +do + log DEBUG "sourcing $module" + source $module +done diff --git a/versions/acceptance b/versions/acceptance new file mode 100644 index 00000000..89d509ec --- /dev/null +++ b/versions/acceptance @@ -0,0 +1,6 @@ +FOCUS_TAG=develop +BEAM_TAG=develop +BLAZE_TAG=0.32 +POSTGRES_TAG=15.13-alpine +TEILER_DASHBOARD_TAG=develop +MTBA_TAG=develop \ No newline at end of file diff --git a/versions/prod b/versions/prod index 1dd754fa..7f6642df 100644 --- a/versions/prod +++ b/versions/prod @@ -1,2 +1,6 @@ FOCUS_TAG=main -BEAM_TAG=main \ No newline at end of file +BEAM_TAG=main +BLAZE_TAG=0.32 +POSTGRES_TAG=15.13-alpine +TEILER_DASHBOARD_TAG=main +MTBA_TAG=main \ No newline at end of file diff --git a/versions/test b/versions/test index 10ae062d..b1a3a402 100644 --- a/versions/test +++ b/versions/test @@ -1,2 +1,6 @@ FOCUS_TAG=develop -BEAM_TAG=develop \ No newline at end of file +BEAM_TAG=develop +BLAZE_TAG=0.32 +POSTGRES_TAG=15.13-alpine +TEILER_DASHBOARD_TAG=develop +MTBA_TAG=develop