Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5a68acb
- Add tests to increase coverage in medcat-trainer frontend
jocelyneholdbrook Sep 9, 2025
fb7752a
Fix indentation
jocelyneholdbrook Sep 9, 2025
37643c7
Update medcat-trainer_ci.yml
jocelyneholdbrook Sep 9, 2025
ea5153c
Fix line endings
jocelyneholdbrook Sep 9, 2025
86cb877
properly fix line endings mismatch
jocelyneholdbrook Sep 9, 2025
727aec0
Create ConceptDatabase.spec.ts
jocelyneholdbrook Sep 9, 2025
5b9d2da
Merge branch 'CU-869admfwg-increase-test-coverage-in-medcattrainer' o…
jocelyneholdbrook Sep 9, 2025
678adba
remove erroneously added test files
jocelyneholdbrook Sep 9, 2025
0a8397c
re-add end of file return
jocelyneholdbrook Sep 9, 2025
7962ac7
readd new line at end of file
jocelyneholdbrook Sep 9, 2025
413a8ab
properly fix line endings mismatch
jocelyneholdbrook Sep 9, 2025
99e6a67
Merge branch 'CU-869admfwg-increase-test-coverage-in-medcattrainer' o…
jocelyneholdbrook Sep 10, 2025
24f9bb2
CU-869admfwg: Disable push to dockerhub in QA workflow for testing
jocelyneholdbrook Sep 10, 2025
462c569
CU-869admfwg: Remove workflow dispatch from QA workflow now that test…
jocelyneholdbrook Sep 10, 2025
435f855
CU-869ajt55f: Initial dev setup with feature flag
jocelyneholdbrook Sep 29, 2025
894dfa4
CU-869ajt55f: Map Keycloak Roles to Django User
jocelyneholdbrook Oct 1, 2025
6012f20
CU-869ajt55f: Add env var expansion to optionally include Traefik lab…
jocelyneholdbrook Oct 1, 2025
0aab53a
CU-869ajt55f: Tidy up configuration and fix logout
jocelyneholdbrook Oct 2, 2025
b0a2325
CU-869ajt55f: Disable OIDC auth by default
jocelyneholdbrook Oct 2, 2025
1a95506
CU-869ajt55f: Revert unintended changes
jocelyneholdbrook Oct 2, 2025
5c85b0d
CU-869ajt55f: Addressing code review feedback
jocelyneholdbrook Oct 6, 2025
de9c139
Merge branch 'main' into CU-869ajt55f_Add-auth-via-centralised-IdP-wi…
jocelyneholdbrook Oct 7, 2025
d47162d
CU-869ajt55f: Fix broken test
jocelyneholdbrook Oct 7, 2025
6856a20
Merge remote-tracking branch 'origin/CU-869ajt55f_Add-auth-via-centra…
jocelyneholdbrook Oct 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion medcat-trainer/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ SPACY_MODELS="en_core_web_sm en_core_web_md en_core_web_lg"

# Ports
MCTRAINER_PORT=8001
SOLR_PORT=8983
SOLR_PORT=8983
2 changes: 2 additions & 0 deletions medcat-trainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ MedCATTrainer was presented at EMNLP/IJCNLP 2019 :tada:
Official docs available [here](https://docs.cogstack.org/projects/medcat-trainer)

If you have any questions why not reach out to the community [discourse forum here](https://discourse.cogstack.org/)


16 changes: 16 additions & 0 deletions medcat-trainer/docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ services:
args:
SPACY_MODELS: ${SPACY_MODELS:-en_core_web_md}
restart: always
networks:
- gateway-auth_gateway-net
volumes:
- api-media:/home/api/media
- api-static:/home/api/static
Expand All @@ -32,13 +34,20 @@ services:
- api-static:/home/api/static
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/sites-enabled/:/etc/nginx/sites-enabled
networks:
- gateway-auth_gateway-net
env_file:
- ./envs/env
ports:
- ${MCTRAINER_PORT:-8001}:8000
depends_on:
- medcattrainer
- solr
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.rule=Host(`${NGINX_HOST:-medcattrainer.cogstack.localhost}`)"
- "traefik.http.routers.nginx.entrypoints=web"
- "traefik.http.services.nginx.loadbalancer.server.port=8000"

solr:
container_name: mct_solr
Expand All @@ -50,6 +59,8 @@ services:
- ${SOLR_PORT:-8983}:8983
volumes:
- solr-data:/var/solr
networks:
- gateway-auth_gateway-net
command:
- -cloud

Expand All @@ -59,3 +70,8 @@ volumes:
api-db:
api-db-backup:
solr-data:

networks:
gateway-auth_gateway-net:
external: true

26 changes: 25 additions & 1 deletion medcat-trainer/docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ For password resets and other emailing services email environment variables are

Personal email accounts can be set up by users to do this, or you can contact someone in CogStack for a cogstack no email credentials.

The environment variables required are listed in [Environment Variables.](#(optional)-environment-variables)
The environment variables required are listed in [Environment Variables.](#optional-environment-variables)

Environment Variables are located in envs/env or envs/env-prod, when those are set webapp/frontend/.env must change "VITE_APP_EMAIL" to 1.

Expand All @@ -73,6 +73,30 @@ Set these and re-run the docker-compose file.

You'll need to `docker stop` the running containers if you have already run the install.

## OIDC Authentication

You can enable OIDC (OpenID Connect) authentication for the MedCAT Trainer. To do so, you must configure the following environment variables:

| Variable | Used by | Description |
|-----------------------------------------|-------------------------|----------------------------------------------------------------|
| USE_OIDC | Backend | Enable OIDC login flow (1 (true) / 0 (false)). |
| VITE_USE_OIDC | Frontend | Exposed version of USE_OIDC for Vue. |
| VITE_API_URL | Frontend | Base API URL for frontend calls. |
| VITE_KEYCLOAK_URL | Frontend | Keycloak base URL (e.g. http://keycloak.cogstack.localhost/). |
| VITE_KEYCLOAK_REALM | Frontend | Keycloak realm name. |
| VITE_KEYCLOAK_CLIENT_ID | Frontend | Keycloak client ID for this app. |
| VITE_KEYCLOAK_TOKEN_REFRESH_INTERVAL_MS | Frontend | Token refresh frequency in ms. |
| VITE_KEYCLOAK_TOKEN_MIN_VALIDITY_SECS | Frontend | Minimum token validity before refresh. |
| VITE_LOGOUT_REDIRECT_URI | Frontend | Where to send user after logout. |
| NGINX_HOST | Backend | Host alias used by reverse proxy (Traefik ) |

You can either use the Gateway Auth stack available in cogstack-ops or deploy your own Keycloak instance.
If you deploy your own Keycloak instance, make sure to configure the network accordingly.

Currently, there are two roles that can be assigned to users:
- medcattrainer_superuser: grants superuser privileges in the application.
- medcattrainer_staff: grants staff-level privileges without full superuser access.

### (Optional) Postgres Database Support
MedCAT trainer defaults to a local SQLite database, which is suitable for single-user or small-scale setups.

Expand Down
2 changes: 1 addition & 1 deletion medcat-trainer/docs/project_admin.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Creating an Annotation Project
Annotation projects are used to inspect, validate and improve concepts recognised & linked by MedCAT.
They can also be used collect annotations for defined MetaCAT models tasks, and coming soon RelCAT, or relation annotation models.
They can also be used to collect annotations for defined MetaCAT models tasks, and coming soon RelCAT, or relation annotation models.

Using the admin page, a configured admin or superuser can create, edit and delete annotation projects.

Expand Down
11 changes: 11 additions & 0 deletions medcat-trainer/envs/env
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@ EMAIL_USER=example@cogstack.org
EMAIL_PASS="to be changed"
EMAIL_HOST=mail.cogstack.org
EMAIL_PORT=465

# Backend auth
USE_OIDC=

# Traefik
NGINX_HOST=medcattrainer.cogstack.localhost

# OIDC settings
OIDC_HOST=http://keycloak:8080
OIDC_REALM=cogstack-realm
OIDC_FRONTEND_CLIENT_ID=cogstack-medcattrainer-frontend
37 changes: 37 additions & 0 deletions medcat-trainer/webapp/api/api/oidc_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from django.contrib.auth import get_user_model
import secrets

def get_user_by_email(request, id_token):
"""
Resolve or create a Django user using the email claim from OIDC.
"""
User = get_user_model()
email = id_token.get('email')
username = id_token.get('preferred_username')
print(id_token)
roles = []
if 'realm_access' in id_token:
roles = id_token['realm_access'].get('roles', [])

is_superuser = 'medcattrainer_superuser' in roles
is_staff = 'medcattrainer_staff' in roles

user, created = User.objects.get_or_create(
email=email,
defaults={
"username": username,
"first_name": id_token.get("given_name", ""),
"last_name": id_token.get("family_name", ""),
"is_active": True,
"password": secrets.token_urlsafe(32),
},
)

user.username = username
user.first_name = id_token.get("given_name", "")
user.last_name = id_token.get("family_name", "")
user.is_superuser = is_superuser
user.is_staff = is_staff

user.save()
return user
2 changes: 1 addition & 1 deletion medcat-trainer/webapp/api/api/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import traceback
from smtplib import SMTPException
from tempfile import NamedTemporaryFile
Expand Down Expand Up @@ -223,7 +224,6 @@ def post(self, request, *args, **kwargs):
Please visit https://medcattrainer.readthedocs.io for more information to resolve this. <br>
You can also ask a question at: https://discourse.cogstack.org/c/medcat/5''')


@api_view(http_method_names=['GET'])
def get_anno_tool_conf(_):
return Response({k: v for k, v in os.environ.items()})
Expand Down
34 changes: 34 additions & 0 deletions medcat-trainer/webapp/api/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@

APPEND_SLASH = True

USE_OIDC = os.getenv('USE_OIDC', '').lower() == '1'

# Application definition

INSTALLED_APPS = [
Expand Down Expand Up @@ -197,6 +199,38 @@
]
}

if USE_OIDC:
log.info("Using OIDC authentication")
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] = [
'oidc_auth.authentication.JSONWebTokenAuthentication',
'oidc_auth.authentication.BearerTokenAuthentication',
]

OIDC_HOST = os.environ.get("OIDC_HOST", "")
OIDC_REALM = os.environ.get("OIDC_REALM", "")
OIDC_FRONTEND_CLIENT_ID = os.environ.get("OIDC_FRONTEND_CLIENT_ID", "")

OIDC_AUTH = {
'OIDC_ENDPOINT': f"{OIDC_HOST}/realms/{OIDC_REALM}",
'OIDC_CLAIMS_OPTIONS': {
'aud': {
'values': [
'account',
OIDC_FRONTEND_CLIENT_ID
],
'essential': True,
},
'iss': {
'values': [
f"{OIDC_HOST}/realms/{OIDC_REALM}"
],
'essential': True,
},
},
'USERINFO_ENDPOINT': f"{OIDC_HOST}/realms/{OIDC_REALM}/protocol/openid-connect/userinfo",
'OIDC_CREATE_USER': True,
'OIDC_RESOLVE_USER_FUNCTION': 'api.oidc_utils.get_user_by_email',
}

# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
Expand Down
9 changes: 9 additions & 0 deletions medcat-trainer/webapp/frontend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Override this with your .env.production
# Frontend auth settings
VITE_USE_OIDC=0
VITE_KEYCLOAK_URL=http://keycloak.cogstack.localhost/
VITE_KEYCLOAK_REALM=cogstack-realm
VITE_KEYCLOAK_CLIENT_ID=cogstack-medcattrainer-frontend
VITE_KEYCLOAK_TOKEN_REFRESH_INTERVAL_MS=10000
VITE_KEYCLOAK_TOKEN_MIN_VALIDITY_SECS=30
VITE_LOGOUT_REDIRECT_URI=http://home.cogstack.localhost/
Loading