diff --git a/.github/workflows/build-orthanc-share.yml b/.github/workflows/build-orthanc-share.yml index 7b76f62..44954ed 100644 --- a/.github/workflows/build-orthanc-share.yml +++ b/.github/workflows/build-orthanc-share.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/.gitignore b/.gitignore index c7b92d0..1446cce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/README.md b/README.md index 659f6ad..2148991 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ diff --git a/demo-setup/README.md b/demo-setup/README.md index bb173ea..34b9123 100644 --- a/demo-setup/README.md +++ b/demo-setup/README.md @@ -1,5 +1,5 @@ diff --git a/demo-setup/common-orthanc.env b/demo-setup/common-orthanc.env index 530f272..79225ec 100644 --- a/demo-setup/common-orthanc.env +++ b/demo-setup/common-orthanc.env @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/demo-setup/doc/Share-study.gif.license b/demo-setup/doc/Share-study.gif.license index b215975..6d5f61c 100644 --- a/demo-setup/doc/Share-study.gif.license +++ b/demo-setup/doc/Share-study.gif.license @@ -1,3 +1,3 @@ -SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL SPDX-License-Identifier: CC-BY-4.0 diff --git a/demo-setup/docker-compose.meddream.yml b/demo-setup/docker-compose.meddream.yml index b35f0a0..d9e480f 100644 --- a/demo-setup/docker-compose.meddream.yml +++ b/demo-setup/docker-compose.meddream.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/demo-setup/docker-compose.yml b/demo-setup/docker-compose.yml index 4a75811..bc16db5 100644 --- a/demo-setup/docker-compose.yml +++ b/demo-setup/docker-compose.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/demo-setup/meddream/meddream-plugin.py b/demo-setup/meddream/meddream-plugin.py index ad3e513..b1b441e 100644 --- a/demo-setup/meddream/meddream-plugin.py +++ b/demo-setup/meddream/meddream-plugin.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/demo-setup/orthanc.jsonc b/demo-setup/orthanc.jsonc index b0a62a5..6148349 100644 --- a/demo-setup/orthanc.jsonc +++ b/demo-setup/orthanc.jsonc @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +// SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL // // SPDX-License-Identifier: CC0-1.0 diff --git a/demo-setup/start-demo-with-meddream.sh b/demo-setup/start-demo-with-meddream.sh index 960c613..9f6e4cc 100755 --- a/demo-setup/start-demo-with-meddream.sh +++ b/demo-setup/start-demo-with-meddream.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/demo-setup/start-demo.sh b/demo-setup/start-demo.sh index 6565697..59f8e57 100755 --- a/demo-setup/start-demo.sh +++ b/demo-setup/start-demo.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/minimal-setup/basic-auth/README.md b/minimal-setup/basic-auth/README.md index 8bb4e64..50907d4 100644 --- a/minimal-setup/basic-auth/README.md +++ b/minimal-setup/basic-auth/README.md @@ -1,5 +1,5 @@ diff --git a/minimal-setup/basic-auth/docker-compose.yml b/minimal-setup/basic-auth/docker-compose.yml index 594ed2f..18b11b0 100644 --- a/minimal-setup/basic-auth/docker-compose.yml +++ b/minimal-setup/basic-auth/docker-compose.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/minimal-setup/keycloak-meddream-full/README.md b/minimal-setup/keycloak-meddream-full/README.md index 6dab5b1..76fc8f6 100644 --- a/minimal-setup/keycloak-meddream-full/README.md +++ b/minimal-setup/keycloak-meddream-full/README.md @@ -1,5 +1,5 @@ diff --git a/minimal-setup/keycloak-meddream-full/docker-compose.yml b/minimal-setup/keycloak-meddream-full/docker-compose.yml index 5798066..752a356 100644 --- a/minimal-setup/keycloak-meddream-full/docker-compose.yml +++ b/minimal-setup/keycloak-meddream-full/docker-compose.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/minimal-setup/keycloak-meddream-full/meddream-plugin.py b/minimal-setup/keycloak-meddream-full/meddream-plugin.py index ad3e513..b1b441e 100644 --- a/minimal-setup/keycloak-meddream-full/meddream-plugin.py +++ b/minimal-setup/keycloak-meddream-full/meddream-plugin.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/minimal-setup/keycloak-meddream-full/permissions.jsonc b/minimal-setup/keycloak-meddream-full/permissions.jsonc index 8624a25..4213baf 100644 --- a/minimal-setup/keycloak-meddream-full/permissions.jsonc +++ b/minimal-setup/keycloak-meddream-full/permissions.jsonc @@ -1,4 +1,4 @@ -// "SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL " +// "SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL " // SPDX-License-Identifier: CC0-1.0 { "roles" : { diff --git a/minimal-setup/keycloak/README.md b/minimal-setup/keycloak/README.md index 8744157..9f70cb0 100644 --- a/minimal-setup/keycloak/README.md +++ b/minimal-setup/keycloak/README.md @@ -1,5 +1,5 @@ @@ -40,7 +40,7 @@ To start the setup, type: `docker compose up`. Some containers will restart mul - Add the `external` label to a few of the studies - On one of the uploaded studies, click on the `Share` button and then on `Share` in the dialog box and then on `Copy and close` - Keep the link in your clipboard. You may share this link with an external user. -- Go to `Profile` -> `Logout` +- In the left panel, go to `Admin` -> `Logout` ## As a guest user @@ -64,4 +64,42 @@ If you wish to access this demo from a remote computer, you must configure the d Update these settings: - orthanc: ORTHANC_JSON -> OrthancExplorer2 -> Keycloak -> "Url": "http://mydomain.com/keycloak/" - keycloak: KC_HOSTNAME_URL: "http://mydomain.com/keycloak" -- keycloak: KC_HOSTNAME_ADMIN_URL: "http://mydomain.com/keycloak" \ No newline at end of file +- keycloak: KC_HOSTNAME_ADMIN_URL: "http://mydomain.com/keycloak" + +# Enabling API keys + +If you wish to enable support for api-keys authentication, you should log in the Keycloak admin console and: +- in http://localhost/keycloak/admin/master/console/ (login/pwd: `admin`/`change-me`), left panel: Clients +- In the `orthanc` realm +- select `admin-cli` +- `Settings` tab --> enable `Client authentication` +- `Settings` tab --> enable `Service accounts roles` +- click on Save button!! +- then, `Credentials` tab is available to get the `Client secret`. Copy it in the `docker-compose.yml` file in `KEYCLOAK_CLIENT_SECRET` +- `Service accounts roles` tab --> `Assign role` button +- **select `Filter by clients` in the combo box** and `view-users` in the search filter +- check `realm-management view-users` and click `Assign` + +Then, you should add an API-key to a user. Still in the Keycloak admin area: +- In the `orthanc` realm +- Open `Users` and select the `external` user +- In the `Attributes` tab, click `Add an attribute` and enter: +- `api-key` as the Key and `api-key-for-external-user-that-should-be-a-long-random-string` as the Value. +- Click `Save` + +Then, in the `docker-compose.yml` uncomment the 2 lines related to these 2 env var: +- `ENABLE_KEYCLOAK_API_KEYS` +- `KEYCLOAK_CLIENT_SECRET` +- make sure that the Orthanc Authorization plugin contains this line: + `"TokenHttpHeaders" : [ "api-key" ],` + +And restart the Docker setup: +- `docker compose up` + +Then, in a terminal, type this command to access the API with an api-key. + +```bash +curl -H "api-key: api-key-for-external-user-that-should-be-a-long-random-string" -d '{"Level": "Study", "Query": {"PatientID": "*"}}' http://localhost:8043/tools/find +``` + +This should only list the studies with the `external` label. \ No newline at end of file diff --git a/minimal-setup/keycloak/docker-compose.yml b/minimal-setup/keycloak/docker-compose.yml index 6430ab9..c089298 100644 --- a/minimal-setup/keycloak/docker-compose.yml +++ b/minimal-setup/keycloak/docker-compose.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 @@ -67,6 +67,7 @@ services: "stone-webviewer", "orthanc-explorer-2" ], + "TokenHttpHeaders" : [ "api-key" ], "CheckedLevel": "studies" }, "DicomWeb": { @@ -76,7 +77,7 @@ services: } orthanc-auth-service: - image: orthancteam/orthanc-auth-service:23.12.2 + image: orthancteam/orthanc-auth-service:api-keys # always disable port mapping in production !!! # ports: ["8000:8000"] # permissions can be customized in the permissions.json file @@ -87,6 +88,8 @@ services: environment: SECRET_KEY: "change-me-I-am-a-secret-key" ENABLE_KEYCLOAK: "true" +# ENABLE_KEYCLOAK_API_KEYS: "true" +# KEYCLOAK_CLIENT_SECRET: "change-me-I-am-a-secret-you-get-in-keycloak-admin-ui" PUBLIC_ORTHANC_ROOT: "http://localhost/orthanc/" PUBLIC_LANDING_ROOT: "http://localhost/orthanc/ui/app/token-landing.html" PUBLIC_OHIF_ROOT: "http://localhost/ohif/" diff --git a/minimal-setup/keycloak/ohif-app-config.js b/minimal-setup/keycloak/ohif-app-config.js index 26c9a14..ad972e2 100644 --- a/minimal-setup/keycloak/ohif-app-config.js +++ b/minimal-setup/keycloak/ohif-app-config.js @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +// SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL // // SPDX-License-Identifier: CC0-1.0 diff --git a/minimal-setup/keycloak/permissions.jsonc b/minimal-setup/keycloak/permissions.jsonc index 8624a25..4213baf 100644 --- a/minimal-setup/keycloak/permissions.jsonc +++ b/minimal-setup/keycloak/permissions.jsonc @@ -1,4 +1,4 @@ -// "SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL " +// "SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL " // SPDX-License-Identifier: CC0-1.0 { "roles" : { diff --git a/release-notes.md b/release-notes.md index fe3527c..d34b58d 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,9 +1,14 @@ +- added support for Api-keys that can be defined as user custom attributes `api-key` + in Keycloak. This requires the definition of 3 new env vars: `ENABLE_KEYCLOAK_API_KEYS`, + `KEYCLOAK_CLIENT_SECRET` and `KECLOAK_ADMIN_URI`. This also requires you to configure + the `admin-cli` client in Keycloak. + v 24.1.1 ======== diff --git a/sources/Dockerfile.orthanc-auth-service b/sources/Dockerfile.orthanc-auth-service index 79ca2b8..e6a4643 100644 --- a/sources/Dockerfile.orthanc-auth-service +++ b/sources/Dockerfile.orthanc-auth-service @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/README-dockerhub-ohif-v3.md b/sources/README-dockerhub-ohif-v3.md index d8c1d30..bed0112 100644 --- a/sources/README-dockerhub-ohif-v3.md +++ b/sources/README-dockerhub-ohif-v3.md @@ -1,5 +1,5 @@ diff --git a/sources/README-dockerhub-orthanc-auth-service.md b/sources/README-dockerhub-orthanc-auth-service.md index 885e510..d162bc7 100644 --- a/sources/README-dockerhub-orthanc-auth-service.md +++ b/sources/README-dockerhub-orthanc-auth-service.md @@ -1,5 +1,5 @@ @@ -9,18 +9,22 @@ SPDX-License-Identifier: CC0-1.0 Web service to run next to Orthanc to handle sharing of studies by issuing [JWT](https://jwt.io/) that can then be passed in authorization headers that will be checked by the [Authorization plugin](https://book.orthanc-server.com/plugins/authorization.html). -| Environment variables | Default value | Description | -|-----------------------------|:---------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ENABLE_KEYCLOAK | false | Connects the auth-service to keycloak to handle users | -| KEYCLOAK_URI | http://keycloak:8080/realms/orthanc/ | The URI of the realm to use. | -| PERMISSIONS_FILE_PATH | /orthanc-auth-service/permissions.json | Path to a file containing the mapping between keycloak roles and permissions. | -| | | | -| PUBLIC_ORTHANC_ROOT | - | The public root URL to Orthanc when using links to access resources. e.g: `http://localhost/orthanc/` | -| PUBLIC_LANDING_ROOT | - | The landing page URL for links. This page will check the token validity and redirect to e.g. a viewer. e.g.: `http://localhost/orthanc/ui/app/token-landing.html` | -| PUBLIC_OHIF_ROOT | - | The public root URL to OHIF when using links to access resources. e.g: `https://ohif.my.site/` | -| USERS | - | Define a list of user/pwd that can access this webservice. | -| | | | -| MEDDREAM_TOKEN_SERVICE_URL | - | The URL to the MedDream token service. e.g `http://meddream-token-service:8088/v3/generate` | -| PUBLIC_MEDDREAM_ROOT | - | The public root URL to access the MedDream viewer. e.g `http://localhost/meddream/` | +| Environment variables | Default value | Description | +|----------------------------|:-------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ENABLE_KEYCLOAK | false | Connects the auth-service to keycloak to handle users | +| KEYCLOAK_URI | http://keycloak:8080/realms/orthanc/ | The URI of the realm to use. | +| PERMISSIONS_FILE_PATH | /orthanc-auth-service/permissions.json | Path to a file containing the mapping between keycloak roles and permissions. | +| | | | +| ENABLE_KEYCLOAK_API_KEYS | false | Enables the API keys support in Keycloak | +| KEYCLOAK_ADMIN_URI | http://keycloak:8080/admin/realms/orthanc/ | The URI of admin API of the realm to use. | +| KEYCLOAK_CLIENT_SECRET | - | `admin-cli` client secret used to authenticate to the Keycloak admin API | +| | | | +| PUBLIC_ORTHANC_ROOT | - | The public root URL to Orthanc when using links to access resources. e.g: `http://localhost/orthanc/` | +| PUBLIC_LANDING_ROOT | - | The landing page URL for links. This page will check the token validity and redirect to e.g. a viewer. e.g.: `http://localhost/orthanc/ui/app/token-landing.html` | +| PUBLIC_OHIF_ROOT | - | The public root URL to OHIF when using links to access resources. e.g: `https://ohif.my.site/` | +| USERS | - | Define a list of user/pwd that can access this webservice. | +| | | | +| MEDDREAM_TOKEN_SERVICE_URL | - | The URL to the MedDream token service. e.g `http://meddream-token-service:8088/v3/generate` | +| PUBLIC_MEDDREAM_ROOT | - | The public root URL to access the MedDream viewer. e.g `http://localhost/meddream/` | 3 demo setups are available [here](https://github.com/orthanc-team/orthanc-auth-service/tree/main/minimal-setup). diff --git a/sources/README-dockerhub-orthanc-keycloak.md b/sources/README-dockerhub-orthanc-keycloak.md index af451c8..b44f623 100644 --- a/sources/README-dockerhub-orthanc-keycloak.md +++ b/sources/README-dockerhub-orthanc-keycloak.md @@ -1,5 +1,5 @@ diff --git a/sources/README-dockerhub-orthanc-nginx.md b/sources/README-dockerhub-orthanc-nginx.md index c63318f..83c5657 100644 --- a/sources/README-dockerhub-orthanc-nginx.md +++ b/sources/README-dockerhub-orthanc-nginx.md @@ -1,5 +1,5 @@ diff --git a/sources/keycloak/Dockerfile.orthanc-keycloak b/sources/keycloak/Dockerfile.orthanc-keycloak index 80583fa..9aa43f7 100644 --- a/sources/keycloak/Dockerfile.orthanc-keycloak +++ b/sources/keycloak/Dockerfile.orthanc-keycloak @@ -1,5 +1,5 @@ #syntax=docker/dockerfile:1 -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/keycloak/orthanc-theme/login/resources/css/login.css b/sources/keycloak/orthanc-theme/login/resources/css/login.css index 20f4ec2..56e761b 100644 --- a/sources/keycloak/orthanc-theme/login/resources/css/login.css +++ b/sources/keycloak/orthanc-theme/login/resources/css/login.css @@ -1,4 +1,4 @@ -/* SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +/* SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/sources/keycloak/orthanc-theme/login/theme.properties b/sources/keycloak/orthanc-theme/login/theme.properties index 4af6696..9e65dfb 100644 --- a/sources/keycloak/orthanc-theme/login/theme.properties +++ b/sources/keycloak/orthanc-theme/login/theme.properties @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: Apache-2.0 diff --git a/sources/keycloak/realm-export.json.license b/sources/keycloak/realm-export.json.license index a896a2a..e78f46d 100644 --- a/sources/keycloak/realm-export.json.license +++ b/sources/keycloak/realm-export.json.license @@ -1,3 +1,3 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/sources/meddream/token-service/Dockerfile.meddream-token-service b/sources/meddream/token-service/Dockerfile.meddream-token-service index 9156053..bec6845 100644 --- a/sources/meddream/token-service/Dockerfile.meddream-token-service +++ b/sources/meddream/token-service/Dockerfile.meddream-token-service @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/meddream/token-service/token-service.application.properties b/sources/meddream/token-service/token-service.application.properties index 50310c2..8cf6a1f 100644 --- a/sources/meddream/token-service/token-service.application.properties +++ b/sources/meddream/token-service/token-service.application.properties @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/meddream/viewer/Dockerfile.meddream-viewer b/sources/meddream/viewer/Dockerfile.meddream-viewer index f9a2b4e..ced2b9b 100644 --- a/sources/meddream/viewer/Dockerfile.meddream-viewer +++ b/sources/meddream/viewer/Dockerfile.meddream-viewer @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/meddream/viewer/system.json.license b/sources/meddream/viewer/system.json.license index ee63f98..aa8f68d 100644 --- a/sources/meddream/viewer/system.json.license +++ b/sources/meddream/viewer/system.json.license @@ -1,3 +1,3 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/meddream/viewer/viewer.application.properties b/sources/meddream/viewer/viewer.application.properties index 8ebb6c4..7a09c60 100644 --- a/sources/meddream/viewer/viewer.application.properties +++ b/sources/meddream/viewer/viewer.application.properties @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/Dockerfile.orthanc-nginx b/sources/nginx/Dockerfile.orthanc-nginx index a25f9c6..90915d0 100644 --- a/sources/nginx/Dockerfile.orthanc-nginx +++ b/sources/nginx/Dockerfile.orthanc-nginx @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/docker-entrypoint.sh b/sources/nginx/docker-entrypoint.sh index 80aa781..fceb1da 100755 --- a/sources/nginx/docker-entrypoint.sh +++ b/sources/nginx/docker-entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/orthanc-nginx-http.conf b/sources/nginx/orthanc-nginx-http.conf index 2dd4745..249b1e4 100644 --- a/sources/nginx/orthanc-nginx-http.conf +++ b/sources/nginx/orthanc-nginx-http.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/orthanc-nginx-https.conf b/sources/nginx/orthanc-nginx-https.conf index 01390f7..256bcd0 100644 --- a/sources/nginx/orthanc-nginx-https.conf +++ b/sources/nginx/orthanc-nginx-https.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/reverse-proxy.keycloak-http.conf b/sources/nginx/reverse-proxy.keycloak-http.conf index 45e1e35..545512f 100644 --- a/sources/nginx/reverse-proxy.keycloak-http.conf +++ b/sources/nginx/reverse-proxy.keycloak-http.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/reverse-proxy.keycloak-https.conf b/sources/nginx/reverse-proxy.keycloak-https.conf index c5d7234..65f5a8f 100644 --- a/sources/nginx/reverse-proxy.keycloak-https.conf +++ b/sources/nginx/reverse-proxy.keycloak-https.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/reverse-proxy.meddream.conf b/sources/nginx/reverse-proxy.meddream.conf index e182004..0b545ce 100644 --- a/sources/nginx/reverse-proxy.meddream.conf +++ b/sources/nginx/reverse-proxy.meddream.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/reverse-proxy.ohif.conf b/sources/nginx/reverse-proxy.ohif.conf index 708901f..6cd8aa1 100644 --- a/sources/nginx/reverse-proxy.ohif.conf +++ b/sources/nginx/reverse-proxy.ohif.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/reverse-proxy.orthanc-api.conf b/sources/nginx/reverse-proxy.orthanc-api.conf index 0637cbc..540d73b 100644 --- a/sources/nginx/reverse-proxy.orthanc-api.conf +++ b/sources/nginx/reverse-proxy.orthanc-api.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/reverse-proxy.orthanc.conf b/sources/nginx/reverse-proxy.orthanc.conf index 7c97fd0..11ebcd2 100644 --- a/sources/nginx/reverse-proxy.orthanc.conf +++ b/sources/nginx/reverse-proxy.orthanc.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/nginx/reverse-proxy.token-service.conf b/sources/nginx/reverse-proxy.token-service.conf index de52cd2..eb92848 100644 --- a/sources/nginx/reverse-proxy.token-service.conf +++ b/sources/nginx/reverse-proxy.token-service.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/ohif/Dockerfile.ohif-v3 b/sources/ohif/Dockerfile.ohif-v3 index 49de131..2c697d3 100644 --- a/sources/ohif/Dockerfile.ohif-v3 +++ b/sources/ohif/Dockerfile.ohif-v3 @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/ohif/default-app-config.js b/sources/ohif/default-app-config.js index 26c9a14..ad972e2 100644 --- a/sources/ohif/default-app-config.js +++ b/sources/ohif/default-app-config.js @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +// SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL // // SPDX-License-Identifier: CC0-1.0 diff --git a/sources/ohif/ohif-nginx-http.conf b/sources/ohif/ohif-nginx-http.conf index 61a1f74..cbb5b90 100644 --- a/sources/ohif/ohif-nginx-http.conf +++ b/sources/ohif/ohif-nginx-http.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/ohif/ohif-static.conf b/sources/ohif/ohif-static.conf index e8e1c76..ee94c06 100644 --- a/sources/ohif/ohif-static.conf +++ b/sources/ohif/ohif-static.conf @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: CC0-1.0 diff --git a/sources/orthanc_auth_service/__init__.py b/sources/orthanc_auth_service/__init__.py index 355fa45..30b2bda 100644 --- a/sources/orthanc_auth_service/__init__.py +++ b/sources/orthanc_auth_service/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/app.py b/sources/orthanc_auth_service/app.py index 2d399fa..c9a5fb4 100644 --- a/sources/orthanc_auth_service/app.py +++ b/sources/orthanc_auth_service/app.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later @@ -15,11 +15,41 @@ from shares.models import * from shares.orthanc_token_service_factory import create_token_service_from_secrets from shares.keycloak import create_keycloak_from_secrets +from shares.roles_configuration import RolesConfiguration, create_roles_configuration_from_file +from shares.api_keys import create_api_keys +from shares.utils.utils import get_secret_or_die logging.basicConfig(level=logging.DEBUG) token_service = create_token_service_from_secrets() -keycloak = create_keycloak_from_secrets() +keycloak = None +api_keys = None + +permissions_file_path = os.environ.get("PERMISSIONS_FILE_PATH", "/orthanc_auth_service/permissions.json") +roles_configuration = create_roles_configuration_from_file(permissions_file_path) + +handle_users_with_keycloak = os.environ.get("ENABLE_KEYCLOAK", "false") == "true" + +if not handle_users_with_keycloak: + logging.warning("ENABLE_KEYCLOAK is not set, won't use keycloak and will not handle users") +else: + logging.warning("ENABLE_KEYCLOAK is set, using keycloak to handle users") + keycloak_uri = os.environ.get("KEYCLOAK_URI", "http://keycloak:8080/realms/orthanc/") + keycloak = create_keycloak_from_secrets(keycloak_uri=keycloak_uri, + roles_configuration=roles_configuration) + + enable_api_keys = os.environ.get("ENABLE_KEYCLOAK_API_KEYS", "false") == "true" + if not enable_api_keys: + logging.warning("ENABLE_KEYCLOAK_API_KEYS is not set, api-keys are disabled") + else: + logging.warning("ENABLE_KEYCLOAK_API_KEYS is set, using keycloak to handle api-keys") + keycloak_client_secret = get_secret_or_die("KEYCLOAK_CLIENT_SECRET") + keycloak_admin_uri = os.environ.get("KECLOAK_ADMIN_URI", "http://keycloak:8080/admin/realms/orthanc/") + api_keys = create_api_keys(keycloak_uri=keycloak_uri, + keycloak_admin_uri=keycloak_admin_uri, + keycloak_client_secret=keycloak_client_secret, + roles_configuration=roles_configuration) + app = FastAPI() # check if the service requires basic auth (by checking of some USERS have been defined) @@ -167,8 +197,13 @@ def get_user_profile(user_profile_request: UserProfileRequest): if keycloak is None: logging.warning("Keycloak is not configured, all users are considered anonymous") return anonymous_profile + elif user_profile_request.token_key is not None: - response = keycloak.get_user_profile_from_token(user_profile_request.token_value) + if user_profile_request.token_key == "api-key" and api_keys is not None: + response = api_keys.get_user_profile_from_api_key(api_key=user_profile_request.token_value) + else: + response = keycloak.get_user_profile_from_token(user_profile_request.token_value) + else: return anonymous_profile diff --git a/sources/orthanc_auth_service/permissions.json b/sources/orthanc_auth_service/permissions.json index 8624a25..4213baf 100644 --- a/sources/orthanc_auth_service/permissions.json +++ b/sources/orthanc_auth_service/permissions.json @@ -1,4 +1,4 @@ -// "SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL " +// "SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL " // SPDX-License-Identifier: CC0-1.0 { "roles" : { diff --git a/sources/orthanc_auth_service/shares/__init__.py b/sources/orthanc_auth_service/shares/__init__.py index 6691edc..cce2bb8 100644 --- a/sources/orthanc_auth_service/shares/__init__.py +++ b/sources/orthanc_auth_service/shares/__init__.py @@ -1,3 +1,3 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/shares/api_keys.py b/sources/orthanc_auth_service/shares/api_keys.py new file mode 100644 index 0000000..06089c1 --- /dev/null +++ b/sources/orthanc_auth_service/shares/api_keys.py @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL +# +# SPDX-License-Identifier: GPL-3.0-or-later + +import os +import logging +import datetime +import requests +import jwt +import jsonc +from typing import Dict, Any, List, Tuple +from .models import * +from .utils.utils import get_secret_or_die, is_secret_defined +from .roles_configuration import RolesConfiguration +import requests +from urllib.parse import urljoin + +def _get_keycloak_access_token(keycloak_uri: str, keycloak_client_secret: str) -> str: + keycloak_url = urljoin(keycloak_uri, "protocol/openid-connect/token") + + data = { + 'client_id': 'admin-cli', + 'client_secret': keycloak_client_secret, + 'grant_type': 'client_credentials' + } + + response = requests.post(keycloak_url, data=data) + access_token = response.json()['access_token'] + return access_token + +class ApiKeys: + + def __init__(self, keycloak_uri: str, keycloak_admin_uri: str, keycloak_client_secret: str, roles_configuration: RolesConfiguration): + self.keycloak_uri = keycloak_uri + self.keycloak_admin_uri = keycloak_admin_uri + self.keycloak_client_secret = keycloak_client_secret + self.roles_configuration = roles_configuration + + def get_user_profile_from_api_key(self, api_key: str) -> Optional[UserProfileResponse]: + keycloak_users_url = urljoin(self.keycloak_admin_uri, f"users?q=api-key:{api_key}") + headers = { + 'Authorization': 'Bearer ' + _get_keycloak_access_token(self.keycloak_uri, self.keycloak_client_secret), + 'Content-Type': 'application/json' + } + keycloak_user_response = requests.get(keycloak_users_url, headers=headers) + if keycloak_user_response.status_code == 200: + users = keycloak_user_response.json() + if len(users) > 1: + logging.error("Multiple users found with the same api-key") + return None + elif len(users) == 0: + return None + else: + user = users[0] + if not user['enabled']: + logging.error("User is disabled") + return None + if datetime.now() < datetime.utcfromtimestamp(user['notBefore']): + logging.error("User is not yet valid") + return None + + # retrieve the roles for this user + keycloak_role_url = urljoin(self.keycloak_admin_uri, f"users/{user['id']}/role-mappings") + keycloak_role_response = requests.get(keycloak_role_url, headers=headers) + if keycloak_role_response.status_code != 200: + logging.error("Unable to retrieve roles for user") + return None + + # keep only the roles that we have defined ourselves + roles = [] + resp_roles = keycloak_role_response.json() + for resp_role in resp_roles['realmMappings']: + if resp_role['name'] in self.roles_configuration.get_roles(): + roles.append(resp_role['name']) + + response = UserProfileResponse( + name=user['username'], + permissions=[], # populated below + validity=60, + authorized_labels=[] # populated below + ) + + response.permissions, response.authorized_labels = self.roles_configuration.get_role_configuration(roles) + + return response + else: + logging.error("Failed to fetch/search users from api-key from keycloak: " + str(keycloak_response)) + return None + +def create_api_keys(keycloak_uri: str, keycloak_admin_uri: str, keycloak_client_secret: str, roles_configuration: RolesConfiguration): + try: + #validate that we can connect to keycloak and retrieve users list + keycloak_users_url = urljoin(keycloak_admin_uri, "users") + headers = { + 'Authorization': 'Bearer ' + _get_keycloak_access_token(keycloak_uri, keycloak_client_secret), + 'Content-Type': 'application/json' + } + response = requests.get(keycloak_users_url, headers=headers) + if response.status_code != 200: + logging.error(f"Unable to retrieve users list from keycloak to validate client connection " + str(response) + ", exiting...") + exit(-1) + + return ApiKeys(keycloak_uri=keycloak_uri, + keycloak_admin_uri=keycloak_admin_uri, + keycloak_client_secret=keycloak_client_secret, + roles_configuration=roles_configuration) + + except Exception as ex: + logging.exception(ex) + logging.error(f"Unable to validate client connection with keycloak, exiting...") + exit(-1) + diff --git a/sources/orthanc_auth_service/shares/exceptions.py b/sources/orthanc_auth_service/shares/exceptions.py index 09aa45b..9ea848b 100644 --- a/sources/orthanc_auth_service/shares/exceptions.py +++ b/sources/orthanc_auth_service/shares/exceptions.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/shares/keycloak.py b/sources/orthanc_auth_service/shares/keycloak.py index 14b9de4..41c9f59 100644 --- a/sources/orthanc_auth_service/shares/keycloak.py +++ b/sources/orthanc_auth_service/shares/keycloak.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later @@ -9,14 +9,15 @@ import jsonc from typing import Dict, Any, List, Tuple from .models import * +from .roles_configuration import RolesConfiguration from .utils.utils import get_secret_or_die, is_secret_defined class Keycloak: - def __init__(self, public_key, configured_roles: Dict[str, Any]): + def __init__(self, public_key, roles_configuration: RolesConfiguration): self.public_key = public_key - self.configured_roles = configured_roles + self.roles_configuration = roles_configuration def decode_token(self, jwt_token: str) -> Dict[str, Any]: return jwt.decode(jwt=jwt_token, key=self.public_key, audience="account", algorithms=["RS256"]) @@ -95,40 +96,11 @@ def get_user_profile_from_token(self, jwt_token: str) -> UserProfileResponse: roles = self.get_roles_from_decoded_token(decoded_token=decoded_token) - response.permissions, response.authorized_labels = self.get_role_configuration(roles) + response.permissions, response.authorized_labels = self.roles_configuration.get_role_configuration(roles) return response - def get_role_configuration(self, roles: List[str]) -> Tuple[List[UserPermissions], List[str], List[str]]: - permissions = [] - authorized_labels = [] - configured_user_roles = [] - - for r in roles: - if r in self.configured_roles: - configured_user_roles.append(r) - - # complain if there are 2 roles for the same user ??? How should we combine the authorized and forbidden labels in this case ??? - if len(configured_user_roles) > 1: - raise ValueError("Unable to handle multiple roles for a single user") - - role = configured_user_roles[0] - # search for it in the configured roles - configured_role = self.configured_roles.get(role) - # if it has been configured: - if configured_role is None: - raise ValueError(f"Role not found in configuration: {role}") - - for item in configured_role.get('permissions'): - # (if not already there) - if UserPermissions(item) not in permissions: - permissions.append(UserPermissions(item)) - - if configured_role.get("authorized_labels"): - authorized_labels = configured_role.get("authorized_labels") - - return permissions, authorized_labels def _get_keycloak_public_key(keycloak_uri: str) -> str: @@ -146,36 +118,9 @@ def _get_keycloak_public_key(keycloak_uri: str) -> str: return ''.join([begin_public_key, public_key, end_public_key]).encode() -def _get_config_from_file(file_path: str): - with open(file_path) as f: - data = jsonc.load(f) - - roles = data.get('roles') - - for key, role_def in roles.items(): - if not role_def.get("authorized_labels"): - msg = f'No "authorized_labels" defined for role "{key}". You should, e.g, include "authorized_labels" = ["*"] if you want to authorize all labels.")' - logging.error(msg) - raise ValueError(msg) - - if not role_def.get("permissions"): - msg = f'No "permissions" defined for role "{key}". You should, e.g, include "permissions" = ["all"] if you want to authorize all permissions.")' - logging.error(msg) - raise ValueError(msg) - return roles - -def create_keycloak_from_secrets(): - handle_users_with_keycloak = os.environ.get("ENABLE_KEYCLOAK", "false") == "true" - - if not handle_users_with_keycloak: - logging.warning("ENABLE_KEYCLOAK is not set, won't use keycloak and will not handle users") - return None - - logging.warning("ENABLE_KEYCLOAK is set, will handle users with Keycloak") - keycloak_uri = os.environ.get("KEYCLOAK_URI", "http://keycloak:8080/realms/orthanc/") - permissions_file_path = os.environ.get("PERMISSIONS_FILE_PATH", "/orthanc_auth_service/permissions.json") +def create_keycloak_from_secrets(keycloak_uri: str, roles_configuration: RolesConfiguration): try: public_key = _get_keycloak_public_key(keycloak_uri) @@ -186,13 +131,4 @@ def create_keycloak_from_secrets(): logging.error(f"Unable to reach keycloak (be patient, Keycloak may need more than 1 min to start), exiting...") exit(-1) - try: - configured_roles = _get_config_from_file(permissions_file_path) - logging.info(f"Got the roles and permissions from configuration file") - - except Exception as ex: - logging.exception(ex) - logging.error(f"Unable to get roles and permissions from configuration file ({permissions_file_path}), exiting...") - exit(-1) - - return Keycloak(public_key=public_key, configured_roles=configured_roles) + return Keycloak(public_key=public_key, roles_configuration=roles_configuration) diff --git a/sources/orthanc_auth_service/shares/models.py b/sources/orthanc_auth_service/shares/models.py index 52b7cf3..28bd1cc 100644 --- a/sources/orthanc_auth_service/shares/models.py +++ b/sources/orthanc_auth_service/shares/models.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/shares/orthanc_token_service.py b/sources/orthanc_auth_service/shares/orthanc_token_service.py index 3d841a2..679bb58 100644 --- a/sources/orthanc_auth_service/shares/orthanc_token_service.py +++ b/sources/orthanc_auth_service/shares/orthanc_token_service.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/shares/orthanc_token_service_factory.py b/sources/orthanc_auth_service/shares/orthanc_token_service_factory.py index 17d45ed..bdd8f3e 100644 --- a/sources/orthanc_auth_service/shares/orthanc_token_service_factory.py +++ b/sources/orthanc_auth_service/shares/orthanc_token_service_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/shares/roles_configuration.py b/sources/orthanc_auth_service/shares/roles_configuration.py new file mode 100644 index 0000000..0ccf82b --- /dev/null +++ b/sources/orthanc_auth_service/shares/roles_configuration.py @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL +# +# SPDX-License-Identifier: GPL-3.0-or-later + +import logging +import jsonc +import os +from .models import * +from typing import Dict, Any, List, Tuple + + +def _get_config_from_file(file_path: str): + with open(file_path) as f: + data = jsonc.load(f) + + roles = data.get('roles') + + for key, role_def in roles.items(): + if not role_def.get("authorized_labels"): + msg = f'No "authorized_labels" defined for role "{key}". You should, e.g, include "authorized_labels" = ["*"] if you want to authorize all labels.")' + logging.error(msg) + raise ValueError(msg) + + if not role_def.get("permissions"): + msg = f'No "permissions" defined for role "{key}". You should, e.g, include "permissions" = ["all"] if you want to authorize all permissions.")' + logging.error(msg) + raise ValueError(msg) + + return roles + + +class RolesConfiguration: + + def __init__(self, roles: Dict[str, Any]): + self.configured_roles = roles + + def get_roles(self) -> List[str]: + return self.configured_roles.keys() + + def get_role_configuration(self, roles: List[str]) -> Tuple[List[UserPermissions], List[str], List[str]]: + permissions = [] + authorized_labels = [] + configured_user_roles = [] + + for r in roles: + if r in self.configured_roles: + configured_user_roles.append(r) + + # complain if there are 2 roles for the same user ??? How should we combine the authorized and forbidden labels in this case ??? + if len(configured_user_roles) > 1: + raise ValueError("Unable to handle multiple roles for a single user") + + role = configured_user_roles[0] + # search for it in the configured roles + configured_role = self.configured_roles.get(role) + # if it has been configured: + if configured_role is None: + raise ValueError(f"Role not found in configuration: {role}") + + for item in configured_role.get('permissions'): + # (if not already there) + if UserPermissions(item) not in permissions: + permissions.append(UserPermissions(item)) + + if configured_role.get("authorized_labels"): + authorized_labels = configured_role.get("authorized_labels") + + return permissions, authorized_labels + +def create_roles_configuration_from_file(permissions_file_path: str): + try: + roles = _get_config_from_file(permissions_file_path) + logging.info(f"Got the roles and permissions from configuration file") + + except Exception as ex: + logging.exception(ex) + logging.error(f"Unable to get roles and permissions from configuration file ({permissions_file_path}), exiting...") + exit(-1) + + return RolesConfiguration(roles) \ No newline at end of file diff --git a/sources/orthanc_auth_service/shares/tokens_manager.py b/sources/orthanc_auth_service/shares/tokens_manager.py index 1470148..54a2770 100644 --- a/sources/orthanc_auth_service/shares/tokens_manager.py +++ b/sources/orthanc_auth_service/shares/tokens_manager.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/shares/utils/__init__.py b/sources/orthanc_auth_service/shares/utils/__init__.py index 6691edc..cce2bb8 100644 --- a/sources/orthanc_auth_service/shares/utils/__init__.py +++ b/sources/orthanc_auth_service/shares/utils/__init__.py @@ -1,3 +1,3 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/orthanc_auth_service/shares/utils/utils.py b/sources/orthanc_auth_service/shares/utils/utils.py index 791450a..0bc62aa 100644 --- a/sources/orthanc_auth_service/shares/utils/utils.py +++ b/sources/orthanc_auth_service/shares/utils/utils.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/requirements.txt b/sources/requirements.txt index a8d3c63..229ab59 100644 --- a/sources/requirements.txt +++ b/sources/requirements.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/tests/__init__.py b/sources/tests/__init__.py index 6691edc..cce2bb8 100644 --- a/sources/tests/__init__.py +++ b/sources/tests/__init__.py @@ -1,3 +1,3 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/tests/run-tests.sh b/sources/tests/run-tests.sh index 693be9e..d128fe6 100755 --- a/sources/tests/run-tests.sh +++ b/sources/tests/run-tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/sources/tests/test_tokens.py b/sources/tests/test_tokens.py index efcee9c..6502991 100644 --- a/sources/tests/test_tokens.py +++ b/sources/tests/test_tokens.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 - 2023 Orthanc Team SRL +# SPDX-FileCopyrightText: 2022 - 2024 Orthanc Team SRL # # SPDX-License-Identifier: GPL-3.0-or-later