Mercurial > hg > orthanc-book
view Sphinx/source/plugins/authorization.rst @ 1123:292479b06402
security: authentication wrt RemoteAccess
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Thu, 19 Dec 2024 08:17:02 +0100 |
parents | d57ca05c6478 |
children |
line wrap: on
line source
.. _authorization: Advanced authorization plugin ============================= .. contents:: This **official plugin** extends Orthanc with an advanced authorization mechanism. For each incoming REST request to some URI, 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 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 ? --------------- The source code is available on `Mercurial <https://orthanc.uclouvain.be/hg/orthanc-authorization/>`__. Binaries are included in: - The `orthancteam/orthanc Docker image <https://hub.docker.com/r/orthancteam/orthanc>`__ - The `Windows Installer <https://www.orthanc-server.com/download-windows.php>`__ - The `MacOS packages <https://www.orthanc-server.com/static.php?page=download-mac>`__ Release notes are available `here <https://orthanc.uclouvain.be/hg/orthanc-authorization/file/tip/NEWS>`__. Compilation instructions are available below. Usage ----- .. highlight:: json Once Orthanc is installed, you must change the :ref:`configuration file <configuration>` to tell Orthanc where it can find the plugin: This is done by properly modifying the ``Plugins`` option. You could for instance use the following configuration file:: { "Name" : "MyOrthanc", [...] "Plugins" : [ "/home/user/OrthancAuthorization/Build/libOrthancAuthorization.so" ], "Authorization" : { // .. all options are document below } } Orthanc must of course be restarted after the modification of its configuration file. User based authorization vs resource based tokens ------------------------------------------------- The plugin can work in 2 modes that can be combined: * **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). 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. 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:: { "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`` 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 <model-world>`. This field can be set to ``patient``, ``study``, ``series``, or ``instance``. * The ``method`` field specifies which HTTP method is used by the to-be-authorized request. It can be set to ``get``, ``post``, ``delete``, or ``put``. * The ``dicom-uid`` field gives the :ref:`DICOM identifier <dicom-identifiers>` of the resource that is accessed. If the resource is a patient, this field contains the ``PatientID`` DICOM tag. For a study, it contains its ``StudyInstanceUID``. For a series, it contains its ``SeriesInstanceUID``. For an instance, it contains its ``SOPInstanceUID``. * The ``orthanc-id`` field gives the :ref:`Orthanc identifier <orthanc-ids>` of the resource. * The ``server-id`` field contains the value of the ``WebServiceIdentifier`` 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). It the user is accessing a URI that is not directly related to an individual DICOM resource, the JSON body will look as follows:: { "level" : "system", "method" : "get", "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: * 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. And your Web service must provide this kind of responses:: { "granted": true, "validity": 60 } Where: * ``granted`` tells whether access to the resource is granted (``true``) or not granted (``false``). In the case the user is accessing a DICOM resource, the access to *all* the levels of the hierarchy above this resource must be granted (logical conjunction 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. 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. **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", ... } { "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", ... } **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 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 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:: { "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 of ``GET`` arguments in the configuration options. In this case, the authorization plugin will loop over all the available authentication tokens, until it finds one for which the access is granted (logical disjunction over the authentication tokens). **Note 2:** The cache entry that remembers whether some access was granted in the past, depends on the value of the token. **Note 3:** The support of authentication tokens provided as ``GET`` arguments requires a version of Orthanc that is above 1.2.1. Full configuration ------------------ .. highlight:: json The full list of configuration is available `here <https://orthanc.uclouvain.be/hg/orthanc-authorization/file/tip/Plugin/DefaultConfiguration.json>`__. Here is the list of all the configuration options:: { "Authorization" : { // 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 4 routes ! // "WebServiceRootUrl" : "http://change-me:8000/", // The URL of the auth webservice route implementing user profile (optional) // "WebServiceUserProfileUrl" : "http://change-me:8000/user/profile", // The URL of the auth webservice route implementing resource level authorization (optional) // "WebServiceTokenValidationUrl" : "http://change-me:8000/tokens/validate", // The Base URL of the auth webservice route to create tokens (optional) // "WebServiceTokenCreationBaseUrl" : "http://change-me:8000/tokens/", // The URL of the auth webservice route implementing token decoding (optional) // "WebServiceTokenDecoderUrl": "http://change-me:8000/tokens/decode" // The username and password to connect to the webservice (optional) //"WebServiceUsername": "change-me", //"WebServicePassword": "change-me", // 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 //"TokenHttpHeaders" : [], // The name of the GET arguments that may contain auth tokens //"TokenGetArguments" : [], // A list of predefined configurations for well-known plugins // "StandardConfigurations": [ // new in v 0.4.0 // "osimis-web-viewer", // "stone-webviewer", // "orthanc-explorer-2", // "ohif" // ], //"UncheckedResources" : [], //"UncheckedFolders" : [], //"CheckedLevel" : "studies", //"UncheckedLevels" : [], // Definition of required "user-permissions". This can be fully customized. // You may define other permissions yourself as long as they match the permissions // provided in the user-profile route implemented by the auth-service. // You may test your regex in https://regex101.com/ by selecting .NET (C#) and removing the leading ^ and trailing $ // The default configuration is suitable for Orthanc-Explorer-2 (see https://github.com/orthanc-team/orthanc-auth-service) "Permissions" : [ ["post", "^/auth/tokens/decode$", ""], ["post", "^/tools/lookup$", ""], // elemental browsing in OE2 ["post", "^/tools/find$", "all|view"], ["get" , "^/(patients|studies|series|instances)/([a-f0-9-]+)$", "all|view"], ... ] } } The following options have been described above: ``WebServiceRootUrl``, ``TokenGetArguments``, and ``TokenHttpHeaders``. Here are the remaining options: * ``StandardConfigurations`` is a helper configuration to pre-populate ``UncheckedResources``, ``UncheckedFolders``, ``TokenGetArguments``, and ``TokenHttpHeaders`` of well-known plugins. Allowed values are ``osimis-web-viewer``, ``stone-webviewer``. * ``CheckedLevel`` may replace ``UncheckedLevels`` when authorization is checked only at one level of the DICOM hierarchy. This is the most common use-case. * ``UncheckedResources`` specifies a list of resources for which the authentication plugin is not triggered, and to which access is always granted. * ``UncheckedFolders`` specifies a list of root paths for which the authentication plugin is not triggered when receiving a GET request. This is actually mainly used to grant access to static resources e.g. HTML and JS resources from plugins like :ref:`Orthanc Explorer 2 <orthanc-explorer-2>`. * ``UncheckedLevels`` allows to specify which levels of the :ref:`DICOM hierarchy <model-world>` are ignored by the authorization plugin. This can be used to reduce the number of calls to the Web service. Think for instance about an authorization mechanism that 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>`:: { // disable basic authentication since it is replaced by the authorization plugin "AuthenticationEnabled": false, "Authorization" : { "WebServiceTokenValidationUrl" : "http://localhost:8000/shares/validate", "StandardConfigurations": [ "stone-webviewer" ], "CheckedLevel" : "studies" } } .. _orthanc-explorer-2-authorization: Integration with the Orthanc Explorer 2 --------------------------------------- This project contains a `complete example <https://github.com/orthanc-team/orthanc-auth-service>`__ 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 the 4 routes that the webservice might provide. .. _orthanc-explorer-authorization: Integration with the Orthanc Explorer ------------------------------------- Starting from Orthanc 1.5.8, you can pass authorization tokens in the url search params when opening the Orthanc explorer, i.e. ``http://localhost:8042/app/explorer.html?token=1234``. This token will be included as an HTTP header in every request sent to the Orthanc Rest API. It will also be included in the URL search params when opening the Orthanc or :ref:`Osimis Web viewer <osimis_webviewer>`. Only 3 tokens name will be recognized and forwarded: ``token``, ``auth-token`` and ``authorization``. Please note that the Orthanc Explorer has not been designed to handle the authorization so, when an authorization is not granted, it will simply display an empty page or an error message. Compilation ----------- .. highlight:: bash The procedure to compile this plugin is similar of that for the :ref:`core of Orthanc <binaries>`. The following commands should work for most UNIX-like distribution (including GNU/Linux):: $ mkdir Build $ cd Build $ cmake .. -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release $ make The compilation will produce a shared library ``OrthancAuthorization`` that contains the authorization plugin.