Mercurial > hg > orthanc-book
changeset 1020:3f4a7ee8033b
auth plugin huge rewrite
author | Alain Mazy <am@osimis.io> |
---|---|
date | Fri, 26 Jan 2024 18:37:04 +0100 |
parents | a1d28570ef23 |
children | a3436ae3709c |
files | Sphinx/source/plugins/authorization.rst |
diffstat | 1 files changed, 255 insertions(+), 130 deletions(-) [+] |
line wrap: on
line diff
--- a/Sphinx/source/plugins/authorization.rst Fri Jan 26 15:15:46 2024 +0100 +++ b/Sphinx/source/plugins/authorization.rst Fri Jan 26 18:37:04 2024 +0100 @@ -8,14 +8,21 @@ This **official plugin** extends Orthanc with an advanced authorization mechanism. For each incoming REST request to some URI, -the plugin will query a Web service to know whether the access is -granted to the user. If access is not granted, the HTTP status code is -set to ``403`` (Forbidden). +the plugin will query an external Web service to check whether the access should be granted. +If access is not granted, the HTTP status code is set to ``403`` (Forbidden). + +The request must include either an ``HTTP header`` or a ``GET argument`` that may +either identify a ``user`` or an access to a single DICOM ``resource`` (an instance, +a series, a study or a patient). + +In the text below, the ``HTTP header`` and the ``GET argument`` is named the ``token``. + **Status:** This plugin was `deprecated <https://discourse.orthanc-server.org/t/advanced-authorization-plugin-vs-remote-access/1859/5?u=jodogne>`__ between 2020 and 2022, but its active development has been resumed -since May 2022. +since May 2022 and is intensively used in the `orthanc-auth-service <https://github.com/orthanc-team/orthanc-auth-service>`__ +project that provides user permissions and sharing of single studies. How to get it ? @@ -51,9 +58,7 @@ "/home/user/OrthancAuthorization/Build/libOrthancAuthorization.so" ], "Authorization" : { - "WebServiceRootUrl" : "http://localhost:8000/", - "WebServiceUsername": "my-user", - "WebServicePassword": "my-password" + // .. all options are document below } } @@ -61,36 +66,141 @@ configuration file. -Web Service ------------ +User based authorization vs resource based tokens +------------------------------------------------- + +The plugin can work in 2 modes that can be combined: -This section describes how a Web service suitable for the -authorization plugin can be designed. +* **User based authorization** is used e.g. in :ref:`Orthanc Explorer 2 <orthanc-explorer-2>` + to allow various ``actions`` based on ``permissions`` defined in a ``user profile``. +* **Resource based authorization** is used e.g. to share a link that + grants access to a single DICOM resource (e.g. a study). -Incoming request -^^^^^^^^^^^^^^^^ +External Web Service +-------------------- + +This section describes how an external Web service suitable for the +authorization plugin can be designed. For each HTTP/REST request that Orthanc receives, the plugin will issue a set of HTTP ``POST`` requests against the Web service that is -specified in the configuration file (in the basic configuration file -above, the Web service listening at ``http://localhost:8000/tokens/validate`` is -used). The body of each of those ``POST`` requests is a JSON file -similar to the following one:: +specified in the configuration file. + +Depending on the kind of authorization you'd like to use, your Web service shall +implement part or all of these routes: + +- ``/tokens/validate`` to validate tokens identifying a DICOM **resource** +- ``/tokens/{token_type}`` to generate tokens granting access to specific DICOM **resources**. +- ``/tokens/decode`` to extract the info from a **resource** token +- ``/user/get-profile`` to return the **user profile** linked to a given token. This profile + must include a list of permissions. + +These routes url may be defined individually or globally in the configuration file. + +**Note:** The source code of the plugin contains a `basic example +<https://orthanc.uclouvain.be/hg/orthanc-authorization/file/default/Resources/TestService.js>`__ +of a simple Web service that implements only the ``validate`` route. + +The `orthanc-auth-service project <https://github.com/orthanc-team/orthanc-auth-service>`__ +provides a full implementation of the Web service. It notably contains a `definition of +all the requests and responses <https://github.com/orthanc-team/orthanc-auth-service/blob/main/sources/orthanc_auth_service/shares/models.py>`__ used between the plugin and the Web service. + + +Resource tokens generation: /tokens/{token_type} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The tokens can actually be generated anywhere, e.g, in the `orthanc-auth-service project <https://github.com/orthanc-team/orthanc-auth-service>`__, +the **user tokens** are generated by KeyCloak. But a user logged into Orthanc can also +generate links to share a single study in Orthanc Explorer 2. In this case, OE2 will +call the authorization plugin that will forward the call to the Authorization Web Service (this route) +that will generate a **resource token**. + +The implementation of this route is optional and only required if you want to generate share links +in OE2. + +Your Web service receives this kind of POST requests:: + + { + "id": "your-optional-id", + "type": "depending-on-your-web-service", // will instruct your Web service how to generate the url to access the resource (if relevant) + "resources": [ // a list of Orthanc resources that can be identified either by the orthanc id + // or their DICOM ID (SOPInstanceUID, StudyInstanceUID, PatientID, SeriesInstanceUID) + { + "dicom-uid": "1.2.3", + "orthanc-id": "6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8", + "level": "study", // one of "patient", "study", "series", "instance", "system" + "url": "/optional/system/url" // only for system level resources + } + ], + "expiration-date": "2027-04-23T19:25:43.511Z", // optional + "validity-duration": 3600 // validity duration (in seconds) + } + +And your Web service must provide this kind of responses:: { - "dicom-uid" : "123ABC", - "level" : "patient", - "method" : "get", - "orthanc-id" : "6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8", - "server-id": null, - "uri": null + "request": {}, // a copy of the request + "token": "my-super-safe-resource-token", // the token that will identify the resource + "url": "http://optional.link.to/ui/app/token-landing.html?token=my-super-safe-resource-token" // optional: url to access the shared resource + } + + +Resource tokens decoding: /tokens/decode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This route is quite specific to OE2 shares: When a user opens OE2 with a **resource token**, +it usually lands on a specific landing page that calls this route to extract the content +of the token to know e.g which viewer must be opened to display the DICOM resource or to check +if the token has expired. + +The implementation of this route is optional and only required if you want to open the share links +in OE2. + +Your Web service receives this kind of POST requests:: + + { + "token-key": "token", // the name of the token (HTTP Header or GET argument) + "token-value": "my-super-safe-resource-token" // the token to be decoded + } + +And your Web service must provide this kind of responses:: + + { + "token-type": "depending-on-your-web-service", // the type of the token + "redirect-url": "http://your.domain.com/orthanc/stone-webviewer/index.html?study=...&token=....", + "error-code": "expired" // optional; one of "expired", "invalid", "unknown". This is used to display + // a friendly user message in OE2 in case of error. + } + + +Resource tokens validation: /tokens/validate +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This route must absolutely be implemented if you want to implement **resource** based authentication, +For each query that is made through Orthanc, Orthanc will use the response of this route +to grant access or not to the API route. + +Consider that a user issues this request:: + + curl -H "auth-token-header: my-super-safe-resource-token" http://localhost:8042/patients/6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8 + +Your Web service receives this kind of POST requests:: + + { + "dicom-uid": "123ABC", + "orthanc-id": "6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8", + "level": "patient", + "method": "get", + "token-key": "auth-token-header", + "token-value": "my-super-safe-resource-token", + "server-id": "optional-id-ex-orthanc-public" } In this example, the user is accessing an URI that is related to some DICOM resource, namely a patient whose DICOM identifier is -``123ABC``. In such a case, the following fields will be set in the -JSON body: +``123ABC`` and orthanc id ``6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8``. +In such a case, the following fields will be set in the JSON body: * The ``level`` field specifies which type of resource the user is accessing, according to the :ref:`DICOM model of the real world @@ -111,30 +221,6 @@ configuration or ``null`` if this configuration is not defined. This allows the WebService to identity which Orthanc instance is calling it (new in v 0.3.0). -When the user accesses a lower-level resource in the DICOM hierarchy -(a study, a series or an instance), the authorization plugin will -issue one separate call to the Web service for each level of the -hierarchy. For instance, here are the 3 successive requests that are -issued when accessing some series:: - - { - "dicom-uid" : "123ABC", - "level" : "patient", - "method" : "get", - "orthanc-id" : "6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8" - } - { - "dicom-uid" : "1.3.51.0.1.1.192.168.29.133.1681753.1681732", - "level" : "study", - "method" : "get", - "orthanc-id" : "6e2c0ec2-5d99c8ca-c1c21cee-79a09605-68391d12" - } - { - "dicom-uid" : "1.3.12.2.1107.5.2.33.37097.2012041612474981424569674.0.0.0", - "level" : "series", - "method" : "get", - "orthanc-id" : "6ca4c9f3-5e895cb3-4d82c6da-09e060fe-9c59f228" - } It the user is accessing a URI that is not directly related to an individual DICOM resource, the JSON body will look as follows:: @@ -142,7 +228,10 @@ { "level" : "system", "method" : "get", - "uri" : "/changes" + "uri" : "/changes", + "token-key": "auth-token-header", + "token-value": "my-super-safe-resource-token", + "server-id": "optional-id-ex-orthanc-public" } In such a situation, the following fields are set: @@ -150,29 +239,16 @@ * The ``level`` field is always set to ``system``. * The ``method`` field is the same as above. * The ``uri`` field provides the URI that was accessed by the user. - -**Important note:** The plugin will transparently parse the URIs of -the core :ref:`REST API of Orthanc <rest>`, of the :ref:`Web viewer -plugin <webviewer>`, of the :ref:`DICOMweb plugin <dicomweb>`, and of -the :ref:`whole-slide imaging plugin <wsi>`. Unrecognized URIs (such -as those introduced by other plugins) will be handled as a ``system`` -call. It is possible to introduce parsing support for more plugins by -modifying the ``DefaultAuthorizationParser`` C++ class in the source -code of the plugin. - + -Expected answer -^^^^^^^^^^^^^^^ - -The Web service must answer by sending a JSON file that tells whether -the access is granted or not to the user. Here is a sample answer:: +And your Web service must provide this kind of responses:: { "granted": true, - "validity" : 5 + "validity": 60 } -Here is a description of these two fields: +Where: * ``granted`` tells whether access to the resource is granted (``true``) or not granted (``false``). In the case the user is @@ -181,72 +257,124 @@ over the levels). * ``validity`` tells the authorization plugin for how many seconds the result of the Web service must be cached. If set to ``0`` second, - the cache entry will never expire. - -**Note:** The source code of the plugin contains a `basic example -<https://orthanc.uclouvain.be/hg/orthanc-authorization/file/default/Resources/TestService.js>`__ -of such a Web service written in node.js. + the cache entry will never expire. By setting a ``validity`` duration, + Orthanc can cache the response to avoid asking the same question + thousands of times to your web-service e.g. when opening a study in a web viewer. -Authentication tokens -^^^^^^^^^^^^^^^^^^^^^ -It is obviously desirable to limit access to the resources depending -on the user that is logged in. Real-life Web framework such as Django -would send the identity of the authenticated user either as an HTTP -header, or as an additional argument for ``GET`` requests. The -authorization plugin allows to forward these authentication tokens to -the Web service. - -To configure the authentication plugin to use some HTTP header, one -must provide the option ``TokenHttpHeaders`` the configuration file of -Orthanc as follows:: - - { - "Name" : "MyOrthanc", - [...] - "Authorization" : { - "WebService" : "http://localhost:8000/", - "TokenHttpHeaders" : [ "token" ] - } - } - -.. highlight:: text - -In such a situation, if some HTTP client issues the following call:: - - # curl -H 'token: my-token' http://localhost:8042/patients/6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8 - -.. highlight:: json - -Here is the JSON body the Web service would receive:: +**Note** depending on your configuration, the Web service might receive multiple requests, one for +each level of the hierarchy that must be checked (see in the configuration below). E.G:: { "dicom-uid" : "123ABC", "level" : "patient", "method" : "get", "orthanc-id" : "6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8", - "token-key" : "token", - "token-value" : "my-token" + ... + } + { + "dicom-uid" : "1.3.51.0.1.1.192.168.29.133.1681753.1681732", + "level" : "study", + "method" : "get", + "orthanc-id" : "6e2c0ec2-5d99c8ca-c1c21cee-79a09605-68391d12", + ... + } + { + "dicom-uid" : "1.3.12.2.1107.5.2.33.37097.2012041612474981424569674.0.0.0", + "level" : "series", + "method" : "get", + "orthanc-id" : "6ca4c9f3-5e895cb3-4d82c6da-09e060fe-9c59f228", + ... } -.. highlight:: text + +**Important note:** The plugin will transparently parse the URIs of +the core :ref:`REST API of Orthanc <rest>` and the most common official plugins. +Unrecognized URIs (such +as those introduced by other non official plugins) will be handled as a ``system`` +call. It is possible to introduce parsing support for more plugins by +modifying the ``DefaultAuthorizationParser`` C++ class in the source +code of the plugin. + + + + +Get User profile: /user/get-profile +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This route must absolutely be implemented if you want to implement **user** permissions based authorization. -Note how the key and the value of the authentication token stored as a -HTTP header are forwarded to the Web service. +Note that **user** based authorization has been implemented with the OE2 integration in mind. +It has currently not been designed for other use cases. + +For each query that is made through Orthanc, if no **resource token** granting access to the route was found, +Orthanc will possibly try to retrieve a **user profile** to identify a possible user for this token. + +Consider that a user issues this request:: + + curl -H "auth-token-header: my-super-safe-user-token" http://localhost:8042/studies/6e2c0ec2-5d99c8ca-c1c21cee-79a09605-68391d12 -The same mechanism can be used if the authentication token is provided -as some ``GET`` argument by setting the ``TokenGetArguments`` -configuration option:: + +Your Web service receives this kind of POST requests:: + + { + "token-key": "auth-token-header", + "token-value": "my-super-safe-user-token", + "server-id": "optional-id-ex-orthanc-public" + } + +And your Web service must provide this kind of responses:: - # curl http://localhost:8042/patients/6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8?token=my-token { - "dicom-uid" : "123ABC", - "level" : "patient", - "method" : "get", - "orthanc-id" : "6eeded74-75005003-c3ae9738-d4a06a4f-6beedeb8", - "token-key" : "token", - "token-value" : "my-token" + "name": "John Who", // The name of the user (e.g. to display in OE2) + "authorized-labels": [ // A list of labels the user has access to. + "my-label", // use "*" to grant access to all labels + "his-label" + ], + "permissions": [ // A list of permissions for this user + "view", + "upload", + "..." + ] + "validity": 60 // the validity duration (in seconds) of this response. + } + +By setting a ``validity`` duration, Orthanc can cache the response to avoid asking the same question +thousands of times to your web-service e.g. when opening a study in a web viewer. + +If a list of ``authorized-labels`` has been returned, the authorization plugin will add a label filter to each call to +``tools/find`` to include only the labels the user has access to or, when accessing a specific DICOM resource, the plugin will +check that the resource has one of these ``authorized-labels``. + +The list of ``permissions`` are defined in the plugin configuration. +E.g, the following configuration defines that a user must have either the ``all`` or the ``view`` permission to +be authorized to issue GET requests to ``/studies/{orthanc-id}``, provided that the study has one of the labels +that is listed in the ``authorized-labels`` :: + + ["get" , "^/(patients|studies|series|instances)/([a-f0-9-]+)$", "all|view"], + + +This permission defines that a user must have either the ``all`` or the ``share`` permission to be +authorized to issue a PUT request to generate a **resource token** to share a single DICOM study:: + + ["put", "^/auth/tokens/(stone-viewer-publication||ohif-viewer-publication)$", "all|share"], + + + + +Authentication tokens +^^^^^^^^^^^^^^^^^^^^^ + +To configure the authentication plugin to use some HTTP header or GET argument, one +must provide these options:: + + { + "Authorization" : { + ... + "TokenHttpHeaders" : [ "token-header" ], + "TokenGetArguments" : [ "token-in-url" ], + } } **Note 1:** It is allowed to provide a list of HTTP tokens or a list @@ -273,17 +401,15 @@ { "Authorization" : { - // The Base URL of the auth webservice. This is an alias for all 3 next configurations: + // The Base URL of the auth webservice. This is an alias for all next 4 configurations: // // "WebServiceUserProfileUrl" : " ROOT /user/get-profile", // // "WebServiceTokenValidationUrl" : " ROOT /tokens/validate", // // "WebServiceTokenCreationBaseUrl" : " ROOT /tokens/", // // "WebServiceTokenDecoderUrl" : " ROOT /tokens/decode", - // You should define it only if your auth webservice implements all 3 routes ! + // You should define it only if your auth webservice implements all 4 routes ! // "WebServiceRootUrl" : "http://change-me:8000/", // The URL of the auth webservice route implementing user profile (optional) - // (this configuration was previously named "WebService" and its old name is still accepted - // for backward compatibility) // "WebServiceUserProfileUrl" : "http://change-me:8000/user/profile", // The URL of the auth webservice route implementing resource level authorization (optional) @@ -299,7 +425,8 @@ //"WebServiceUsername": "change-me", //"WebServicePassword": "change-me", - // An identifier added to the payload of each request to the auth webservice (optional) + // An identifier added to the payload of each request to the auth webservice (optional). + // It is used to identify the Orthanc instance that is sending the request to the auth webservice //"WebServiceIdentifier": "change-me" // The name of the HTTP headers that may contain auth tokens @@ -366,6 +493,10 @@ simply associates its studies to a set of granted users: In this case, the series and instance levels can be ignored. +* ``WebServiceIdentifier`` is used to identify the Orthanc instance that + is calling the Web service. This value is copied in ``server-id`` in + the requests to the web services (new in v 0.3.0). + Here is a minimal configuration for the :ref:`Stone Web viewer <stone_webviewer>`:: @@ -391,13 +522,7 @@ of a Web services integrating with :ref:`Orthanc Explorer 2 <orthanc-explorer-2>` to implement user level permissions and sharing of single studies. -This sample also shows how to implement all routes that the webservice might provide: - -- ``/tokens/validate`` to validate tokens identifying either a user or granting access to a single resource -- ``/tokens/{token_type}`` to generate tokens granting access to specific DICOM resources. -- ``/tokens/decode`` to extract the info from a token -- ``/user/get-profile`` to return the user profile linked to a given token. This profile - includes a list of permissions. +This sample also shows how to implement the 4 routes that the webservice might provide. .. _orthanc-explorer-authorization: