Skip to content

Commit

Permalink
feat: add implementation guide from astacus main repository
Browse files Browse the repository at this point in the history
  • Loading branch information
pl-buiquang committed Oct 17, 2023
1 parent 853b342 commit 0824388
Show file tree
Hide file tree
Showing 82 changed files with 3,005 additions and 2 deletions.
101 changes: 101 additions & 0 deletions .github/workflows/deploy-ig.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy implementation-guide to GitHub pages

on:
# Runs on pushes targeting the default branch
push:
branches: ["main", "deploy_ig_pages"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

# Set up languages framework and libraries
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'

- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm install -g fsh-sushi
- name: Setup Gradle
uses: gradle/gradle-build-action@v2

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2

- name: Install Jekyll
run: |
gem install jekyll bundler
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'

- run: dotnet tool install --tool-path ./fhir-cli firely.terminal

# Build IG
- run: gradle buildIG
continue-on-error: true

# Build Fix
# TODO remove this and properly fix the build...
- run: ./fhir-cli/fhir install hl7.fhir.fr.core 1.1.0
continue-on-error: true

- run: rm package.json package-lock.json

- run: gradle buildIG
# End Of Build Fix
# Build IG

# Upload IG artifact
- name: Setup Pages
uses: actions/configure-pages@v3

- name: Fix permissions
run: cp -r ./output _site

- name: Fix permissions
run: |
chmod -c -R +rX "_site/" | while read line; do
echo "::warning title=Invalid file permissions automatically fixed::$line"
done
- name: Upload artifact
uses: actions/upload-pages-artifact@v2

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.DS_Store
.idea
.gradle
/gradle
gradlew
gradlew.bat
Thumbs.db
/fsh-generated
/_input-cache
input-cache
/output
/temp
/template
/build
/node_modules
package.json
package-lock.json
Empty file added LICENSE
Empty file.
129 changes: 127 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,127 @@
# astacus-ig
Guide d'implémentation FHIR du projet Astacus
<h1 align="center">Astacus</h1>

<p align="center">
APIs multicentriques de dénombrement d'items cliniques et médicaux.
<br>
</p>

## À propos

Astacus propose des APIs de dénombrement des lames numérisées disponibles dans l'ensemble des centres médicaux partenaires du projet.

Ce projet est conduit en collaboration avec l'entreprise de pathologie numérique Tribun Health.

Ces APIs de dénombrement permettront à terme d'évaluer la quantité de matériel des centres partenaires en perspective et en préparation de collobarations futures.

Les centres partenaires s'engagent à mettre leurs données médicales à disposition dans des bases métiers suivant un guide d'implémentation FHIR. Ce guide d'implémentation tend à présenter une version initiale de format d'interopérabilité.

Au vu de la sensibilité des données exploitées, l'authentification des APIs et leurs dispositions au sein des systèmes d'information font l'objet de contraintes de sécurité avancées.

## Centres partenaires

![logo_centres](images/logo_centres.png)

## Partenaires industriels

![logo_tribun](images/logo_tribun.png)

## Table des matières

- [Architecture Générale](#architecture-générale)
- [CQF-Ruler](#cqf-Ruler)

- [Bases métiers](#bases-métiers)
- [Guide d'implémentation](#guide-dimplémentation)
- [Structuration des données](#structuration-des-données)
- [Anonymisation et Pseudonymisation](#anonymisation-et-pseudonymisation)
- [APIs](#apis)
- [Système de requêtage](#système-de-requêtage)
- [Sécurité](#sécurité)
- [Annexes](#annexes)
- [Comptes Rendus](#comptes-rendus)
- [Contacts](#contacts)

## Architecture Générale

![architecture](images/draws/astacus-interop-3centres-v2.drawio.png)

## CQF-Ruler

Le framework [cqf-ruler](https://github.com/cqframework/cqf-ruler) propose une implémentation d'une application employant des ressources FHIR.

### Bases métiers

Chaque centre a, dans son système d'information, une base métier rassemblant l'ensemble des données cliniques et médical de comptage.

#### Guide d'implémentation

Le guide d'implémentation FHIR définit les schémas de données des bases de données métiers, ainsi que les méthodes d'extraction et d'utilisation des ressources entreposées.

Voir [implementation-guide](implementation-guide)

#### Structuration des données

Le niveau de structuration de chaque ressource conditionne la profondeur des requêtes de dénombrement. Il est nécessaire d'établir une vue pour connaître le niveau de structuration de chaque centre sur les ressources du guide d'implémentation.

Voir [structured-data](structured-data)

#### Anonymisation et Pseudonymisation

Des méthodes d'anonymisation ou de pseudonymisation peuvent être envisagées pour apporter une couche de sécurisation supplémentaire.

### APIs

Chaque centre expose une API de dénombrement aux autres centres partenaires.

### Système de requêtage

Le système de requêtage utilisé par les APIs Astacus est le [langage CQL](https://cql.hl7.org/).

## Cas d'usage

Des cas d'usage ont été définis pour répondre aux premiers besoins de dénombrement.

Voir [use-cases](use-cases)

## Sécurité

### Certificat ANS

## Annexes

### Comptes Rendus

Les comptes rendus de réunion sont disponibles dans le fichier suivant : [comptes-rendus.md](https://github.com/curie-data-factory/astacus/blob/main/comptes-rendus.md)

### Contacts

#### APHP

RUBOD Vincent <vincent.rubod@aphp.fr> <br>
BUI-QUANG Paul <paul.bui-quang@aphp.fr> <br>
LUCAS Noel <noel.lucas@aphp.fr> <br>
DUBIEL Julien <julien.dubiel@aphp.fr> <br>
GUIRAUD Amandine <amandine.guiraud@aphp.fr> <br>
OUAGNE David <david.ouagne@aphp.fr> <br>
**RSSI** : Didier Perret <didier.perret@aphp.fr>


#### Curie

Gilles Flavien <flavien.gilles@curie.fr> <br>
Legros Aurelien <aurelien.legros@curie.fr> <br>
Archinard Johan <johan.archinard@curie.fr> <br>
Balezeau Thomas <thomas.balezeau@curie.fr> <br>
**RSSI** : Jeunehomme Frederic <frederic.jeunehomme@curie.fr>

#### Gustave Roussy

MATTLER Christophe <Christophe.Mattler@gustaveroussy.fr> <br>
DELOGER Marc <Marc.DELOGER@gustaveroussy.fr> <br>
BENSIMON Sylvain <Sylvain.BENSIMON@gustaveroussy.fr> <br>
**RSSI** : DAREES Julien <Julien.DAREES@gustaveroussy.fr> <br>

#### Tribun Health

Bertrand de Fürst <bdefurst@tribun.health> <br>
Sylvain Roux <sroux@tribun.health> <br>
6 changes: 6 additions & 0 deletions RSSI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
| Etablissement | Poste | Nom |
| ---- | ---- | ------ |
| AP-HP | RSSI | Didier Perret didier.perret@aphp.fr |
| AP-HP | RSSI adjoint | Jacques Hullu jacques.hullu@aphp.fr |
| Gustave Roussy | RSSI | DAREES Julien Julien.DAREES@gustaveroussy.fr |
| Curie | RSSI | Jeunehomme Frederic frederic.jeunehomme@curie.fr |
33 changes: 33 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Astacus Application Stack

## Deployment

* Dockerfile pour chaque app
* Helm chart (APHP / Curie par la DSI)
* Docker Compose (IGR)

## Stack

### fhir-cql

* Serveur HAPI FHIR avec CQL
* API FHIR Asynchrone (https://www.hl7.org/fhir/R4/async.html)
* Avec données MCode test Data https://confluence.hl7.org/display/COD/mCODE+Test+Data -> Petite doc pour insert les data dans l'API
* Limitation de requêtes/réponses CQL directement dans le requêteur CQL
* proxy sécuritaire : partie limitation de requête

### aggregator

* Techno : FastAPI/Flask
* API Asynchrone OU Websocket ?
* Modèle de données à définir
* Stockage bdd SQL des requêtes/jobs
* Requêtage/recevoir des requêtes mTLS pour communication inter-centres
* authentification utilisateur final (Bonus)
* proxy sécuritaire : partie authentification mTLS

### front

* Pouvoir éditer une requête CQL et l'executer
* Reprendre le front actuel développé par l'AP-HP et l'adapter pour qu'il se branche à l'aggrégateur

9 changes: 9 additions & 0 deletions app/aggregator/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#
FROM python:3.9

WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY . /code/app

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
Empty file added app/aggregator/__init__.py
Empty file.
7 changes: 7 additions & 0 deletions app/aggregator/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"Hello": "World"}
3 changes: 3 additions & 0 deletions app/aggregator/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fastapi==0.103.2
pydantic>=1.8.0,<2.0.0
uvicorn>=0.15.0,<0.16.0
Empty file added app/fhir-cql/Dockerfile
Empty file.
Empty file added app/front/Dockerfile
Empty file.
79 changes: 79 additions & 0 deletions app/nginx-mtls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
Cette documentation a pour objectif la mise en place d'un proxy d'authentification mTLS en amont de la brique aggregateur, notamment pour les entités externes souhaitant communiquer avec un aggrégateur local.

Elle est constituée de plusieurs étapes :

* La création d'un couple de clés privée/publique
* La configuration mTLS d'un reverse proxy Nginx en amont de l'aggrégateur


### Création d'un couple de clés privée/publique

Dans un terminal executez la commande suivante :

```
bash generate_certs.sh
```

Cette commande va générer deux fichiers : votre clé privée et votre clé publique.

La clé privée ne doit jamais être partagée (elle est privée ^^), tandis que la clé publique doit être partagée avec tous les partenaires avec qui souhaitez communiquer afin qu'ils puissent vérifier votre authenticité.


### Ajout de votre clé au fichier clients-ca.pem

Dans le cadre du projet Astacus, tous les partenaires peuvent se partager leurs clés publiques dans le fichier `clients-ca.pem`.

Pour ce faire, vous pouvez ainsi ajouter votre clé avec la commande suivante :

```
cat {VOTRE_CERTIFICAT.crt} >> clients-ca.pem
```

Il faudra bien évidemment remplacer `{VOTRE_CERTIFICAT.crt}` par le nom du fichier de votre clé publique (finissant en .crt)

Cela ajoutera le contenu de votre clé publique à la fin du fichier clients-ca.pem.

Ce fichier sera utilisé par Nginx pour vérifier que les requêtes qu'il reçoit proviennent bien de clients qui ont inscrit leur clé publique dans ce fichier.


### Configuration du reverse proxy Nginx mtls

Il suffira de lancer un container nginx avec la commande suivante :

```
docker run -d --net=host \
-v $(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf \
-v $(pwd)/{VOTRE_CERTIFICAT.crt}:/etc/nginx/ssl/me.crt \
-v $(pwd)/{VOTRE_CERTIFICAT.key}:/etc/nginx/ssl/me.key \
-v $(pwd)/clients-ca.pem:/etc/nginx/ssl/clients-ca.pem \
nginx:latest
```

où vous remplacerez `{VOTRE_CERTIFICAT.crt}` par le nom du fichier de votre clé publique (finissant en .crt), et `{VOTRE_CERTIFICAT.key}` par le nom du fichier de votre clé privée (finissant en .key).

Si tout se passe correctement, le docker devrait tourner en background, et écouter sur le port 20000 (vous pouvez consulter la configuration du reverse proxy dans le fichier nginx.conf).

Toute requête envoyée sur ce port et authentifiée sera par la suite proxyfiée vers le port 20001 du serveur (port sur lequel devra se trouver l'aggregateur).


### Test d'interactions avec un script python

La communication avec le serveur Nginx en mtls peut être testée en Python, veuillez tout d'abord vous assurer de disposer de Python3.8 ou supérieur et installez le package `httpx` (dans un [venv](https://docs.python.org/3/library/venv.html) si vous le souhaitez) :

```bash
pip3 install httpx[http2]
```

Le package `httpx` est une alternative au très populaire package `requests`, cependant ce dernier, contrairement à `httpx` ne gère pas la possibilité de désactiver le contrôle du nom de domaine vis-à-vis de celui indiqué dans le certificat, ce qui est généralement réalisé pour un site web hébergé sous un nom de domaine précis. Cependant, nous travaillons ici sans nom de domaine et avec des certificats auto-signés. Cela n'enlève aucune sécurité car les clients et serveurs se contrôlent entre eux grâce au mtls.

Vous pouvez désormais tester la communication avec le script python :

```bash
python3 test_mtls.py https://127.0.0.1:20000 $(pwd)/clients-ca.pem $(pwd)/me.crt $(pwd)/me.key
```

Ce qui vous retournera, si tout s'est bien passé :

* Une erreur 502 si aucun serveur ne tourne sur le port 20001
* La réponse du serveur agrégateur sur l'url `/` sinon

Empty file added app/nginx-mtls/clients-ca.pem
Empty file.
Loading

0 comments on commit 0824388

Please sign in to comment.