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 between 2020 and 2022, but its active development has been resumed since May 2022 and is intensively used in the orthanc-auth-service project that provides user permissions and sharing of single studies.
How to get it ?¶
The source code is available on Mercurial.
Binaries are included in:
Release notes are available here.
Compilation instructions are available below.
Usage¶
Once Orthanc is installed, you must change the configuration file 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 Orthanc Explorer 2 to allow various
actions
based onpermissions
defined in auser 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
of a simple Web service that implements only the validate
route.
The orthanc-auth-service project provides a full implementation of the Web service. It notably contains a definition of all the requests and responses 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, 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 DICOM model of the real world. This field can be set topatient
,study
,series
, orinstance
.The
method
field specifies which HTTP method is used by the to-be-authorized request. It can be set toget
,post
,delete
, orput
.The
dicom-uid
field gives the DICOM identifier of the resource that is accessed. If the resource is a patient, this field contains thePatientID
DICOM tag. For a study, it contains itsStudyInstanceUID
. For a series, it contains itsSeriesInstanceUID
. For an instance, it contains itsSOPInstanceUID
.The
orthanc-id
field gives the Orthanc identifier of the resource.The
server-id
field contains the value of theWebServiceIdentifier
configuration ornull
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 tosystem
.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 to0
second, the cache entry will never expire. By setting avalidity
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 REST API of Orthanc 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¶
The full list of configuration is available here.
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-populateUncheckedResources
,UncheckedFolders
,TokenGetArguments
, andTokenHttpHeaders
of well-known plugins. Allowed values areosimis-web-viewer
,stone-webviewer
.CheckedLevel
may replaceUncheckedLevels
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 Orthanc Explorer 2.UncheckedLevels
allows to specify which levels of the DICOM hierarchy 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 inserver-id
in the requests to the web services (new in v 0.3.0).
Here is a minimal configuration for the Stone Web viewer:
{
// 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"
}
}
Integration with the Orthanc Explorer 2¶
This project contains a complete example of a Web services integrating with 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.
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 Osimis Web viewer.
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¶
The procedure to compile this plugin is similar of that for the core of Orthanc. 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.