Role Permissions Issue

I’m pretty new to Orthanc/OHIF and brand new to Keycloak, and I’m confused about permissions. I don’t now if it’s a bug, or misconfigured (probably misconfigured). When I log into /orthanc/ui/app/ with admin, I have no way to upload studies. It’s stripped of any administrative role stuff.

Here is my docker-compose.yml:

version: "3"
services:

  nginx:
    image: orthancteam/orthanc-nginx:24.2.0
    depends_on: [orthanc, orthanc-auth-service, keycloak]
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /etc/letsencrypt/live/mydomain.com/fullchain.pem:/etc/nginx/tls/crt.pem
      - /etc/letsencrypt/live/mydomain.com/privkey.pem:/etc/nginx/tls/key.pem
    environment:
      ENABLE_ORTHANC: "true"
      ENABLE_KEYCLOAK: "true"
      ENABLE_ORTHANC_TOKEN_SERVICE: "true"
      ENABLE_HTTPS: "true"
      ENABLE_OHIF: "false"

  orthanc:
    image: orthancteam/orthanc:24.3.3
    volumes:
      - /opt/orthanc/storage:/var/lib/orthanc/db
    depends_on: [orthanc-db]
    restart: unless-stopped
    environment:
      STONE_WEB_VIEWER_PLUGIN_ENABLED: "true"
      DICOM_WEB_PLUGIN_ENABLED: "true"
      OHIF_PLUGIN_ENABLED: "true"
      ORTHANC__POSTGRESQL__HOST: "orthanc-db"
      VERBOSE_ENABLED: "true"
      VERBOSE_STARTUP: "true"
      ORTHANC_JSON: |
        {
          "Name": "Orthanc",
          "OrthancExplorer2": {
            "IsDefaultUI": true,
            "UiOptions": {
              "EnableShares": true,
              "DefaultShareDuration": 0,
              "ShareDurations": [0, 7, 15, 30, 90, 365],
              "EnableOpenInOhifViewer3": true,
              "OhifViewer3PublicRoot": "https://mydomain.com/orthanc/ohif/"
            },
            "Tokens" : {
              "InstantLinksValidity": 3600,
              "ShareType": "ohif-viewer-publication"
            },
            "Keycloak" : {
              "Enable": true,
              "Url": "https://mydomain.com/keycloak/",
              "Realm": "orthanc",
              "ClientId": "orthanc"
            }
          },
          "AuthenticationEnabled": false,
          "Authorization": {
            "WebServiceRootUrl": "http://orthanc-auth-service:8000/",
            "WebServiceUsername": "share-user",
            "WebServicePassword": "change-me",
            "StandardConfigurations" : [
              "osimis-web-viewer",
              "stone-webviewer",
              "orthanc-explorer-2",
              "ohif"
            ],
            "TokenHttpHeaders" : [ "api-key" ],
            "CheckedLevel": "studies"
          },
          "DicomWeb": {
            "Enable": true,
            "PublicRoot": "/orthanc/dicom-web/"
          }
          , "OHIF": {
            "DataSource": "dicom-web",
            "RouterBasename": "/orthanc/ohif/"
          }
        }

  orthanc-auth-service:
    image: orthancteam/orthanc-auth-service:24.2.0
    volumes:
      - /opt/orthanc/orthanc-auth-service/permissions.jsonc:/orthanc_auth_service/permissions.json
    depends_on: [keycloak]
    restart: unless-stopped
    environment:
      SECRET_KEY: "thisismysecretkey"
      ENABLE_KEYCLOAK: "true"
      KEYCLOAK_CLIENT_SECRET: "thisismykeycloakclientsecret"
      PUBLIC_ORTHANC_ROOT: "https://mydomain.com/orthanc/"
      PUBLIC_LANDING_ROOT: "https://mydomain.com/orthanc/ui/app/token-landing.html"
      PUBLIC_OHIF_ROOT: "https://mydomain.com/orthanc/ohif/"
      USERS: |
        {
          "share-user": "thisismyshareuserpassword"
        }

  orthanc-db:
    image: postgres:14
    restart: unless-stopped
    volumes: ["/opt/orthanc/orthanc-db:/var/lib/postgresql/data"]
    environment:
      POSTGRES_HOST_AUTH_METHOD: "trust"

  keycloak:
    image: orthancteam/orthanc-keycloak:24.2.0
    depends_on: [keycloak-db]
    restart: unless-stopped
    environment:
      KEYCLOAK_ADMIN: "admin"
      KEYCLOAK_ADMIN_PASSWORD: "change-me"
      KC_DB: "postgres"
      KC_DB_URL: "jdbc:postgresql://keycloak-db:5432/keycloak"
      KC_DB_USERNAME: "keycloak"
      KC_DB_PASSWORD: "keycloak"
      KC_HOSTNAME_URL: "https://mydomain.com/keycloak"
      KC_HOSTNAME_ADMIN_URL: "https://mydomain.com/keycloak"

  keycloak-db:
    image: postgres:14
    restart: unless-stopped
    volumes: ["keycloak-db:/var/lib/postgresql/data"]
    environment:
      POSTGRES_PASSWORD: "keycloak"
      POSTGRES_USER: "keycloak"
      POSTGRES_DB: "keycloak"

volumes:
  orthanc-storage:
  orthanc-db:
  keycloak-db:

In my permissions.jsonc file:

{
  "roles" : {
    "admin-role": {
      "permissions": ["all"],
      "authorized_labels": ["*"]
    },
    "doctor-role": {
      "permissions":["view", "download", "share", "send"],
      "authorized_labels": ["*"]
    },
    "external-role": {
      "permissions":["view", "download"],
      "authorized_labels": ["external"]
    }
  }
}

You’re welcome

https://mydomain.com/… is just an example of what should be included in that field. so if you have a FQDN you should replace the mydomain.com field with that and also in all fields that require a URL, otherwise you can use localhost or use an IP address for external connection.

example.

http://localhost/

I did replace that. I just didn’t want to include my actual domain, so for making this post, I replaced it with “mydomain.com

Hi @threehappypenguins

It’s actually not easy to run your sample setup because it requires a domain and a certificate which is not straightforward to obtain. Note that we have a real life repository with https here (only difference is that we have a native nginx that handles the https)

I guess that if you get back to the minimal keycloak setup without https, it works for you right ?

I understand that you can log in as an admin right ? I guess you do not get any permissions in this call then ?

Then, you should check the logs from all the services (mainly Orthanc and the auth-service) around the login time to check for any weird things when requesting the user-profile …

HTH,

Alain.

No, if I don’t do https (which is what I attempted at first), I get an error saying that I have to use https. I have a remote setup (using an Oracle Cloud compute instance). So I have no idea if it would work without https or not.

This is what my admin page looks like when I log in:

I guess you do not get any permissions in this call then ?

I don’t know? What is that a screenshot of? You did an API call? I don’t have API enabled? How do I verify what permissions I have? In the permissions.json I have all for the admin permissions, so why would I not have all permissions when I spin up the docker containers?

Also, I checked the log of the auth-service container and after I log in, I see (which looks like an API call to me):

"POST /user/get-profile HTTP/1.1" 401 Unauthorized

Here is the full log after the container finally starts:

WARNING:root:PUBLIC_ORTHANC_ROOT is defined, configuring generator for standard 'osimis-viewer-publication' and 'stone-viewer-publication' shares
WARNING:root:SERVER_ID is not defined.  This is not mandatory
WARNING:root:PUBLIC_OHIF_ROOT is defined, configuring generator for standard 'ohif-viewer-publication'
WARNING:root:SERVER_ID is not defined.  This is not mandatory
WARNING:root:OHIF_DATA_SOURCE is not defined, will default to dicom-web.
WARNING:root:MEDDREAM_TOKEN_SERVICE_URL or PUBLIC_MEDDREAM_ROOT are not defined, the generator will not allow 'meddream-instant-links' shares
INFO:root:Got the roles and permissions from configuration file
WARNING:root:ENABLE_KEYCLOAK is set, using keycloak to handle users
INFO:root:Got the public key from Keycloak.
WARNING:root:ENABLE_KEYCLOAK_API_KEYS is not set, api-keys are disabled
WARNING:root:HTTP Basic auth is required to connect to the web-service
INFO:     Started server process [1]
INFO:uvicorn.error:Started server process [1]
INFO:     Waiting for application startup.
INFO:uvicorn.error:Waiting for application startup.
INFO:     Application startup complete.
INFO:uvicorn.error:Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:uvicorn.error:Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     172.18.0.4:57422 - "POST /user/get-profile HTTP/1.1" 401 Unauthorized
INFO:     172.18.0.4:40542 - "POST /user/get-profile HTTP/1.1" 401 Unauthorized
INFO:     172.18.0.4:52472 - "POST /user/get-profile HTTP/1.1" 401 Unauthorized
INFO:     172.18.0.4:48386 - "POST /user/get-profile HTTP/1.1" 401 Unauthorized

Here is my orthanc container log after I log in:

I0415 14:06:01.988802          HTTP-32 HttpServer.cpp:1262] (http) GET /ui/app
I0415 14:06:01.988888          HTTP-32 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app
I0415 14:06:02.065963          HTTP-33 HttpServer.cpp:1262] (http) GET /ui/app/assets/main-a245fdb1.js
I0415 14:06:02.066056          HTTP-33 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/main-a245fdb1.js
I0415 14:06:02.076757          HTTP-34 HttpServer.cpp:1262] (http) GET /ui/app/assets/index-85ee5be2.css
I0415 14:06:02.076831          HTTP-34 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/index-85ee5be2.css
I0415 14:06:02.077069          HTTP-35 HttpServer.cpp:1262] (http) GET /ui/app/assets/index-88ea2505.js
I0415 14:06:02.077112          HTTP-35 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/index-88ea2505.js
I0415 14:06:02.077783          HTTP-36 HttpServer.cpp:1262] (http) GET /ui/app/assets/index-427f6994.css
I0415 14:06:02.077825          HTTP-36 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/index-427f6994.css
I0415 14:06:02.504824          HTTP-38 HttpServer.cpp:1262] (http) GET /ui/api/pre-login-configuration
I0415 14:06:02.504907          HTTP-38 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/api/pre-login-configuration
I0415 14:06:02.847866          HTTP-39 HttpServer.cpp:1262] (http) GET /ui/api/configuration
I0415 14:06:02.847962          HTTP-39 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/api/configuration
I0415 14:06:02.848106          HTTP-39 OrthancPlugins.cpp:3245] (plugins) Plugin making REST GET call on URI /auth/user/profile (after plugins)
I0415 14:06:02.848183          HTTP-39 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /auth/user/profile
I0415 14:06:02.851289          HTTP-39 PluginsManager.cpp:161] (plugins) New HTTP request to: http://orthanc-auth-service:8000/user/get-profile (timeout: 10s)
I0415 14:06:02.859633          HTTP-49 HttpServer.cpp:1262] (http) GET /ui/app/assets/orthanc-0a9a8203.png
I0415 14:06:02.859840          HTTP-49 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/orthanc-0a9a8203.png
I0415 14:06:02.895537          HTTP-40 HttpServer.cpp:1262] (http) GET /ui/app/assets/fa-solid-900-886c8611.woff2
I0415 14:06:02.895783          HTTP-40 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/fa-solid-900-886c8611.woff2
I0415 14:06:02.926647          HTTP-40 HttpServer.cpp:1262] (http) GET /ui/app/assets/fa-regular-400-9169d8be.woff2
I0415 14:06:02.926814          HTTP-40 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/fa-regular-400-9169d8be.woff2
I0415 14:06:02.935920          HTTP-41 HttpServer.cpp:1262] (http) GET /ui/app/assets/bootstrap-icons-bacd70af.woff2
I0415 14:06:02.936008          HTTP-41 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /ui/app/assets/bootstrap-icons-bacd70af.woff2
I0415 14:06:03.064826          HTTP-39 PluginsManager.cpp:161] (plugins) HTTP status code 401 in 213 ms after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0415 14:06:03.064942          HTTP-39 PluginsManager.cpp:153] Error in HTTP request, received HTTP status 401 (Unauthorized) after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0415 14:06:03.137712          HTTP-39 PluginsManager.cpp:153] Bad file format: List of strings expected in field: permissions
E0415 14:06:03.137840          HTTP-39 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format

Is it this line?

Bad file format: List of strings expected in field: permissions

I apologize for not understanding all of this. It’s a bit fuzzy for me in my understanding in how all of this works together.

I figured it out. It was one simple line in my docker-compose.yml file in the orthanc-auth-service container:

USERS: |
        {
          "share-user": "change-me",
          "admin": "change-me" // I needed to add this line
        }

That’s it. That’s all I needed to do. Then I could log into keycloak and change the password for the admin afterward.

No problem, it’s quite a complex system that we use ourselves but that is not yet very well documented and community tested :wink:

Given your error, I would say that Orthanc does not have the correct credentials to access the python orhtanc-authorization-service. So I would check
image
and

They should match !

HTH,

Alain

I spoke too soon. After I made the changes that I specified in my previous comment, I tried to upload. The whole system would hang for around 5-10 minutes, and then resume and I would be logged out. I checked the orthanc container logs and:

I0415 15:37:50.746946 HTTP-16 PluginsManager.cpp:161] (plugins) New HTTP request to: http://orthanc-auth-service:8000/user/get-profile (timeout: 10s)
I0415 15:37:50.751044 HTTP-16 PluginsManager.cpp:161] (plugins) HTTP status code 400 in 4 ms after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0415 15:37:50.751078 HTTP-16 PluginsManager.cpp:153] Error in HTTP request, received HTTP status 400 (Bad Request) after POST request on: http://orthanc-auth-service:8000/user/get-profile
I0415 15:37:50.751187 HTTP-16 PluginsManager.cpp:161] (plugins) Testing whether user has the required permissions ‘all|upload’ based on the HTTP header ‘token’ required to match ‘^/instances$’ → not granted

I also forgot to mention that I had changed "share-user": "thisismyshareuserpassword", back to "share-user": "change-me",.

So how do I get the upload to work?

Since Orthanc receives a 400 from the auth-service, you should look at the logs from the auth-service and possibly the keycloak logs as well since the auth-service should contact keycloak to retrieve the user roles at that time.

I now have permission. In the orthanc-auth-service container:

USERS: |
        {
          "admin": "change-me", \\ Must have this line
          "doctor": "change-me"
        }

In the orthanc container:

Instead of:
"WebServiceUsername": "share-user",

I should have:

"WebServiceUsername": "admin",

Then I have permission to upload.

A couple of problems:

  1. When I upload, it takes forever, with no progress, and appears to hang. It even hangs in ssh terminal. I see it takes it immense amount of memory and CPU (I only have 1 GB of each in my Oracle instance). Then finally, when I see I have regained access to ssh, I can refresh the Orthanc page and I see the study there.

  2. When I try to view the study with OHIF, it’s a black screen. I click on the study, click on “View in OHIF” and it’s black. It doesn’t matter how long I wait… it stays black. If I go to https://mydomain.com/orthanc/ohif/ it redirects me to https://mydomain.com/orthanc/ohif/notfoundserver and says:

Unable to query for studies at this time. Check your data source configuration or network connection

A few more things:

  1. The upload feature needs some sort of “upload complete” dialog or something. There’s the green bar, but for all I know, it could be hanging on the last image. There is no way that I know of to verify that all the images uploaded.

  2. I can’t choose the entire study to upload. It’s too big, and causes my system to hang, and then not everything is uploaded (and no way to verify that it was complete; see previous comment). I have to upload each study, one by one, by folder. For the larger ones (100+ images), I chose by files, uploaded the headers zip first, and then chose 50 images at a time until I uploaded all images in the folder.

  3. I had to disable the OHIF plugin because I couldn’t get it to work (see previous post about black screen).

  4. After disabling the OHIF plugin, neither Stone Web Viewer nor OHIF can view the study images. The only images they seem to be able to view are the single image studies. They just don’t load. I noticed in Chrome Developer Tools that I’m getting the message:

You need to enable JavaScript to run this app.

Javascript is indeed enabled.

An update about the plugin… I’m not sure how I missed this, but I forgot to change this in my docker-compose.yml:

PUBLIC_OHIF_ROOT: "https://mydomain.com/ohif/"

to

PUBLIC_OHIF_ROOT: "https://mydomain.com/orthanc/ohif/"

I still get a black screen when I click on “View in Ohif”, but now it’s different when I go to https://mydomain.com/orthanc/ohif. It gives a 403 error and says:

You don’t have authorization to view this page.

Something else I missed about the plugin. There was a comment in the orthanc container in the docker-compose.yml from the github:

to use OHIF-plugin: use the master-unstable image

Once I spun up the master-unstable image instead (orthancteam/orthanc-pre-release:master-unstable), then I no longer got a black page. However, now I have the same issue as the ohif container… the images won’t load. I checked the orthanc-auth-service container, and after I open the Ohif viewer, I see a bunch of requests like (“tokenstuff” is a big long token):

INFO:root:get user profile: {"token_key":"Authorization","token_value":"Bearer tokenstuff","server_id":null}
INFO:     172.20.0.5:59550 - "POST /user/get-profile HTTP/1.1" 400 Bad Request

In the Orthanc container, I get:

I0416 21:04:50.559703          HTTP-12 ServerContext.cpp:1713] Number of matching resources: 1
I0416 21:04:50.560641          HTTP-12 PluginsManager.cpp:161] (plugins) DICOMweb RetrieveFrames on 6165177c-dcab060e-c425cf86-9f6342ee-ca23ebb7, frames: 1 
I0416 21:04:50.561163          HTTP-12 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/6165177c-dcab060e-c425cf86-9f6342ee-ca23ebb7/metadata/TransferSyntax (built-in API)
I0416 21:04:50.564400          HTTP-15 ServerContext.cpp:1565] Number of candidate resources after fast DB filtering on main DICOM tags: 1
I0416 21:04:50.564425          HTTP-13 HttpServer.cpp:1262] (http) GET /dicom-web/studies/1.2.840.114202.4.149708760.963734210.1534682287.1275927649/series/1.3.12.2.1107.5.2.19.145069.2023122716294030222879954.0.0.0/instances/1.3.12.2.1107.5.2.19.145069.2023122716353719864981099/frames/1
I0416 21:04:50.565380          HTTP-13 PluginsManager.cpp:161] (plugins) New HTTP request to: http://orthanc-auth-service:8000/user/get-profile (timeout: 10s)
I0416 21:04:50.627217          HTTP-13 PluginsManager.cpp:161] (plugins) HTTP status code 400 in 61 ms after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0416 21:04:50.628203          HTTP-13 PluginsManager.cpp:153] Error in HTTP request, received HTTP status 400 (Bad Request) after POST request on: http://orthanc-auth-service:8000/user/get-profile
I0416 21:04:50.628673          HTTP-13 PluginsManager.cpp:161] (plugins) Testing whether user has the required permissions 'all|view' based on the HTTP header 'Authorization' required to match '^/dicom-web/studies/([.0-9]+)/series/([.0-9]+)/instances/([.0-9]+)(|/|/frames/.*|/rendered|/metadata|/bulk/.*)(|/)$' -> not granted
I0416 21:04:50.629581          HTTP-13 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token
I0416 21:04:50.630470          HTTP-13 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token -> granted
I0416 21:04:50.631532          HTTP-13 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /dicom-web/studies/1.2.840.114202.4.149708760.963734210.1534682287.1275927649/series/1.3.12.2.1107.5.2.19.145069.2023122716294030222879954.0.0.0/instances/1.3.12.2.1107.5.2.19.145069.2023122716353719864981099/frames/1
I0416 21:04:50.632603          HTTP-13 OrthancPlugins.cpp:4058] (plugins) Plugin making REST POST call to URI /tools/find (after plugins)
I0416 21:04:50.633328          HTTP-13 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /tools/find
I0416 21:04:50.634276          HTTP-13 PluginsManager.cpp:161] (plugins) New HTTP request to: http://orthanc-auth-service:8000/user/get-profile (timeout: 10s)
I0416 21:04:50.632115          HTTP-17 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token
I0416 21:04:50.634992          HTTP-17 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token -> granted
I0416 21:04:50.635225          HTTP-17 OrthancPlugins.cpp:3278] (plugins) Plugin making REST POST call on URI /tools/find (built-in API)
I0416 21:04:50.639387          HTTP-13 PluginsManager.cpp:161] (plugins) HTTP status code 400 in 4 ms after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0416 21:04:50.639601          HTTP-13 PluginsManager.cpp:153] Error in HTTP request, received HTTP status 400 (Bad Request) after POST request on: http://orthanc-auth-service:8000/user/get-profile
I0416 21:04:50.640019          HTTP-13 OrthancPlugins.cpp:3278] (plugins) Plugin making REST POST call on URI /tools/lookup (built-in API)
I0416 21:04:50.632613          HTTP-15 ServerContext.cpp:1713] Number of matching resources: 1
I0416 21:04:50.634576          HTTP-12 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/6165177c-dcab060e-c425cf86-9f6342ee-ca23ebb7/file (built-in API)
I0416 21:04:50.641535          HTTP-15 PluginsManager.cpp:161] (plugins) DICOMweb RetrieveFrames on f2c1d272-1d074aa0-f174f70a-0cb3a349-54cb17f0, frames: 1 
I0416 21:04:50.641798          HTTP-15 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/f2c1d272-1d074aa0-f174f70a-0cb3a349-54cb17f0/metadata/TransferSyntax (built-in API)
I0416 21:04:50.652159          HTTP-16 ServerContext.cpp:1565] Number of candidate resources after fast DB filtering on main DICOM tags: 1
I0416 21:04:50.656182          HTTP-18 ServerContext.cpp:1565] Number of candidate resources after fast DB filtering on main DICOM tags: 1
I0416 21:04:50.659999          HTTP-17 ServerContext.cpp:1565] Number of candidate resources after fast DB filtering on main DICOM tags: 1
I0416 21:04:50.662358          HTTP-12 StorageCache.cpp:127] Read attachment "1e2ffb90-948b-4729-9090-40089b9db5b9" with content type 1 from cache
E0416 21:04:50.663120          HTTP-12 OrthancException.cpp:61] Bad file format: Cannot access a raw frame
E0416 21:04:50.663239          HTTP-12 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format
I0416 21:04:50.665167          HTTP-16 ServerContext.cpp:1713] Number of matching resources: 1
I0416 21:04:50.665693          HTTP-16 PluginsManager.cpp:161] (plugins) DICOMweb RetrieveFrames on 0a0aae50-66649c14-5f5ce8f3-59e26edb-4e943d1b, frames: 1 
I0416 21:04:50.666020          HTTP-16 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/0a0aae50-66649c14-5f5ce8f3-59e26edb-4e943d1b/metadata/TransferSyntax (built-in API)
I0416 21:04:50.667218          HTTP-16 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/0a0aae50-66649c14-5f5ce8f3-59e26edb-4e943d1b/file (built-in API)
I0416 21:04:50.668630          HTTP-17 ServerContext.cpp:1713] Number of matching resources: 1
I0416 21:04:50.725628          HTTP-17 PluginsManager.cpp:161] (plugins) DICOMweb RetrieveFrames on 3e5e3621-d4d28570-e994c894-b291e72f-37d81924, frames: 1 
I0416 21:04:50.725974          HTTP-17 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/3e5e3621-d4d28570-e994c894-b291e72f-37d81924/metadata/TransferSyntax (built-in API)
I0416 21:04:50.726551          HTTP-15 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/f2c1d272-1d074aa0-f174f70a-0cb3a349-54cb17f0/file (built-in API)
I0416 21:04:50.730494          HTTP-13 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token
I0416 21:04:50.730533          HTTP-13 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token -> granted
I0416 21:04:50.730556          HTTP-13 OrthancPlugins.cpp:3278] (plugins) Plugin making REST POST call on URI /tools/find (built-in API)
I0416 21:04:50.736969          HTTP-13 ServerContext.cpp:1565] Number of candidate resources after fast DB filtering on main DICOM tags: 1
I0416 21:04:50.738461          HTTP-20 HttpServer.cpp:1262] (http) GET /dicom-web/studies
I0416 21:04:50.739068          HTTP-13 ServerContext.cpp:1713] Number of matching resources: 1
I0416 21:04:50.739213          HTTP-13 PluginsManager.cpp:161] (plugins) DICOMweb RetrieveFrames on a851e8b9-475c179f-37af9f70-5c0dd0ad-c2fdeb1a, frames: 1 
I0416 21:04:50.739224          HTTP-13 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/a851e8b9-475c179f-37af9f70-5c0dd0ad-c2fdeb1a/metadata/TransferSyntax (built-in API)
I0416 21:04:50.739564          HTTP-20 PluginsManager.cpp:161] (plugins) New HTTP request to: http://orthanc-auth-service:8000/user/get-profile (timeout: 10s)
I0416 21:04:50.741061          HTTP-18 ServerContext.cpp:1713] Number of matching resources: 1
I0416 21:04:50.741205          HTTP-18 PluginsManager.cpp:161] (plugins) DICOMweb RetrieveFrames on f9c4a8f7-8450bd39-3501e3c8-1368053a-0b20b613, frames: 1 
I0416 21:04:50.741216          HTTP-18 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/f9c4a8f7-8450bd39-3501e3c8-1368053a-0b20b613/metadata/TransferSyntax (built-in API)
I0416 21:04:50.743481          HTTP-18 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/f9c4a8f7-8450bd39-3501e3c8-1368053a-0b20b613/file (built-in API)
I0416 21:04:50.744308          HTTP-18 StorageCache.cpp:127] Read attachment "e961923b-f635-462d-818f-9110786fcce0" with content type 1 from cache
E0416 21:04:50.745156          HTTP-18 OrthancException.cpp:61] Bad file format: Cannot access a raw frame
E0416 21:04:50.745267          HTTP-18 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format
I0416 21:04:50.747032          HTTP-15 StorageCache.cpp:127] Read attachment "b4482d27-667d-4324-b3b1-a31ee528dffa" with content type 1 from cache
E0416 21:04:50.748549          HTTP-15 OrthancException.cpp:61] Bad file format: Cannot access a raw frame
E0416 21:04:50.749179          HTTP-15 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format
I0416 21:04:50.750639          HTTP-13 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/a851e8b9-475c179f-37af9f70-5c0dd0ad-c2fdeb1a/file (built-in API)
I0416 21:04:50.751885          HTTP-16 StorageCache.cpp:127] Read attachment "14573827-559a-490c-9bfe-e6f0200ddc82" with content type 1 from cache
E0416 21:04:50.753399          HTTP-16 OrthancException.cpp:61] Bad file format: Cannot access a raw frame
E0416 21:04:50.755545          HTTP-16 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format
I0416 21:04:50.755386          HTTP-17 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/3e5e3621-d4d28570-e994c894-b291e72f-37d81924/file (built-in API)
I0416 21:04:50.757545          HTTP-17 StorageCache.cpp:127] Read attachment "739ca11f-6690-4396-8fa4-9bce240bd83f" with content type 1 from cache
E0416 21:04:50.759319          HTTP-17 OrthancException.cpp:61] Bad file format: Cannot access a raw frame
E0416 21:04:50.827403          HTTP-17 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format
I0416 21:04:50.829387          HTTP-20 PluginsManager.cpp:161] (plugins) HTTP status code 400 in 89 ms after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0416 21:04:50.829481          HTTP-20 PluginsManager.cpp:153] Error in HTTP request, received HTTP status 400 (Bad Request) after POST request on: http://orthanc-auth-service:8000/user/get-profile
I0416 21:04:50.830161          HTTP-20 PluginsManager.cpp:161] (plugins) Testing whether user has the required permissions 'all|view' based on the HTTP header 'Authorization' required to match '^/dicom-web/(studies|series|instances)(|/)$' -> not granted
I0416 21:04:50.833195          HTTP-13 StorageCache.cpp:127] Read attachment "4e9f6d83-afc1-4f3a-af7d-224576d2ddeb" with content type 1 from cache
E0416 21:04:50.834744          HTTP-13 OrthancException.cpp:61] Bad file format: Cannot access a raw frame
E0416 21:04:50.834941          HTTP-13 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format
I0416 21:04:50.924947          HTTP-19 HttpServer.cpp:1262] (http) GET /dicom-web/studies/1.2.840.114202.4.149708760.963734210.1534682287.1275927649/series/1.3.12.2.1107.5.2.19.145069.2023122716130076820674112.0.0.0/instances/1.3.12.2.1107.5.2.19.145069.2023122716130076242274109/frames/1
I0416 21:04:50.925122          HTTP-19 PluginsManager.cpp:161] (plugins) New HTTP request to: http://orthanc-auth-service:8000/user/get-profile (timeout: 10s)
I0416 21:04:50.927930          HTTP-19 PluginsManager.cpp:161] (plugins) HTTP status code 400 in 2 ms after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0416 21:04:50.927965          HTTP-19 PluginsManager.cpp:153] Error in HTTP request, received HTTP status 400 (Bad Request) after POST request on: http://orthanc-auth-service:8000/user/get-profile
I0416 21:04:50.928091          HTTP-19 PluginsManager.cpp:161] (plugins) Testing whether user has the required permissions 'all|view' based on the HTTP header 'Authorization' required to match '^/dicom-web/studies/([.0-9]+)/series/([.0-9]+)/instances/([.0-9]+)(|/|/frames/.*|/rendered|/metadata|/bulk/.*)(|/)$' -> not granted
I0416 21:04:50.930050          HTTP-19 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/82687d19-cf4b2840-b28bbe3d-2fec321b-f2a457c3 (built-in API)
I0416 21:04:50.932425          HTTP-19 PluginsManager.cpp:161] (plugins) Linking Instance "82687d19-cf4b2840-b28bbe3d-2fec321b-f2a457c3" to its parent Series "e530fb8b-46f426ae-5e00030d-601ba70b-b08967e0"
I0416 21:04:50.932462          HTTP-19 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token
I0416 21:04:50.932478          HTTP-19 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token -> granted
I0416 21:04:50.932523          HTTP-19 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /dicom-web/studies/1.2.840.114202.4.149708760.963734210.1534682287.1275927649/series/1.3.12.2.1107.5.2.19.145069.2023122716130076820674112.0.0.0/instances/1.3.12.2.1107.5.2.19.145069.2023122716130076242274109/frames/1
I0416 21:04:50.932569          HTTP-19 OrthancPlugins.cpp:4058] (plugins) Plugin making REST POST call to URI /tools/find (after plugins)
I0416 21:04:50.932605          HTTP-19 OrthancPlugins.cpp:2475] (plugins) Delegating HTTP request to plugin for URI: /tools/find
I0416 21:04:50.932680          HTTP-19 PluginsManager.cpp:161] (plugins) New HTTP request to: http://orthanc-auth-service:8000/user/get-profile (timeout: 10s)
I0416 21:04:50.935984          HTTP-19 PluginsManager.cpp:161] (plugins) HTTP status code 400 in 3 ms after POST request on: http://orthanc-auth-service:8000/user/get-profile
E0416 21:04:50.936017          HTTP-19 PluginsManager.cpp:153] Error in HTTP request, received HTTP status 400 (Bad Request) after POST request on: http://orthanc-auth-service:8000/user/get-profile
I0416 21:04:50.936142          HTTP-19 OrthancPlugins.cpp:3278] (plugins) Plugin making REST POST call on URI /tools/lookup (built-in API)
I0416 21:04:50.942701          HTTP-19 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token
I0416 21:04:50.942766          HTTP-19 PluginsManager.cpp:161] (plugins) Testing whether access to study "803a049a-cef111ef-d4bcc8f5-45ec6d93-3fc38c40" is allowed with a resource token -> granted
I0416 21:04:50.942794          HTTP-19 OrthancPlugins.cpp:3278] (plugins) Plugin making REST POST call on URI /tools/find (built-in API)
I0416 21:04:50.949448          HTTP-19 ServerContext.cpp:1565] Number of candidate resources after fast DB filtering on main DICOM tags: 1
I0416 21:04:51.025856          HTTP-19 ServerContext.cpp:1713] Number of matching resources: 1
I0416 21:04:51.026122          HTTP-19 PluginsManager.cpp:161] (plugins) DICOMweb RetrieveFrames on 82687d19-cf4b2840-b28bbe3d-2fec321b-f2a457c3, frames: 1 
I0416 21:04:51.026138          HTTP-19 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/82687d19-cf4b2840-b28bbe3d-2fec321b-f2a457c3/metadata/TransferSyntax (built-in API)
I0416 21:04:51.027916          HTTP-19 OrthancPlugins.cpp:3221] (plugins) Plugin making REST GET call on URI /instances/82687d19-cf4b2840-b28bbe3d-2fec321b-f2a457c3/file (built-in API)
I0416 21:04:51.030493          HTTP-19 FilesystemStorage.cpp:164] Reading attachment "8881f5f5-d188-4738-b729-de757bd53409" of "DICOM" content type
I0416 21:04:51.035421          HTTP-19 FilesystemStorage.cpp:170] Read attachment "8881f5f5-d188-4738-b729-de757bd53409" (118.19KB in 5.04ms = 191.91Mbps)
E0416 21:04:51.038556          HTTP-19 OrthancException.cpp:61] Bad file format: Cannot access a raw frame
E0416 21:04:51.039034          HTTP-19 PluginsErrorDictionary.cpp:100] Exception inside the plugin engine: Bad file format

This is my current docker-compose.yml. Everything is exactly as is, including change-me. The only things redacted (changed for privacy purposes) are my domain is changed to mydomain.com, and I changed SECRET_KEY, and KEYCLOAK_CLIENT_SECRET:

version: "3"
services:

  nginx:
    image: orthancteam/orthanc-nginx:24.2.0
    depends_on: [orthanc, orthanc-auth-service, keycloak]
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /etc/letsencrypt/live/mydomain.com/fullchain.pem:/etc/nginx/tls/crt.pem
      - /etc/letsencrypt/live/mydomain.com/privkey.pem:/etc/nginx/tls/key.pem
    environment:
      ENABLE_ORTHANC: "true"
      ENABLE_KEYCLOAK: "true"
      ENABLE_ORTHANC_TOKEN_SERVICE: "true"
      ENABLE_HTTPS: "true"
# to use OHIF-plugin: you don't need the OHIF reverse proxy
      ENABLE_OHIF: "false"

  orthanc:
    image: orthancteam/orthanc-pre-release:master-unstable
    volumes:
      - /opt/orthanc/storage:/var/lib/orthanc/db
    depends_on: [orthanc-db]
    restart: unless-stopped
    environment:
      STONE_WEB_VIEWER_PLUGIN_ENABLED: "true"
      DICOM_WEB_PLUGIN_ENABLED: "true"
      # to use OHIF-plugin:  make sure to have a "OHIF" config and use "dicom-web" source
      OHIF_PLUGIN_ENABLED: "true"
      ORTHANC__POSTGRESQL__HOST: "orthanc-db"
      VERBOSE_ENABLED: "true"
      VERBOSE_STARTUP: "true"
      ORTHANC_JSON: |
        {
          "Name": "Orthanc",
          "OrthancExplorer2": {
            "IsDefaultUI": true,
            "UiOptions": {
              "EnableShares": true,
              "DefaultShareDuration": 0,
              "ShareDurations": [0, 7, 15, 30, 90, 365],
              "EnableOpenInOhifViewer3": true,
			  // to use OHIF-plugin:  use http://localhost/orthanc/ohif/
              "OhifViewer3PublicRoot": "https://mydomain.com/orthanc/ohif/"
            },
            "Tokens" : {
              "InstantLinksValidity": 3600,
              "ShareType": "ohif-viewer-publication"
            },
            "Keycloak" : {
              "Enable": true,
              //"Url": "http://localhost/keycloak/",
              "Url": "https://mydomain.com/keycloak/",
              "Realm": "orthanc",
              "ClientId": "orthanc"
            }
          },
          "AuthenticationEnabled": false,     // because it is handled by the authorization plugin
          "Authorization": {
            "WebServiceRootUrl": "http://orthanc-auth-service:8000/",
            "WebServiceUsername": "admin",
            "WebServicePassword": "change-me",
            // to use OHIF-plugin:  make sure to include "ohif" in the list
            "StandardConfigurations" : [
              "osimis-web-viewer",
              "stone-webviewer",
              "orthanc-explorer-2",
              "ohif"
            ],
            "TokenHttpHeaders" : [ "api-key" ],
            "CheckedLevel": "studies"
          },
          "DicomWeb": {
            "Enable": true,
            "PublicRoot": "/orthanc/dicom-web/"
          }
          // to use OHIF-plugin:  make sure to have a "OHIF" config and use "dicom-web" source
          , "OHIF": {
            "DataSource": "dicom-web",
            "RouterBasename": "/orthanc/ohif/"
          }
        }

  orthanc-auth-service:
    image: orthancteam/orthanc-auth-service:24.2.0
    volumes:
      - /opt/orthanc/orthanc-auth-service/permissions.jsonc:/orthanc_auth_service/permissions.json
    depends_on: [keycloak]
    restart: unless-stopped
    environment:
      SECRET_KEY: "mysecretkeyhere"
      ENABLE_KEYCLOAK: "true"
      KEYCLOAK_CLIENT_SECRET: "mykeycloakclientsecret"
      PUBLIC_ORTHANC_ROOT: "https://mydomain.com/orthanc/"
      PUBLIC_LANDING_ROOT: "https://mydomain.com/orthanc/ui/app/token-landing.html"
      PUBLIC_OHIF_ROOT: "https://mydomain.com/orthanc/ohif/"
      USERS: |
        {
          "share-user": "change-me",
          "admin": "change-me",
          "doctor": "change-me"
        }

  orthanc-db:
    image: postgres:14
    restart: unless-stopped
    volumes: ["/opt/orthanc/orthanc-db:/var/lib/postgresql/data"]
    environment:
      POSTGRES_HOST_AUTH_METHOD: "trust"

  keycloak:
    image: orthancteam/orthanc-keycloak:24.2.0
    depends_on: [keycloak-db]
    restart: unless-stopped
    environment:
      KEYCLOAK_ADMIN: "admin"
      KEYCLOAK_ADMIN_PASSWORD: "change-me"
      KC_DB: "postgres"
      KC_DB_URL: "jdbc:postgresql://keycloak-db:5432/keycloak"
      KC_DB_USERNAME: "keycloak"
      KC_DB_PASSWORD: "keycloak"
      KC_HOSTNAME_URL: "https://mydomain.com/keycloak"
      KC_HOSTNAME_ADMIN_URL: "https://mydomain.com/keycloak"

  keycloak-db:
    image: postgres:14
    restart: unless-stopped
    volumes: ["keycloak-db:/var/lib/postgresql/data"]
    environment:
      POSTGRES_PASSWORD: "keycloak"
      POSTGRES_USER: "keycloak"
      POSTGRES_DB: "keycloak"

volumes:
  orthanc-storage:
  orthanc-db:
  keycloak-db: