init
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Deployment",
|
||||
"position": 3
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
sidebar_label: Authorization
|
||||
---
|
||||
|
||||
# Authorization
|
||||
The OHIF Viewer can be configured to work with authorization servers that support one or more of the OpenID-Connect authorization flows. The Viewer finds it's OpenID-Connect settings on the oidc configuration key. You can set these values in your configuration files. For instance you can take a look at our
|
||||
`google.js` configuration file.
|
||||
|
||||
|
||||
```js
|
||||
oidc: [
|
||||
{
|
||||
// ~ REQUIRED
|
||||
// Authorization Server URL
|
||||
authority: 'https://accounts.google.com',
|
||||
client_id:
|
||||
'723928408739-k9k9r3i44j32rhu69vlnibipmmk9i57p.apps.googleusercontent.com',
|
||||
redirect_uri: '/callback',
|
||||
response_type: 'id_token token',
|
||||
scope:
|
||||
'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid
|
||||
// ~ OPTIONAL
|
||||
post_logout_redirect_uri: '/logout-redirect.html',
|
||||
revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=',
|
||||
automaticSilentRenew: true,
|
||||
revokeAccessTokenOnSignout: true,
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
You need to provide the following information:
|
||||
- authority: The URL of the authorization server.
|
||||
- client_id: The client id of your application (provided by the authorization server).
|
||||
- redirect_uri: The callback URL of your application.
|
||||
- response_type: The response type of the authorization flow (e.g. id_token token, [learn more about different flows](https://darutk.medium.com/diagrams-of-all-the-openid-connect-flows-6968e3990660)).
|
||||
- scope: The scopes that your application needs to access
|
||||
- post_logout_redirect_uri: The URL that the user will be redirected to after logout.
|
||||
- revoke_uri: The URL that the user will be redirected to after logout.
|
||||
- automaticSilentRenew: If true, the user will be automatically logged in after the token expires.
|
||||
- revokeAccessTokenOnSignout: If true, the access token will be revoked on logout.
|
||||
|
||||
|
||||
|
||||
## How it works
|
||||
The Viewer uses the `userAuthenticationService` to set the OpenID-Connect settings. The `userAuthenticationService` is a singleton service that is responsible for authentication and authorization. It is initialized by the app and you can grab it
|
||||
from the `servicesManager`
|
||||
|
||||
```js
|
||||
const userAuthenticationService = servicesManager.services.userAuthenticationService;
|
||||
```
|
||||
|
||||
Then the userAuthenticationService will inject the token as Authorization header in the requests that are sent to the server (both metadata
|
||||
and pixelData).
|
||||
|
||||
|
||||
## Token based authentication
|
||||
Sometimes (although not recommended), some servers like to send the token
|
||||
in the query string. In this case, the viewer will automatically grab the token from the query string
|
||||
and add it to the userAuthenticationService and remove it from the query string (to prevent it from being logged in the console
|
||||
in future requests).
|
||||
|
||||
and example would be
|
||||
|
||||
```js
|
||||
http://localhost:3000/viewer?StudyInstanceUIDs=1.2.3.4.5.6.6.7&token=e123125jsdfahsdf
|
||||
```
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Build for Production
|
||||
|
||||
### Build Machine Requirements
|
||||
|
||||
- [Node.js & NPM](https://nodejs.org/en/download/)
|
||||
- [Yarn](https://yarnpkg.com/lang/en/docs/install/)
|
||||
- [Git](https://www.atlassian.com/git/tutorials/install-git)
|
||||
|
||||
### Getting the Code
|
||||
|
||||
_With Git:_
|
||||
|
||||
```bash
|
||||
# Clone the remote repository to your local machine
|
||||
git clone https://github.com/OHIF/Viewers.git
|
||||
```
|
||||
|
||||
More on: _[`git clone`](https://git-scm.com/docs/git-clone),
|
||||
[`git checkout`](https://git-scm.com/docs/git-checkout)_
|
||||
|
||||
_From .zip:_
|
||||
|
||||
[OHIF/Viewers: master.zip](https://github.com/OHIF/Viewers/archive/master.zip)
|
||||
|
||||
### Restore Dependencies & Build
|
||||
|
||||
Open your terminal, and navigate to the directory containing the source files.
|
||||
Next run these commands:
|
||||
|
||||
```bash
|
||||
# If you haven't already, enable yarn workspaces
|
||||
yarn config set workspaces-experimental true
|
||||
|
||||
# Restore dependencies
|
||||
yarn install
|
||||
|
||||
# Build source code for production
|
||||
yarn run build
|
||||
```
|
||||
|
||||
If everything worked as expected, you should have a new `dist/` directory in the
|
||||
`platform/app/dist` folder. It should roughly resemble the following:
|
||||
|
||||
```bash title="<root>platform/app/dist/"
|
||||
├── app-config.js
|
||||
├── app.bundle.js
|
||||
├── app.css
|
||||
├── index.html
|
||||
├── manifest.json
|
||||
├── service-worker.js
|
||||
└── ...
|
||||
```
|
||||
|
||||
By default, the build output will connect to OHIF's publicly accessible PACS. If
|
||||
this is your first time setting up the OHIF Viewer, it is recommended that you
|
||||
test with these default settings. After testing, you can find instructions on
|
||||
how to configure the project for your own imaging archive below.
|
||||
|
||||
### Configuration
|
||||
|
||||
The configuration for our viewer is in the `<root>platform/app/public/config`
|
||||
directory. Our build process knows which configuration file to use based on the
|
||||
`APP_CONFIG` environment variable. By default, its value is
|
||||
[`config/default.js`][default-config]. The majority of the viewer's features,
|
||||
and registered extension's features, are configured using this file.
|
||||
|
||||
The easiest way to apply your own configuration is to modify the `default.js`
|
||||
file. For more advanced configuration options, check out our
|
||||
[configuration essentials guide](../configuration/configurationFiles.md).
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Deploying Build Output
|
||||
|
||||
_Drag-n-drop_
|
||||
|
||||
- [Netlify: Drop](./static-assets#netlify-drop)
|
||||
|
||||
_Easy_
|
||||
|
||||
- [Surge.sh](./static-assets#surgesh)
|
||||
- [GitHub Pages](./static-assets#github-pages)
|
||||
|
||||
_Advanced_
|
||||
|
||||
- [AWS S3 + Cloudfront](./static-assets#aws-s3--cloudfront)
|
||||
- [GCP + Cloudflare](./static-assets#gcp--cloudflare)
|
||||
- [Azure](./static-assets#azure)
|
||||
|
||||
### Testing Build Output Locally
|
||||
|
||||
A quick way to test your build output locally is to spin up a small webserver.
|
||||
You can do this by running the following commands in the `dist/` output
|
||||
directory:
|
||||
|
||||
```bash
|
||||
# Install http-server as a globally available package
|
||||
yarn global add http-server
|
||||
|
||||
# Change the directory to the platform/app
|
||||
|
||||
# Serve the files in our current directory
|
||||
npx serve ./dist -c ../public/serve.json
|
||||
```
|
||||
|
||||
:::caution
|
||||
In the video below notice that there is `platform/viewer` which has been renamed to `platform/app` in the latest version
|
||||
:::
|
||||
|
||||
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
|
||||
<iframe src="https://player.vimeo.com/video/551957266?badge=0&autopause=0&player_id=0&app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="measurement-report"></iframe>
|
||||
</div>
|
||||
|
||||
|
||||
### Automating Builds and Deployments
|
||||
|
||||
If you found setting up your environment and running all of these steps to be a
|
||||
bit tedious, then you are in good company. Thankfully, there are a large number
|
||||
of tools available to assist with automating tasks like building and deploying
|
||||
web application. For a starting point, check out this repository's own use of:
|
||||
|
||||
- [CircleCI][circleci]: [config.yaml][circleci-config]
|
||||
- [Netlify][netlify]: [netlify.toml][netlify.toml] |
|
||||
[build-deploy-preview.sh][build-deploy-preview.sh]
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[circleci]: https://circleci.com/gh/OHIF/Viewers
|
||||
[circleci-config]: https://github.com/OHIF/Viewers/blob/master/.circleci/config.yml
|
||||
[netlify]: https://app.netlify.com/sites/ohif/deploys
|
||||
[netlify.toml]: https://github.com/OHIF/Viewers/blob/master/platform/app/netlify.toml
|
||||
[build-deploy-preview.sh]: https://github.com/OHIF/Viewers/blob/master/.netlify/build-deploy-preview.sh
|
||||
<!-- prettier-ignore-end -->
|
||||
313
platform/docs/versioned_docs/version-3.8/deployment/cors.md
Normal file
313
platform/docs/versioned_docs/version-3.8/deployment/cors.md
Normal file
@@ -0,0 +1,313 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
# Cross-Origin Information for OHIF
|
||||
|
||||
This document describes various security configurations, settings and environments/contexts needed to fully leverage OHIF’s capabilities. One may need some configurations while others might need ALL of them - it all depends on the environment OHIF is expected to run in.
|
||||
|
||||
In particular, three of OHIF’s features depend on these configurations:
|
||||
- [OHIF’s use of SharedArrayBuffer](#sharedarraybuffer)
|
||||
- [Embedding OHIF in an iframe](#embedding-ohif-in-an-iframe)
|
||||
- [XMLHttpRequests to fetch data from data sources](#cors-in-ohif)
|
||||
|
||||
|
||||
|
||||
## SharedArrayBuffer
|
||||
A `SharedArrayBuffer` is a JavaScript object that is similar to an `ArrayBuffer` but can be shared between web workers and the window that spawned them via the `postMessage` API. See [SharedArrayBuffer in MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) for more information.
|
||||
|
||||
:::tip
|
||||
To turn off Shared Array Buffer completely, just set `useSharedArrayBuffer` to `false` in the [OHIF configuration](../configuration/configurationFiles.md). But keep in mind that you will not get the performance boost that Shared Array Buffer offers for decoding and rendering big volumes where web workers write to the same memory space.
|
||||
:::
|
||||
|
||||
### Security Requirements
|
||||
|
||||
In order to use `SharedArrayBuffer` objects in the browser, the following security conditions must be met:
|
||||
|
||||
- The page must be served in a [secure context](#secure-context).
|
||||
- The page must have [cross-origin isolation](#cross-origin-isolation) enabled.
|
||||
|
||||
### `SharedArrayBuffer` in OHIF
|
||||
|
||||
OHIF uses `SharedArrayBuffer` in its volume loader (from Cornerstone3D). It comes with the benefit of improved performance and optimization at the cost of some configuration to use it.
|
||||
|
||||
As such, if the following popup is shown when launching OHIF then the OHIF server will have to be configured to permit the loading of volumetric images and data. Note that stack viewports are still available and functional even when this error is present.
|
||||
|
||||

|
||||
|
||||
To better determine which (if not all) of the [security requirements](#security-requirements) are lacking, have a look at the browser console.
|
||||
|
||||
Output in the console similar to the following indicates that OHIF is not running in a [secure context](#secure-context).
|
||||
|
||||

|
||||
|
||||
Absence of the above error in the console together with the presence of the Cross Origin Isolation popup warning, likely indicates that either or both of the [COOP](#coop---cross-origin-opener-policy) and/or [COEP](#coep---cross-origin-embedder-policy) headers are not set for OHIF.
|
||||
|
||||
## Embedding OHIF in an iframe
|
||||
|
||||
As described [here](./iframe.md), there are cases where OHIF will be embedded in an iframe. The following links provide more information for setting up and configuring OHIF to work in an iframe:
|
||||
|
||||
- [OHIF iframe documentation](./iframe.md#static-build)
|
||||
- [OHIF as a Cross-origin Resource in an iframe](#ohif-as-a-cross-origin-resource-in-an-iframe)
|
||||
|
||||
## Secure Context
|
||||
|
||||
MDN defines a secure context as [“a Window or Worker for which certain minimum standards of authentication and confidentiality are met.“](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)
|
||||
|
||||
Any local URL is considered secure. The following are some examples of local URLs that are considered secure…
|
||||
- http://localhost
|
||||
- http://127.0.0.1:3000
|
||||
|
||||
URLs that are NOT local must be delivered over `https://` or `wss://` (i.e. TLS) to be considered secure. See [When is a context considered secure](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure) in MDN for more information.
|
||||
|
||||
### iframes
|
||||
|
||||
A page embedded in an iframe is considered secure if it itself and every one of its embedding ancestors are delivered securely. Otherwise it is deemed insecure.
|
||||
|
||||
### Why does OHIF require a secure context?
|
||||
|
||||
Beyond all of the inherent benefits of a secure connection, OHIF requires a secure context so that it can utilize [SharedArrayBuffer](#sharedarraybuffer) objects for volume rendering.
|
||||
|
||||
### Configuring/setting up a secure context
|
||||
|
||||
[Local URLs are considered secure](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure), and as such whenever OHIF is accessed via a local URL (e.g. http://localhost:3000) it is running in a secure context. For example, in a development environment using the default webpack setup, OHIF can be deployed and accessed in a secure context at http://localhost:3000.
|
||||
|
||||
The best alternative is to host OHIF over HTTPS.
|
||||
|
||||
:::tip
|
||||
OHIF can be served over HTTPS in a variety of ways (these are just some examples).
|
||||
- Website hosting services that offer HTTPS deployment (e.g,. Netlify) or offer HTTPS load balancers (AWS, Google Cloud etc.)
|
||||
- Setting up a reverse proxy (e.g. `nginx`) with a self-signed certificate that forwards requests to the OHIF server
|
||||
- [An OHIF Docker image can be set up this way](./docker.md#ssl).
|
||||
:::
|
||||
|
||||
## Origin Definition
|
||||
|
||||
According to [MDN](https://developer.mozilla.org/en-US/docs/Glossary/Origin), a Web content’s origin is defined by the scheme (protocol), hostname (domain), and port of the URL used to access it. Two objects have the same origin only when the scheme, hostname, and port all match.
|
||||
|
||||
## CORS - Cross-Origin Resource Sharing
|
||||
|
||||
A cross-origin resource is a resource (e.g. image, JSON, etc) that is served by one origin and used/referenced by a different origin.
|
||||
|
||||
CORS is the protocol utilized by web servers and browsers whereby a server of one origin identifies and/or restricts which of its resources that other origins (i.e. other than its own) a browser should allow access to. By default a browser does not permit cross-origin resource sharing.
|
||||
|
||||
The CORS mechanism relies on the HTTP response headers from the server to indicate if a resource can be shared with a different origin.
|
||||
|
||||
See the [MDN CORS article](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) for more information.
|
||||
|
||||
### CORS HTTP Headers
|
||||
|
||||
The header that mostly concerns OHIF is listed below and should be configured accordingly on the DICOMweb server or any data source that OHIF would make XMLHttpRequests to for its data.
|
||||
|
||||
```http
|
||||
Access-Control-Allow-Origin: `<origin>` | *
|
||||
```
|
||||
|
||||
:::tip
|
||||
The `Access-Control-Allow-Origin` header specifies which origins can access the served resource embedded in the response.
|
||||
|
||||
Either a single, specific origin (i.e. `<origin>`) can be specified or ALL origins (i.e. *)
|
||||
|
||||
See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-allow-origin) for more information.
|
||||
:::
|
||||
|
||||
### CORS in OHIF
|
||||
|
||||
OHIF fetches and displays data and images from data sources. It invokes XMLHttpRequests to some data sources such as DICOMweb data sources to fetch the information to render. Typically, a DICOMweb server is hosted on a completely different origin than the one serving OHIF. As such, those XMLHttpRequests use CORS.
|
||||
|
||||
### Troubleshooting CORS in OHIF
|
||||
|
||||
The following is an example screenshot of the browser console when one of OHIF’s DICOMweb data source servers is not configured for CORS.
|
||||
|
||||

|
||||
|
||||
And the following is what is in the accompanying network tab.
|
||||
|
||||

|
||||
|
||||
:::info
|
||||
Setting the appropriate CORS header varies per server or service that is hosting the data source. What follows below is just one example to remedy the problem.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If Orthanc is the data source running in a Docker container composed with/behind nginx. And OHIF is being served at localhost:3000. The issue can be remedied by adding either of the following to Orthanc’s Docker container nginx.conf file.
|
||||
|
||||
```nginx
|
||||
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```nginx
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
```
|
||||
:::
|
||||
|
||||
## COOP - Cross-Origin Opener Policy
|
||||
|
||||
The COOP HTTP response header restricts the global, root document of the page from being referenced and accessed by another cross-origin document that might open the page in a window. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) for more information.
|
||||
|
||||
### Header Values Pertinent to OHIF (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy#syntax) for more information)
|
||||
|
||||
|Value|Description|
|
||||
|-----|-----------|
|
||||
|same-origin|Restricts the document to be referenced by openers of the same origin only.|
|
||||
|
||||
### COOP in OHIF
|
||||
|
||||
COOP is required for [SharedArrayBuffer](#sharedarraybuffer) usage in OHIF. See also [Troubleshooting Cross-origin Isolation in OHIF](#troubleshooting-cross-origin-isolation-in-ohif).
|
||||
|
||||
## COEP - Cross-Origin Embedder Policy
|
||||
|
||||
The COEP HTTP response header restricts cross-origin documents from being embedded into a document (e.g. in an iframe, video, image, etc). See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) for more information.
|
||||
|
||||
### Header Values Pertinent to OHIF (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy#syntax) for more information)
|
||||
|
||||
|Value|Description|
|
||||
|-----|-----------|
|
||||
|require-corp|Permits the document to load either of the following embedded resources: <ul><li>Those from the same origin</li><li>Cross-origin resources embedded by a DOM element that has the appropriate [crossorigin attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) set</li><li>Cross-origin resources with the appropriate [CORP response header](#corp---cross-origin-resource-policy)</li></ul>
|
||||
|credentialless|See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy#syntax) for more information|
|
||||
|
||||
### COEP in OHIF
|
||||
|
||||
COEP is required for [SharedArrayBuffer](#sharedarraybuffer) usage in OHIF. See also [Troubleshooting Cross-origin Isolation in OHIF](#troubleshooting-cross-origin-isolation-in-ohif).
|
||||
|
||||
## Cross-origin Isolation
|
||||
|
||||
Cross-origin isolation is [enabled](https://web.dev/cross-origin-isolation-guide/#enable-cross-origin-isolation) for a web page when the following COOP and COEP headers are set.
|
||||
- [COOP](#coop---cross-origin-opener-policy) with `same-origin`
|
||||
- [COEP](#coep---cross-origin-embedder-policy) with `require-corp` or `credentialless`
|
||||
|
||||
### iframe
|
||||
|
||||
An iframe is considered to have cross-origin isolation enabled if it itself has the appropriate COOP and COEP headers set as well as every one of its embedding ancestors.
|
||||
|
||||
### Troubleshooting Cross-origin Isolation in OHIF
|
||||
|
||||
The [SharedArrayBuffer in OHIF](#sharedarraybuffer-in-ohif) section describes how to determine if there are problems with cross-origin isolation in OHIF. If it is determined that COOP and/or COEP is indeed an issue, then the COOP and COEP headers must be set for OHIF. How to accomplish this varies per server or service that is hosting OHIF. The following are just a few examples.
|
||||
|
||||
:::tip
|
||||
In the default dev environment, the following can be set in the webpack.pwa.js file…
|
||||
|
||||
```javascript
|
||||
devServer: {
|
||||
headers: {
|
||||
"Cross-Origin-Opener-Policy": "same-origin",
|
||||
"Cross-Origin-Embedder-Policy": "require-corp"
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If deploying OHIF using Netlify, the Netlify configuration [file](https://docs.netlify.com/configure-builds/file-based-configuration/) can be used to configure the headers as such…
|
||||
|
||||
```
|
||||
[[headers]]
|
||||
# Define which paths this specific [[headers]] block will cover.
|
||||
for = "/*"
|
||||
|
||||
[headers.values]
|
||||
Cross-Origin-Opener-Policy = "same-origin"
|
||||
Cross-Origin-Embedder-Policy = "require-corp"
|
||||
```
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If OHIF is served behind nginx, then the headers can be set in the nginx.conf file as follows. The [template nginx configuration file](https://github.com/OHIF/Viewers/blob/master/.docker/Viewer-v3.x/default.conf.template) for creating a [OHIF Docker image](./docker.md#building-the-docker-image) has an example of this too.
|
||||
```nginx
|
||||
server {
|
||||
location / {
|
||||
add_header Cross-Origin-Opener-Policy same-origin;
|
||||
add_header Cross-Origin-Embedder-Policy require-corp;
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
## CORP - Cross-Origin Resource Policy
|
||||
|
||||
The CORP HTTP response header indicates which origins can read and use a resource. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy) for more information.
|
||||
|
||||
### Header Values (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy#usage) for more information)
|
||||
|
||||
|Value|Description|
|
||||
|-----|-----------|
|
||||
|same-site|Only requests from the same site can read the resource.|
|
||||
|same-origin|Only requests from the same origin can read the resource.|
|
||||
|cross-origin|Requests from any origin can read the resource. The value is useful and [exists](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy#relationship_to_cross-origin_embedder_policy_coep) primarily for letting documents with the [COEP require-corp value](#header-values-pertinent-to-ohif-see-mdn-for-more-information-1) know that the resource is ok to be embedded|
|
||||
|
||||
### OHIF and CORP
|
||||
|
||||
There are two scenarios where the CORP header is relevant to OHIF:
|
||||
|
||||
- [PDF from a Cross Origin DICOMweb Data Source](#pdf-from-a-cross-origin-dicomweb-data-source)
|
||||
- [OHIF as a Cross-origin Resource in an iframe](#ohif-as-a-cross-origin-resource-in-an-iframe)
|
||||
|
||||
Both these scenarios stem from the fact that OHIF has to be served with the [COEP](#coep---cross-origin-embedder-policy) header to support [SharedArrayBuffer](#sharedarraybuffer).
|
||||
|
||||
#### PDF from a Cross Origin DICOMweb Data Source
|
||||
|
||||
There are some DICOMweb data sources (e.g. dcm4chee) whereby OHIF uses the data source’s `/rendered` endpoint to embed a DICOM PDF document in the OHIF DOM using an `<object>` tag.
|
||||
|
||||
As specified for the [COEP require-corp value](#header-values-pertinent-to-ohif-see-mdn-for-more-information-1), a page like OHIF with COEP header `require-corp` can embed cross-origin resources in DOM elements that have the [`crossorigin` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) OR the resource is delivered with an appropriate CORP header. The `<object>` tag does NOT support the `crossorigin` attribute. As such, the PDF must be delivered with a CORP header.
|
||||
|
||||
:::tip
|
||||
Setting the CORP header varies per server or service that is hosting the data source. The following is just one example.
|
||||
|
||||
For a dcm4chee DICOMweb data source composed in Docker behind nginx, the CORP header can be configured in the nginx.conf file as such:
|
||||
|
||||
```nginx
|
||||
add_header 'Cross-Origin-Resource-Policy' 'cross-origin' always;
|
||||
```
|
||||
|
||||
If the dcm4chee server and the OHIF server are hosted on the same site, then the following would also work:
|
||||
```nginx
|
||||
add_header 'Cross-Origin-Resource-Policy' 'same-site' always;
|
||||
```
|
||||
:::
|
||||
|
||||
#### OHIF as a Cross-origin Resource in an iframe
|
||||
|
||||
There are cases where [OHIF is embedded in an iframe](./iframe.md) and the embedding page is from a different origin. Again due to the [security requirements for SharedArrayBuffer](#security-requirements), [both OHIF and the embedding page](#iframe) must have the appropriate COEP header. In this scenario, OHIF is the cross-origin resource and since the `<iframe>` tag does not support the `crossorigin` attribute, OHIF must be served with the appropriate CORP header.
|
||||
|
||||
:::info
|
||||
Setting the CORP header such that OHIF can be embedded in an iframe varies per server or service hosting OHIF. What follows are just a few examples. Note that whenever the embedding page is hosted on the same site as OHIF, consider using the `same-site` value instead of `cross-origin`.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
In the default dev environment, the following can be set in the webpack.pwa.js file…
|
||||
|
||||
```javascript
|
||||
devServer: {
|
||||
headers: {
|
||||
"Cross-Origin-Resource-Policy": "cross-origin",
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If deploying OHIF using Netlify, the Netlify configuration [file](https://docs.netlify.com/configure-builds/file-based-configuration/) can be used to configure the header as such…
|
||||
|
||||
```
|
||||
[[headers]]
|
||||
# Define which paths this specific [[headers]] block will cover.
|
||||
for = "/*"
|
||||
|
||||
[headers.values]
|
||||
Cross-Origin-Resource-Policy = "cross-origin"
|
||||
```
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If OHIF is served behind nginx, then the header can be set in the nginx.conf file as follows.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
location / {
|
||||
add_header Cross-Origin-Resource-Policy cross-origin;
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
title: Custom URL Access/Build
|
||||
---
|
||||
|
||||
|
||||
## Build for non-root path
|
||||
|
||||
Sometimes it is desired to access the viewer from a non-root path (e.g., `/my-awesome-viewer` instead of `/`).
|
||||
You can achieve so by using the `PUBLIC_URL` environment variable AND the `routerBasename` configuration option.
|
||||
|
||||
1. use a config (e.g. `config/myConfig.js`) file that is using the `routerBasename` of your choice `/my-awesome-viewer` (note there is only one / - it is not /my-awesome-viewer/).
|
||||
2. build the viewer with `PUBLIC_URL=/my-awesome-viewer/` (note there are two / - it is not /my-awesome-viewer).
|
||||
|
||||
|
||||
:::tip
|
||||
The PUBLIC_URL tells the application where to find the static assets and the routerBasename will tell the application how to handle the routes
|
||||
:::
|
||||
|
||||
|
||||
### Testing in Development
|
||||
For testing the build locally, you can use the following command:
|
||||
|
||||
```bash
|
||||
# we use default config file, so we assume you have already set the routerBasename to /my-awesome-viewer in the default config as an example
|
||||
PUBLIC_URL=/my-awesome-viewer/ APP_CONFIG=config/default.js yarn dev
|
||||
```
|
||||
|
||||
|
||||
### Testing in Build (production)
|
||||
You need to build the viewer with the following command:
|
||||
|
||||
```bash
|
||||
PUBLIC_URL=/my-awesome-viewer/ APP_CONFIG=config/default.js yarn build
|
||||
```
|
||||
|
||||
We can use the `npx serve` to serve the build folder. There are two things you need to consider however,
|
||||
1. You need to change the public/serve.json file to reflect the new routerBasename in the destination (see the example below)
|
||||
|
||||
|
||||
```json
|
||||
// final serve.json
|
||||
{
|
||||
"rewrites": [{ "source": "*", "destination": "my-awesome-viewer/index.html" }],
|
||||
"headers": [
|
||||
{
|
||||
"source": "**/*",
|
||||
"headers": [
|
||||
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
|
||||
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
cd platform/app;
|
||||
|
||||
# rename the dist folder to my-awesome-viewer
|
||||
mv dist my-awesome-viewer
|
||||
|
||||
# serve the folder with custom json, note that we are using ../public/serve.json and NOT public/serve.json
|
||||
npx serve -c ./public/serve.json
|
||||
```
|
||||
|
||||
|
||||
:::note
|
||||
When you want to authenticate against a sub path, there are a few things you should keep in mind:
|
||||
|
||||
1. Set the `routerBasename` to the sub path and also update the `PUBLIC_URL` to match the sub path.
|
||||
2. Don't forget to modify the `serve.json` file as mentioned earlier.
|
||||
3. Ensure that the sub path is included in the list of allowed callback URLs. For example, in the Google Cloud dashboard, you can set it in the `Authorized redirect URIs` field under the `Credentials` section of the `APIs & Services` menu.
|
||||
:::
|
||||
251
platform/docs/versioned_docs/version-3.8/deployment/docker.md
Normal file
251
platform/docs/versioned_docs/version-3.8/deployment/docker.md
Normal file
@@ -0,0 +1,251 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Docker
|
||||
|
||||
The OHIF source code provides a [Dockerfile](https://github.com/OHIF/Viewers/blob/master/Dockerfile) to create and run a Docker image that containerizes an [nginx](https://www.nginx.com/) web server serving the OHIF Viewer.
|
||||
|
||||
:::info
|
||||
This Dockerfile is the same used to generate the [OHIF image(s) on Docker Hub](https://hub.docker.com/r/ohif/app/tags).
|
||||
:::
|
||||
|
||||
|
||||
## Prerequisites
|
||||
The machine on which to build and run the Docker container must have:
|
||||
1. All of the [requirements](./build-for-production.md#build-for-production) for building a production version of OHIF.
|
||||
2. A checked out branch of the OHIF Viewer.
|
||||
3. [Docker](https://docs.docker.com/get-docker/) installed.
|
||||
|
||||
## Building the Docker Image
|
||||
The docker image can be built from a terminal window as such:
|
||||
1. Switch to the OHIF Viewer code root directory.
|
||||
2. Issue the following Docker command. Note that what follows `-t` flag is the `{name}:{tag}` for the Docker image and is arbitrary when creating a local Docker image.
|
||||
|
||||
```sh
|
||||
docker build . -t ohif-viewer-image
|
||||
```
|
||||
|
||||
:::tip
|
||||
Building a Docker image comes in handy when OHIF has been customized (e.g. with custom extensions, modes, hanging protocols, etc.). For convenience, there are basic OHIF images built in Docker Hub. Find the latest [release](https://hub.docker.com/r/ohif/app/tags?page=1&name=latest) and [dev](https://hub.docker.com/r/ohif/app/tags?page=1&name=beta) images all in Docker Hub.
|
||||
:::
|
||||
|
||||
## Running the Docker Container
|
||||
Once the Docker image has been built, it can be run as a container from the command line as in the block below. Note that the last argument to the command is the name of the Docker image and the table below describes the other arguments.
|
||||
|
||||
|Flag|Description|
|
||||
|----|-----------|
|
||||
|-d|Run the container in the background and print the container ID|
|
||||
|-p `{host-port}:{nginx-port}/tcp`|Publish the `nginx` listen port on the given host port|
|
||||
|--name|An arbitrary name for the container.|
|
||||
|
||||
|
||||
```sh
|
||||
docker run -d -p 3000:80/tcp --name ohif-viewer-container ohif-viewer-image
|
||||
```
|
||||
|
||||
:::tip
|
||||
Any of the [Docker Hub images](https://hub.docker.com/r/ohif/app/tags) can be easily run as a Docker container.
|
||||
|
||||
The following is the command to run the Docker container using the latest released OHIF Docker Hub image.
|
||||
|
||||
```sh
|
||||
docker run -d -p 3000:80/tcp --name LatestReleasedOHIF ohif/app:latest
|
||||
```
|
||||
|
||||
Simply replace `latest` at the end of the command with any of the tags for a specific version.
|
||||
:::
|
||||
|
||||
### Configuring the `nginx` Listen Port
|
||||
|
||||
The Dockerfile and entry point use the `${PORT}` environment variable as the port that the `nginx` server uses to serve the web server. The default value for `${PORT}` is `80`. One way to set this environment variable is to use the `-e` switch when running the container with `docker run`. The block below gives an example where the listen port is set to `8080` and published on the host as `3000`.
|
||||
|
||||
```sh
|
||||
docker run -d -e PORT=8080 -p 3000:8080/tcp --name ohif-viewer-container ohif-viewer-image
|
||||
```
|
||||
|
||||
### Specifying the OHIF config File
|
||||
|
||||
There are two approaches for specifying the OHIF configuration file for a Docker container:
|
||||
|
||||
- [Volume Mounting](#volume-mounting)
|
||||
- [Environment Variable](#environment-variable)
|
||||
|
||||
#### Volume Mounting
|
||||
|
||||
The OHIF [config file](../configuration/configurationFiles.md) can be specified by mounting it as a volume for the Docker container using the `-v` flag. If the OHIF config file is on the local file system then it can be specified as below.
|
||||
|
||||
```sh
|
||||
docker run -d -p 3000:80/tcp -v /path/to/config/file.js:/usr/share/nginx/html/app-config.js --name ohif-viewer-container ohif-viewer-image
|
||||
```
|
||||
:::tip
|
||||
Depending on the version of Docker, an absolute path to the local source config file might be required.
|
||||
:::
|
||||
#### Environment Variable
|
||||
|
||||
In certain scenarios, such as deploying the Docker container to Google Cloud, it might be convenient to specify the configuration file (contents) as an environment variable. That environment variable is `${APP_CONFIG}` and it can be set in the `docker run` command using the `-e` switch.
|
||||
|
||||
:::tip
|
||||
It is important to stress here that the environment variable is the contents of the configuration file and NOT the path to the config file as is [typically specified](https://docs.ohif.org/configuration/configurationFiles#configuration-files) for development and build environments or for the [volume mounting method](#volume-mounting).
|
||||
:::
|
||||
|
||||
Below the `cat` command is used to convert the configuration file to a string and its result set as the `${APP_CONFIG}` environment variable.
|
||||
|
||||
```sh
|
||||
docker run -d -p 3000:80/tcp -e APP_CONFIG="$(cat /path/to/the/config/file)" --name ohif-viewer-container ohif-viewer-image
|
||||
```
|
||||
|
||||
:::tip
|
||||
To be safe, remove single line comments (i.e. `//`) from the configuration file because the presence of these comments might cause the configuration file to be prematurely truncated when it is served to the OHIF client.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
As an alternative to the `cat` command, convert the configuration file to a single line and copy and paste it as the value to the `${APP_CONFIG}` environment variable on the `docker run` line. Editors such as [Visual Studio Code](https://stackoverflow.com/questions/46491061/shortcut-for-joining-two-lines) and [Notepad++](https://superuser.com/questions/518229/how-do-i-remove-linebreaks-in-notepad) have 'Join Lines' commands to facilitate this.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If both the [volume mounting method](#volume-mounting) and the [environment variable method](#environment-variable) are used, the volume mounting method will take precedence.
|
||||
:::
|
||||
|
||||
### Embedding in an iframe
|
||||
|
||||
If the OHIF instance served by the Docker image is to be embedded in an `iframe`, and if [cross-origin isolation](./cors.md#cross-origin-isolation) is required, then the [Cross Origin Resource Policy (CORP) header value](https://github.com/OHIF/Viewers/blob/8a8ae237d26faf123abeb073cbf0cd426c3e9ef2/.docker/Viewer-v3.x/default.conf.template#L10) that OHIF is served with will have to be updated accordingly. More information on CORP and `iframe`s can be found [here](./cors.md#ohif-as-a-cross-origin-resource-in-an-iframe).
|
||||
|
||||
:::tip
|
||||
For SSL Docker deployments, the CORP header value is set [here](https://github.com/OHIF/Viewers/blob/8a8ae237d26faf123abeb073cbf0cd426c3e9ef2/.docker/Viewer-v3.x/default.ssl.conf.template#L12).
|
||||
:::
|
||||
|
||||
## SSL
|
||||
|
||||
:::caution
|
||||
We make no claims or guarantees regarding this section concerning security. If in doubt, enlist the help of an expert and conduct proper audits.
|
||||
:::
|
||||
|
||||
### Why SSL?
|
||||
As described [here](./cors.md), OHIF must be used in a [secure context](./cors.md#secure-context) in order to fully leverage all of OHIF's capabilities. Whenever OHIF is not running in a secure context and is navigated to using the OHIF's server IP address (e.g. `http://192.168.1.162:3000`) or domain name (e.g. `http://my.ohif.server`) then the following popup message will be displayed in OHIF
|
||||
|
||||

|
||||
|
||||
and the following message will appear in the browser console.
|
||||
|
||||

|
||||
|
||||
:::info
|
||||
The above indicate that OHIF is not running in a secure context. Among other things, this means information transferred to/from OHIF is not encrypted and certain capabilities such as 3D volume loading will NOT work. However, basic stack viewport functionality will continue to function.
|
||||
|
||||
Consideration must be given as to whether OHIF should be deployed in a secure context over SSL.
|
||||
:::
|
||||
|
||||
### Specifying the SSL Port, Certificate and Private Key
|
||||
|
||||
For convenience, the [built Docker image](#building-the-docker-image) can be run over SSL by
|
||||
- setting the `${SSL_PORT}` environment variable
|
||||
- volume mounting the SSL certificate
|
||||
- volume mounting the SSL private key
|
||||
|
||||
:::info
|
||||
The volume mounted SSL certificate and private key are mapped to the [`ssl_certificate`](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate) and [`ssl_certificate_key`](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key) `nginx` directives respectively.
|
||||
:::
|
||||
|
||||
Similar to the [`nginx` listen port](#configuring-the-nginx-listen-port), the `${SSL_PORT}` environment variable is the internal port that `nginx` listens on to serve the OHIF web server over SSL and has to be likewise published via the `-p` switch.
|
||||
|
||||
The following is an example command running the Docker container over SSL. Note that depending on the version of Docker, an absolute path to the certificate and private key files might be required.
|
||||
|
||||
```sh
|
||||
docker run -d -e SSL_PORT=443 -p 3003:443/tcp -v /path/to/certificate:/etc/ssl/certs/ssl-certificate.crt -v /path/to/private/key:/etc/ssl/private/ssl-private-key.key --name ohif-viewer-container ohif-viewer-image
|
||||
```
|
||||
|
||||
:::caution
|
||||
The above deploys OHIF over SSL using `nginx`'s default SSL configuration. For further OHIF server hardening and security configuration, consider enlisting an expert and then editing OHIF's `nginx` [SSL template configuration file](https://github.com/OHIF/Viewers/blob/8a8ae237d26faf123abeb073cbf0cd426c3e9ef2/.docker/Viewer-v3.x/default.ssl.conf.template) with further [security settings](https://nginx.org/en/docs/http/ngx_http_ssl_module.html) and [tweaks](http://nginx.org/en/docs/http/configuring_https_servers.html) and then [build a new Docker image](#building-the-docker-image) from there.
|
||||
:::
|
||||
|
||||
:::caution
|
||||
The private key is a secure entity and should have restricted access. Keep it safe!
|
||||
:::
|
||||
|
||||
:::caution
|
||||
The presence of the `${SSL_PORT}` environment variable is used to trigger to deploy over SSL as opposed to HTTP. If `${SSL_PORT}` is NOT defined, then HTTP is used even if the certificate and private key volumes are mounted.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
The read and write permissions of the source, mounted volumes are preserved in the Docker container. The volume mounted certificate and private key require read permission.
|
||||
|
||||
One way to ensure both are readable is to issue the following on the host system terminal prior to running the Docker container and mounting the certificate and private key volumes.
|
||||
|
||||
```sh
|
||||
sudo chmod 644 /path/to/certificate /path/to/private/key
|
||||
```
|
||||
:::
|
||||
|
||||
:::tip
|
||||
The SSL certificate and private key can be either [CA issued](#ca-signed-certificates) or [self-signed](#self-signed-certificates).
|
||||
:::
|
||||
|
||||
|
||||
### CA Signed Certificates
|
||||
|
||||
According to [SSL.com](https://www.ssl.com/faqs/what-is-a-certificate-authority/), a global certificate authority (CA) is a trusted authority and organization that guarantees the identity of other, third-party entities and guarantees the integrity of the electronic information (e.g. web site data) those third-party entities provide and deliver.
|
||||
|
||||
There are many globally trusted CAs. Below is a non-exhaustive list of some CAs including links to some documentation for creating and installing certificates and keys from those authorities to be used with `nginx`.
|
||||
- [GoDaddy](https://ca.godaddy.com/help/nginx-install-a-certificate-6722)
|
||||
- [Let's Encrypt](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/)
|
||||
- [digicert](https://www.digicert.com/kb/csr-ssl-installation/nginx-openssl.htm)
|
||||
|
||||
|
||||
### Self-Signed Certificates
|
||||
|
||||
According to [Entrust](https://www.entrust.com/resources/faq/what-is-a-self-signed-certificate), a self-signed certificate is one that is NOT signed by a trusted, public [CA authority](#ca-signed-certificates), but instead (typically) signed by the developer or individual or organization responsible for a web site.
|
||||
|
||||
Browsers will treat self-signed certificates as not secure because the signer is not publicly recognized and trusted. When visiting a site encrypted with a self-signed certificate, the browser will present a screen similar to the following warning about the potential risk.
|
||||
|
||||

|
||||
|
||||
For a self-signed certificate this is normal and expected. Clicking the `Advanced` button displays further information as well as a link for proceeding to site that the certificate is encrypting.
|
||||
|
||||

|
||||
|
||||
|
||||
Self-signed certificates might be appropriate for testing or perhaps deploying a site within an organization's internal LAN. In any case, consult an expert prior to deploying OHIF over SSL.
|
||||
|
||||
:::tip
|
||||
A self-signed certificate can be generated using [`openssl`](https://www.openssl.org/) on the command line.
|
||||
:::
|
||||
|
||||
To create a self-signed certificate:
|
||||
1. Open a command prompt.
|
||||
2. Issue the following command:
|
||||
```sh
|
||||
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /desired/key/directory/self-signed-private.key -out /desired/cert/directory/self-signed.crt
|
||||
```
|
||||
|
||||
The chart below describes each of the items in the command.
|
||||
|
||||
|Command Item|Description|
|
||||
|------------|-----------|
|
||||
|sudo|temporarily grant access as the root/super user to run the `openssl` command|
|
||||
|openssl|the command line tool for creating and managing certificates and keys|
|
||||
|req|this together with the subsequent `-x509` indicates to request to generate a self-signed certificate|
|
||||
|-x509|this together with the `req` indicates to request to generate a self-signed certificate|
|
||||
|-nodes|skip the option to secure the certificate with a passphrase; this allows `nginx` to start up with without intervention to enter a passphrase each time|
|
||||
|-days 365|the number of days the certificate will be valid for|
|
||||
|-newkey rsa:2048|create the a new certificate and key together and make an RSA key that is 2048 bits long|
|
||||
|-keyout|the path and file name where the private key will be written to|
|
||||
|-out|the path and file name where the certificate will be written to|
|
||||
|
||||
3. Answer the prompts that follow. The table below lists the various prompts. The default value for each prompt is shown within the square brackets. The most important prompt is `Common Name (e.g. server FQDN or YOUR name)`. For this enter the IP address of the OHIF server being secured.
|
||||
|
||||
|Prompt|
|
||||
|------|
|
||||
|Country Name (2 letter code) [AU]|
|
||||
|State or Province Name (full name) [Some-State]|
|
||||
|Locality Name (eg, city) []|
|
||||
|Organization Name (eg, company) [Internet Widgets Pty Ltd]|
|
||||
|Organizational Unit Name (eg, section) []|
|
||||
|Common Name (e.g. server FQDN or YOUR name) []|
|
||||
|Email Address []|
|
||||
|
||||
4. Once completed, the self-signed certificate and private key will be in the locations specified by the `-keyout` and `-out` flags and can be [volume mounted](#specifying-the-ssl-port-certificate-and-private-key) accordingly to the OHIF Docker container.
|
||||
|
||||
:::tip
|
||||
Windows' users can access `openssl` using [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/).
|
||||
:::
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
# Google Cloud Healthcare
|
||||
|
||||
> The [Google Cloud Healthcare API](https://cloud.google.com/healthcare/) is a
|
||||
> powerful option for storing medical imaging data in the cloud.
|
||||
|
||||
An alternative to deploying your own PACS is to use a software-as-a-service
|
||||
provider such as Google Cloud. The Cloud Healthcare API promises to be a
|
||||
scalable, secure, cost effective image storage solution for those willing to
|
||||
store their data in the cloud. It offers an
|
||||
[almost-entirely complete DICOMWeb API](https://cloud.google.com/healthcare/docs/dicom)
|
||||
which requires tokens generated via the
|
||||
[OAuth 2.0 Sign In flow](https://developers.google.com/identity/protocols/oauth2).
|
||||
Images can even be transcoded on the fly if this is desired.
|
||||
|
||||
## Setup a Google Cloud Healthcare Project
|
||||
|
||||
1. Create a Google Cloud account
|
||||
2. Create a project in Google Cloud
|
||||
|
||||
A project in Google Cloud can be created by clicking the projects drop down box.
|
||||
|
||||

|
||||
|
||||
And then clicking the `NEW PROJECT` button in the top-right corner of the
|
||||
dialogue that is displayed.
|
||||
|
||||
3. Enable the [Cloud Healthcare API](https://cloud.google.com/healthcare/) for your project
|
||||
|
||||
:::tip
|
||||
An API can be enabled through the `APIs & Services > Enabled APIs & Services`
|
||||
console and clicking the `+ ENABLE APIS AND SERVICES` button.
|
||||
|
||||

|
||||
:::
|
||||
|
||||
:::tip
|
||||
The principal (i.e. account) that is enabling the Cloud Healthcare API will require
|
||||
the following roles that can be set in the `IAM & Admin > IAM` console for the
|
||||
desired project.
|
||||
- Service Usage Viewer
|
||||
- Service Usage Admin
|
||||
:::
|
||||
|
||||
:::tip
|
||||
Roles can be added to a principal in the `IAM & Admin > IAM` console by clicking
|
||||
the `Edit principal` (i.e. pencil) icon to the right of a principal or by clicking the
|
||||
`GRANT ACCESS` button at the top of the list of principals. The `GRANT ACCESS`
|
||||
button is particularly useful if the `Edit principal` icon is disabled.
|
||||
:::
|
||||
|
||||
4. (Optional): Create a Dataset and DICOM Data Store for storing your DICOM data
|
||||
|
||||
:::tip
|
||||
To both list existing datasets as well as create a new dataset for your project,
|
||||
the principal (i.e. account) must have the following roles enabled
|
||||
in the `IAM & Admin > IAM` console.
|
||||
|
||||
- Editor
|
||||
|
||||
:::
|
||||
|
||||
5. Enable the [Cloud Resource Manager API](https://cloud.google.com/resource-manager/) for your project.
|
||||
|
||||
_Note:_ If you are having trouble finding the APIs, use the search box at
|
||||
the top of the Cloud console.
|
||||
|
||||
6. Go to APIs & Services > OAuth Consent Screen to create an OAuth Consent screen and fill in your application details.
|
||||
|
||||
- Run through the three step process of adding an OAuth Consent Screen, clicking `SAVE AND CONTINUE` at the end of each step.
|
||||
|
||||

|
||||
- For the Scopes step, for Google APIs, click the `ADD OR REMOVE SCOPES` button.
|
||||
- In the `Update selected scopes` dialogue that flies in from the right, add the
|
||||
following scopes to the `Manually add scopes` text box.
|
||||
- `https://www.googleapis.com/auth/cloudplatformprojects.readonly`
|
||||
- `https://www.googleapis.com/auth/cloud-healthcare`
|
||||
|
||||

|
||||
|
||||
- Click `ADD TO TABLE` and then click `UPDATE`
|
||||
|
||||
|
||||
7. Go to APIs & Services > Credentials to create a new set of credentials:
|
||||
|
||||
- Click `+ CREATE CREDENTIALS` and from the drop down select `OAuth Client ID`.
|
||||
See [OAuth 2.0 Client ID](https://developers.google.com/identity/protocols/oauth2/) for more information.
|
||||
|
||||

|
||||
|
||||
- Choose the "Web Application" type
|
||||
- Add your domain (e.g. `http://localhost:3000`) to the Authorized JavaScript
|
||||
origins.
|
||||
- Add your domain, plus `callback` (e.g. `http://localhost:3000/callback`) to the Authorized Redirect URIs.
|
||||
- Save your Client ID for later.
|
||||
|
||||
8. (Optional): Create a bucket containing DICOM files and import it into a Data Store
|
||||
|
||||
- When importing a bucket into a Data Store, the following warning might be
|
||||
displayed indicating that the Cloud Healthcare Service Agent service account associated with the
|
||||
project does not have the `Storage Object Viewer` role.
|
||||
|
||||

|
||||
|
||||
- The Cloud Healthcare Service Agent service account can be displayed in the
|
||||
`IAM & Admin > IAM` console by checking the `Include Google-provided role grants` checkbox.
|
||||
The `Storage Object Viewer` role can then be granted to the Cloud Healthcare Service Agent service account.
|
||||
|
||||

|
||||
|
||||
- More information regarding the Cloud Healthcare Service Agent service account can
|
||||
be found at https://cloud.google.com/healthcare-api/docs/permissions-healthcare-api-gcp-products
|
||||
|
||||
9. (Optional): Enable Public Datasets that are being hosted by Google:
|
||||
https://cloud.google.com/healthcare/docs/resources/public-datasets/
|
||||
|
||||
## Run the viewer with your OAuth Client ID
|
||||
|
||||
1. Open the `config/google.js` file and change `YOURCLIENTID` to your Client ID
|
||||
value.
|
||||
1. Run the OHIF Viewer using the config/google.js configuration file
|
||||
|
||||
```bash
|
||||
cd OHIFViewer
|
||||
yarn install
|
||||
APP_CONFIG=config/google.js yarn run dev
|
||||
```
|
||||
|
||||
## Configuring Google Cloud Healthcare as a datasource in OHIF
|
||||
|
||||
A Google Cloud Healthcare DICOM store can be configured as a DICOMweb datasource
|
||||
in OHIF. A full or partial path is permitted in the configuration file. For
|
||||
partial paths, the [data source configuration UI](../configuration/dataSources/configuration-ui.md)
|
||||
will assist in filling in the missing pieces. For example, a configuration with
|
||||
empty `wadoUriRoot`, `qidoRoot` and `wadoRoot` will prompt for the entire path
|
||||
step-by-step starting with the project.
|
||||
171
platform/docs/versioned_docs/version-3.8/deployment/iframe.md
Normal file
171
platform/docs/versioned_docs/version-3.8/deployment/iframe.md
Normal file
@@ -0,0 +1,171 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
sidebar_label: iframe
|
||||
---
|
||||
|
||||
# iframe
|
||||
|
||||
With the transition to more advanced visualization, loading, and rendering techniques using WebWorkers, WASM, and WebGL, the script tag usage of the OHIF viewer v3 has been deprecated.
|
||||
An alternative option for script tag usage is to employ an iframe. You can utilize the iframe element to load the OHIF viewer and establish communication with it using the postMessage API if needed.
|
||||
|
||||
We recommend utilizing modern development practices and incorporating OHIF viewer within your application using a more modular and integrated approach, such as leveraging bundlers, other UI
|
||||
components, and frameworks.
|
||||
|
||||
## Static Build
|
||||
|
||||
You can use the iframe element to load the OHIF viewer as a child element of your application if you need the
|
||||
viewer to be embedded within your application. The iframe element can be used as follows (use your own custom styles)
|
||||
|
||||
```html
|
||||
<iframe src="./path-to-ohif-build" style="width: 100%; height: 500px; border: none"/>
|
||||
```
|
||||
|
||||
The important thing to note here is that the iframe element is loading the OHIF viewer from the `./path-to-ohif-build`. This path can be
|
||||
named anything you want, but it should be the path to the OHIF viewer build directory. The build directory is the directory that
|
||||
contains the `index.html` file (See [build for production](./build-for-production.md) for more information).
|
||||
|
||||
It is also required that the PUBLIC_URL environment variable is set to the same path. For example, if the iframe is
|
||||
`<iframe src="./ohif" />` (which means there is a `ohif` folder containing the build in your main app), then you need to:
|
||||
|
||||
1. use a config (e.g. config/myConfig.js) file that is using the `routerBasename` of `/ohif` (note the one / - it is not /ohif/).
|
||||
2. build the viewer with `PUBLIC_URL=/ohif/ APP_CONFIG=config/myConfig.js yarn build` (note the two / - it is not /ohif).
|
||||
|
||||
:::tip
|
||||
Check to make sure the `app-config.js` in the build is reflecting the correct routerBasename.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
The PUBLIC_URL tells the application where to find the static assets and the routerBasename will tell the application how to handle the rouets
|
||||
:::
|
||||
|
||||
### Try it locally
|
||||
|
||||
Download the index.html and the build (against the /ohif/ path) from [here](https://ohif-assets.s3.us-east-2.amazonaws.com/iframe-basic/Archive.zip)
|
||||
|
||||
Then run the
|
||||
|
||||
```bash
|
||||
npx http-server unzipped-folder
|
||||
|
||||
# you can use npx serve ./dist -c ../public/serve.json as an alternative to http-server
|
||||
```
|
||||
|
||||
You should be able to see
|
||||
|
||||

|
||||
|
||||
:::info
|
||||
Notice the Cross Origin Isolation Warning. It is present to indicate that OHIF cannot render volumes because the volume viewports
|
||||
use SharedArrayBuffer which is not allowed for non cross origin isolated apps. You can read more about Cross Origin Isolation here
|
||||
https://web.dev/coop-coep/ or follow the steps below to enable it.
|
||||
:::
|
||||
|
||||
### Fixing the Cross Origin Isolation Warning to enable volume rendering
|
||||
|
||||
For that we need a more sophisticated setup, since we need to add the Cross Origin Embedder Policy and Cross Origin Opener Policy headers
|
||||
to make the parent app cross origin isolated. For that we can use an Express server. (Note: you can use any other method
|
||||
to add the headers, this is just one of the methods)
|
||||
|
||||
Download files from [here](https://ohif-assets.s3.us-east-2.amazonaws.com/iframe-express/Archive.zip)
|
||||
|
||||
```js
|
||||
const express = require("express")
|
||||
const app = express()
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader("Cross-Origin-Opener-Policy", "same-origin")
|
||||
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp")
|
||||
next()
|
||||
})
|
||||
|
||||
app.use(express.static("public")) // 'public' should be the folder with the static OHIF build files
|
||||
|
||||
app.listen(8080, () => console.log("Listening on port 8080!"))
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
:::tip
|
||||
if you are using webpack with react you can set
|
||||
|
||||
```js
|
||||
devServer: {
|
||||
headers: {
|
||||
"Cross-Origin-Opener-Policy": "same-origin",
|
||||
"Cross-Origin-Embedder-Policy": "require-corp"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If you are using Angular, you should modify the `angular.json` file to add the headers
|
||||
|
||||
```js
|
||||
"serve": {
|
||||
//
|
||||
"configurations": {
|
||||
//
|
||||
"development": {
|
||||
//
|
||||
"headers": {
|
||||
"Cross-Origin-Opener-Policy": "same-origin",
|
||||
"Cross-Origin-Embedder-Policy": "require-corp"
|
||||
}
|
||||
}
|
||||
},
|
||||
//
|
||||
},
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
|
||||
## Development Server
|
||||
|
||||
If you are not using the static build, you can use the iframe to load the viewer from the local development server. For example, if you are running the viewer locally on port 3000, you can use the following iframe element to load the viewer:
|
||||
|
||||
```html
|
||||
// e.g., app running on 3001 and iframe loading the viewer from 3000
|
||||
<iframe src="http://localhost:3000" style="width: 100%; height: 500px; border: none"/>
|
||||
```
|
||||
|
||||
Notice that not including the static build removes the need for
|
||||
the PUBLIC_URL and the routerBasename. However, the Cross Origin Resource Policy (CORP)
|
||||
headers must be set because the viewer will be loaded from a different port. You can read
|
||||
more about CORP [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy).
|
||||
Basically in the development server that is serving the viewer, add the following headers:
|
||||
|
||||
```js
|
||||
// use this if the embedding app is running on the same site as OHIF
|
||||
// (e.g. parent/embedding app is http://localhost:3001 and OHIF is http://localhost:3000)
|
||||
"Cross-Origin-Resource-Policy": "same-site"
|
||||
or
|
||||
|
||||
// use this if the embedding app is a completely different origin than OHIF (e.g.
|
||||
// parent/embedding app is http://192.168.1.2 and OHIF is http://localhost:3000)
|
||||
"Cross-Origin-Resource-Policy": "cross-origin"
|
||||
```
|
||||
|
||||
:::info
|
||||
You can't set the `Cross-Origin-Resource-Policy` to `same-origin` since the viewer is loaded from a different port.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
If you are using webpack to serve the viewer it would be
|
||||
|
||||
```js
|
||||
devServer: {
|
||||
headers: {
|
||||
"Cross-Origin-Resource-Policy": "same-site" // cross-origin is also valid
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
Take a look at how other people have integrated OHIF in their react app
|
||||
|
||||
example1: https://github.com/OHIF/Viewers/issues/3371#issuecomment-1630405255
|
||||
:::
|
||||
332
platform/docs/versioned_docs/version-3.8/deployment/index.md
Normal file
332
platform/docs/versioned_docs/version-3.8/deployment/index.md
Normal file
@@ -0,0 +1,332 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
sidebar_label: Overview
|
||||
---
|
||||
|
||||
# Deployment
|
||||
|
||||
The OHIF Viewer can be served as a stand-alone PWA ([progressive web
|
||||
application][pwa-url]) by building and hosting a collection of static assets or be embedded in other web applications via an iframe if needed. In
|
||||
either case, you will need to configure your instance of the Viewer so that it
|
||||
can connect to your data source (the database or PACS that provides the data
|
||||
your Viewer will display).
|
||||
|
||||
:::tip
|
||||
Our goal is to make deployment as simple and painless as possible; however,
|
||||
there is an inherent amount of complexity in configuring and deploying web
|
||||
applications. If you find yourself a little lost, please don't hesitate to
|
||||
reach out to for help.
|
||||
:::
|
||||
|
||||
## Deployment Scenarios
|
||||
|
||||
### Stand-alone Viewer
|
||||
|
||||
Deploying the OHIF Viewer as a stand-alone web application provides many
|
||||
benefits, but comes at the cost of time and complexity. Some benefits include:
|
||||
|
||||
_Today:_
|
||||
|
||||
- Leverage [extensions](../platform/extensions/index.md) and
|
||||
[modes](../platform/modes/index.md) to drop-in powerful new features
|
||||
- Add routes and customize the viewer's workflow
|
||||
- Finer control over styling and whitelabeling
|
||||
|
||||
_In the future:_
|
||||
|
||||
- The ability to package the viewer for [App Store distribution][app-store]
|
||||
|
||||
#### Hosted Static Assets
|
||||
|
||||
At the end of the day, a production OHIF Viewer instance is a collection of
|
||||
HTML, CSS, JS, Font Files, and Images. We "build" those files from our
|
||||
`source code` with configuration specific to our project. We then make those
|
||||
files publicly accessible by hosting them on a Web Server.
|
||||
|
||||
##### Part 1 - Build Production Assets
|
||||
|
||||
"Building", or creating, the files you will need is the same regardless of the
|
||||
web host you choose. You can find detailed instructions on how to configure and
|
||||
build the OHIF Viewer in our
|
||||
["Build for Production" guide](./build-for-production.md).
|
||||
|
||||
##### Part 2 - Host Your App
|
||||
|
||||
There are a lot of [benefits to hosting static assets][host-static-assets] over
|
||||
dynamic content. You can find instructions on how to host your build's output
|
||||
via one of these guides:
|
||||
|
||||
_Drag-n-drop_
|
||||
|
||||
- [Netlify: Drop](./static-assets.md#netlify-drop)
|
||||
|
||||
_Easy_
|
||||
|
||||
- [Surge.sh](./static-assets.md#surgesh)
|
||||
- [GitHub Pages](./static-assets.md#github-pages)
|
||||
|
||||
_Advanced_
|
||||
|
||||
- [AWS S3 + Cloudfront](./static-assets.md#aws-s3--cloudfront)
|
||||
- [GCP + Cloudflare](./static-assets.md#gcp--cloudflare)
|
||||
- [Azure](./static-assets.md#azure)
|
||||
|
||||
### Embedded Viewer (iframe)
|
||||
|
||||
`OHIF-v3` has deprecated deploying the viewer as an embedded viewer via a script
|
||||
tag as the number of underlying libraries that run web workers are increasing for OHIF. An example of these libraries is OHIF's 3D rendering functionality that is provided by
|
||||
`vtk-js`.
|
||||
|
||||
However, you can still embed the viewer using an iframe. You can utilize the iframe element to load the OHIF viewer and establish communication with it using the postMessage API if needed.
|
||||
Read more about how to use the iframe [here](./iframe.md).
|
||||
|
||||
|
||||
## Data
|
||||
|
||||
The OHIF Viewer is able to connect to any data source that implements the [DICOM
|
||||
Web Standard][dicom-web-standard]. [DICOM Web][dicom-web] refers to RESTful
|
||||
DICOM Services -- a recently standardized set of guidelines for exchanging
|
||||
medical images and imaging metadata over the internet. Not all archives fully
|
||||
support it yet, but it is gaining wider adoption.
|
||||
|
||||
### Configure Connection
|
||||
|
||||
If you have an existing archive and intend to host the OHIF Viewer at the same
|
||||
domain name as your archive, then connecting the two is as simple as following
|
||||
the steps laid out in our
|
||||
[Configuration Essentials Guide](./../configuration/configurationFiles.md).
|
||||
|
||||
#### What if I don't have an imaging archive?
|
||||
|
||||
We provide some guidance on configuring a local image archive in our
|
||||
[Data Source Essentials](./../configuration/dataSources/introduction.md)
|
||||
guide. Hosting an archive remotely is a little trickier. You can check out some
|
||||
of our [advanced recipes](#recipes) for modeled setups that may work for you.
|
||||
|
||||
#### What if I intend to host the OHIF Viewer at a different domain?
|
||||
|
||||
There are two important steps to making sure this setup works:
|
||||
|
||||
1. Your Image Archive needs to be exposed, in some way, to the open web. This
|
||||
can be directly, or through a `reverse proxy`, but the Viewer needs _some
|
||||
way_ to request its data.
|
||||
2. \* Your Image Archive needs to have appropriate CORS (Cross-Origin Resource
|
||||
Sharing) Headers
|
||||
|
||||
> \* Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional
|
||||
> HTTP headers to tell a browser to let a web application running at one origin
|
||||
> (domain) have permission to access selected resources from a server at a
|
||||
> different origin. - [MDN Web Docs: Web - Http - CORS][cors]
|
||||
|
||||
Most image archives do not provide either of these features "out of the box".
|
||||
It's common to use IIS, Nginx, or Apache to route incoming requests and append
|
||||
appropriate headers. You can find an example of this setup in our
|
||||
[Nginx + Image Archive Deployment Recipe](./nginx--image-archive.md).
|
||||
|
||||
#### What if my archive doesn't support DicomWeb?
|
||||
|
||||
It's possible to supply all Study data via JSON format, in the event you do not
|
||||
have a DicomWeb endpoint. You can host all of the relevant files on any web
|
||||
accessible server (Amazon S3, Azure Blob Storage, Local file server etc.)
|
||||
|
||||
This JSON is supplied via the '?url=' query parameter. It should reference an
|
||||
endpoint that returns **application/json** formatted text.
|
||||
|
||||
If you do not have an API, you can simply return a text file containing the JSON
|
||||
from any web server.
|
||||
|
||||
You tell the OHIF viewer to use JSON by using the `dicomjson` datasource and
|
||||
appending `'?url='` query to your mode's route:
|
||||
|
||||
e.g.
|
||||
`https://my-test-ohif-server/myMode/dicomjson?url=https://my-json-server/study-uid.json`
|
||||
|
||||
The returned JSON object must contain a single root object with a 'studies'
|
||||
array.
|
||||
|
||||
You can read more about using different data sources for mode's routes
|
||||
[here](../platform/modes/routes.md#route-path)
|
||||
|
||||
_Sample JSON format:_
|
||||
|
||||
```json
|
||||
{
|
||||
"studies": [
|
||||
{
|
||||
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78",
|
||||
"StudyDescription": "BRAIN SELLA",
|
||||
"StudyDate": "20010108",
|
||||
"StudyTime": "120022",
|
||||
"PatientName": "MISTER^MR",
|
||||
"PatientId": "832040",
|
||||
"series": [
|
||||
{
|
||||
"SeriesDescription": "SAG T-1",
|
||||
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
|
||||
"SeriesNumber": 2,
|
||||
"SeriesDate": "20010108",
|
||||
"SeriesTime": "120318",
|
||||
"Modality": "MR",
|
||||
"instances": [
|
||||
{
|
||||
"metadata": {
|
||||
"Columns": 512,
|
||||
"Rows": 512,
|
||||
"InstanceNumber": 3,
|
||||
"AcquisitionNumber": 0,
|
||||
"PhotometricInterpretation": "MONOCHROME2",
|
||||
"BitsAllocated": 16,
|
||||
"BitsStored": 16,
|
||||
"PixelRepresentation": 1,
|
||||
"SamplesPerPixel": 1,
|
||||
"PixelSpacing": [0.390625, 0.390625],
|
||||
"HighBit": 15,
|
||||
"ImageOrientationPatient": [0, 1, 0, 0, 0, -1],
|
||||
"ImagePositionPatient": [11.6, -92.5, 98.099998],
|
||||
"FrameOfReferenceUID": "1.2.840.113619.2.5.1762583153.223134.978956938.470",
|
||||
"ImageType": ["ORIGINAL", "PRIMARY", "OTHER"],
|
||||
"Modality": "MR",
|
||||
"SOPInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.124",
|
||||
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
|
||||
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78"
|
||||
},
|
||||
"url": "dicomweb://s3.amazonaws.com/lury/MRStudy/1.2.840.113619.2.5.1762583153.215519.978957063.124.dcm"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
More info on this JSON format can be found here
|
||||
[Issue #1500](https://github.com/OHIF/Viewers/issues/1500)
|
||||
|
||||
**Implementation Notes:**
|
||||
|
||||
<!-- 1. When hosting the viewer, you will also need to host a /viewer route on the server - or the browser may not be able to find the route. -->
|
||||
|
||||
1. For each instance url (dicom object) in the returned JSON, you must prefix
|
||||
the `url` with `dicomjson:` in order for the cornerstone image loader to
|
||||
retrieve it correctly. eg. `https://image-server/my-image.dcm` --->
|
||||
`dicomjson:https://image-server/my-image.dcm`
|
||||
2. The JSON format above is compatible with >= v3.7.8 of the application in `V2`
|
||||
version. Older versions of the viewer used a different JSON format. As of
|
||||
20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does
|
||||
not support this format yet.
|
||||
3. The JSON format is case-sensitive. Please ensure you have matched casing with
|
||||
the naturalised Dicom format referenced in
|
||||
[Issue #1500](https://github.com/OHIF/Viewers/issues/1500).
|
||||
|
||||
_CORS Issues (Cross-Origin Resource Sharing)_
|
||||
|
||||
If you host a JSON API or Images on a different domain from the app itself,
|
||||
you will likely have CORS issues. This will also happen when testing from
|
||||
Localhost and reaching out to remote servers. Even if the domain is the same,
|
||||
different ports, subdomains or protocols (https vs http) will also cause CORS
|
||||
errors. You will to need add a configuration on each server hosting these assets
|
||||
to allow your App server origin.
|
||||
|
||||
For example:
|
||||
|
||||
Let's assume your application is hosted on `https://my-ohif-server.com`.
|
||||
|
||||
Your JSON API is hosted on `https://my-json-api.aws.com`
|
||||
|
||||
And your images are stored on Amazon S3 at `https://my-s3-bucket.aws.com`
|
||||
|
||||
When you first start your application, browsing to
|
||||
`https://my-ohif-server.com/myMode/dicomjson?url=https://my-json-api.aws.com/api/my-json-study-info.json`,
|
||||
you will likely get a CORS error in the browser console as it tries to connect
|
||||
to `https://my-json-api.aws.com`.
|
||||
|
||||
Adding a setting on the JSON server to allow the CORS origin =
|
||||
`https://my-ohif-server.com` should solve this.
|
||||
|
||||
Next, you will likely get a similar CORS error, as the browser tries to go to
|
||||
`https://my-s3-bucket.aws.com`. You will need to go to the S3 bucket
|
||||
configuration, and add a CORS setting to allow origin =
|
||||
`https://my-ohif-server.com`.
|
||||
|
||||
Essentially, whenever the application connects to a remote resource, you will
|
||||
need to add the applications url to the allowed CORS Origins on that resource.
|
||||
Adding an origin similar to https://localhost:3000 will also allow for local
|
||||
testing.
|
||||
|
||||
### Securing Your Data
|
||||
|
||||
Coming soon
|
||||
|
||||
<!--
|
||||
> Feeling lost? Securing your data is important, and it can be hard to tell if
|
||||
> you've gotten it right. Don't hesitate to work with professional auditors, or
|
||||
> [enlist help from experts](../help).
|
||||
|
||||
The OHIF Viewer can be configured to work with authorization servers that
|
||||
support one or more of the OpenID-Connect authorization flows. The Viewer finds
|
||||
it's OpenID-Connect settings on the `oidc` configuration key. You can set these
|
||||
values following the instructions laid out in the
|
||||
[Configuration Essentials Guide](./../configuration/index.md).
|
||||
|
||||
_Example OpenID-Connect Settings:_
|
||||
|
||||
```js
|
||||
window.config = {
|
||||
...
|
||||
oidc: [
|
||||
{
|
||||
// ~ REQUIRED
|
||||
// Authorization Server URL
|
||||
authority: 'http://127.0.0.1/auth/realms/ohif',
|
||||
client_id: 'ohif-viewer',
|
||||
redirect_uri: 'http://127.0.0.1/callback', // `OHIFStandaloneViewer.js`
|
||||
response_type: 'code', // "Authorization Code Flow"
|
||||
scope: 'openid', // email profile openid
|
||||
// ~ OPTIONAL
|
||||
post_logout_redirect_uri: '/logout-redirect.html',
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
You can find an example of this setup in our
|
||||
[User Account Control Deployment Recipe](./user-account-control.md).
|
||||
|
||||
#### Choosing a Flow for the Viewer
|
||||
|
||||
In general, we recommend using the "Authorization Code Flow" ( [see
|
||||
`response_type=code` here][code-flows]); however, the "Implicit Flow" ( [see
|
||||
`response_type=token` here][code-flows]) can work if additional precautions are
|
||||
taken. If the flow you've chosen produces a JWT Token, it's validity can be used
|
||||
to secure access to your Image Archive as well.
|
||||
|
||||
-->
|
||||
|
||||
### Recipes
|
||||
|
||||
We've included a few recipes for common deployment scenarios. There are many,
|
||||
many possible configurations, so please don't feel limited to these setups.
|
||||
Please feel free to suggest or contribute your own recipes.
|
||||
|
||||
- [Build for Production](./build-for-production.md)
|
||||
- [Static](./static-assets.md)
|
||||
- [Nginx + Image Archive](./nginx--image-archive.md)
|
||||
- [User Account Control](./user-account-control.md)
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[viewer-npm]: https://www.npmjs.com/package/@ohif/app
|
||||
[pwa-url]: https://developers.google.com/web/progressive-web-apps/
|
||||
[static-assets-url]: https://www.maxcdn.com/one/visual-glossary/static-content/
|
||||
[app-store]: https://medium.freecodecamp.org/i-built-a-pwa-and-published-it-in-3-app-stores-heres-what-i-learned-7cb3f56daf9b
|
||||
[dicom-web-standard]: https://www.dicomstandard.org/dicomweb/
|
||||
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
|
||||
[host-static-assets]: https://www.netlify.com/blog/2016/05/18/9-reasons-your-site-should-be-static/
|
||||
[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||
[code-flows]: https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660
|
||||
[code-sandbox]: https://codesandbox.io/s/viewer-script-tag-tprch
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -0,0 +1,272 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
# Nginx + Image Archive
|
||||
|
||||
> DISCLAIMER! We make no claims or guarantees of this approach's security. If in
|
||||
> doubt, enlist the help of an expert and conduct proper audits.
|
||||
|
||||
At a certain point, you may want others to have access to your instance of the
|
||||
OHIF Viewer and its medical imaging data. This post covers one of many potential
|
||||
setups that accomplish that. Please note, noticeably absent is user account
|
||||
control.
|
||||
|
||||
Do not use this recipe to host sensitive medical data on the open web. Depending
|
||||
on your company's policies, this may be an appropriate setup on an internal
|
||||
network when protected with a server's basic authentication.
|
||||
|
||||
## Overview
|
||||
|
||||
Our two biggest hurdles when hosting our image archive and web client are:
|
||||
|
||||
- Risks related to exposing our PACS to the network
|
||||
- Cross-Origin Resource Sharing (CORS) requests
|
||||
|
||||
### Handling Web Requests
|
||||
|
||||
We mitigate our first issue by allowing [Nginx][nginx] to handle incoming web
|
||||
requests. Nginx is open source software for web serving, reverse proxying,
|
||||
caching, and more. It's designed for maximum performance and stability --
|
||||
allowing us to more reliably serve content than Orthanc's built-in server can.
|
||||
|
||||
More specifically, we accomplish this by using a
|
||||
[`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) to retrieve
|
||||
resources from our image archive (Orthanc), and when accessing its web admin.
|
||||
|
||||
> A reverse proxy is a type of proxy server that retrieves resources on behalf
|
||||
> of a client from one or more servers. These resources are then returned to the
|
||||
> client, appearing as if they originated from the proxy server itself.
|
||||
|
||||
### CORS Issues
|
||||
|
||||
Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to
|
||||
tell a browser which web applications have permission to access selected
|
||||
resources from a server at a different origin (domain, protocol, port). IE. By
|
||||
default, a Web App located at `http://my-website.com` can't access resources
|
||||
hosted at `http://not-my-website.com`
|
||||
|
||||
We can solve this one of two ways:
|
||||
|
||||
1. Have our Image Archive located at the same domain as our Web App
|
||||
2. Add appropriate `Access-Control-Allow-*` HTTP headers
|
||||
|
||||
**This solution uses the first approach.**
|
||||
|
||||
You can read more about CORS in this Medium article: [Understanding
|
||||
CORS][understanding-cors]
|
||||
|
||||
### Diagram
|
||||
|
||||
This setup allows us to create a setup similar to the one pictured below:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
- All web requests are routed through `nginx` on our `OpenResty` image
|
||||
- `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints
|
||||
- `/pacs-admin` is a reverse proxy for `orthanc`'s Web Admin
|
||||
- All static resources for OHIF Viewer are served up by `nginx` when a matching
|
||||
route for that resource is requested
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Requirements
|
||||
|
||||
- Docker
|
||||
- [Docker for Mac](https://docs.docker.com/docker-for-mac/)
|
||||
- [Docker for Windows](https://docs.docker.com/docker-for-windows/)
|
||||
|
||||
_Not sure if you have `docker` installed already? Try running `docker --version`
|
||||
in command prompt or terminal_
|
||||
|
||||
### Setup
|
||||
|
||||
- Navigate to `app` folder inside `platform`
|
||||
- then: `cd .recipes/OpenResty-Orthanc`
|
||||
- run: `docker-compose up --build`
|
||||
- Navigate to `127.0.0.1` for the viewer
|
||||
- Navigate to `127.0.0.1/pacs-admin` for uploading studies via the UI, or send studies via DIMSE C-STORE to `ORTHANC@127.0.0.1:4242` (hint: you can use utilites like dcm4che's `storescu` to send studies in bulk via the command line)
|
||||
|
||||
|
||||
|
||||
You can see the overview of the mentioned steps:
|
||||
|
||||
|
||||
|
||||
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
|
||||
<iframe src="https://player.vimeo.com/video/843233827?badge=0&autopause=0&player_id=0&app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="measurement-report"></iframe>
|
||||
</div>
|
||||
|
||||
<!-- ### Setup
|
||||
|
||||
_Spin Things Up_
|
||||
|
||||
- Navigate to `<project-root>/docker/OpenResty-Orthanc` in your shell
|
||||
- Run `docker-compose up`
|
||||
|
||||
_Upload Your First Study_
|
||||
|
||||
- Navigate to `http://127.0.0.1/pacs-admin`
|
||||
- From the top right, select "Upload"
|
||||
- Click "Select files to upload..." (DICOM)
|
||||
- Click "Start the upload"
|
||||
- Navigate back to `http://127.0.0.1/` to view your studies in the Study List -->
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
_Exit code 137_
|
||||
|
||||
This means Docker ran out of memory. Open Docker Desktop, go to the `advanced`
|
||||
tab, and increase the amount of Memory available.
|
||||
|
||||
_Cannot create container for service X_
|
||||
|
||||
Use this one with caution: `docker system prune`
|
||||
|
||||
_X is already running_
|
||||
|
||||
Stop running all containers:
|
||||
|
||||
- Win: `docker ps -a -q | ForEach { docker stop $_ }`
|
||||
- Linux: `docker stop $(docker ps -a -q)`
|
||||
|
||||
|
||||
_Traceback (most recent call last):_
|
||||
_File "urllib3/connectionpool.py", line 670, in urlopen_
|
||||
_...._
|
||||
|
||||
Are you sure your docker is running? see explanation [here](https://github.com/docker/compose/issues/7896)
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
After verifying that everything runs with default configuration values, you will
|
||||
likely want to update:
|
||||
|
||||
- The domain: `http://127.0.0.1`
|
||||
|
||||
#### OHIF Viewer
|
||||
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file. The
|
||||
configuration we use is set to a specific file when we build the viewer, and
|
||||
determined by the env variable: `APP_CONFIG`. You can see where we set its value
|
||||
in the `dockerfile` for this solution:
|
||||
|
||||
`ENV APP_CONFIG=config/docker_openresty-orthanc.js`
|
||||
|
||||
You can find the configuration we're using here:
|
||||
`/public/config/docker_openresty-orthanc.js`
|
||||
|
||||
To rebuild the `webapp` image created by our `dockerfile` after updating the
|
||||
Viewer's configuration, you can run:
|
||||
|
||||
- `docker-compose build` OR
|
||||
- `docker-compose up --build`
|
||||
|
||||
#### Other
|
||||
|
||||
All other files are found in: `/docker/OpenResty-Orthanc/`
|
||||
|
||||
| Service | Configuration | Docs |
|
||||
| ----------------- | --------------------------------- | ------------------------------------------- |
|
||||
| OHIF Viewer | [dockerfile][dockerfile] | You're reading them now! |
|
||||
| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] |
|
||||
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Deploying to Production
|
||||
|
||||
While these configuration and docker-compose files model an environment suitable
|
||||
for production, they are not easy to deploy "as is". You can either:
|
||||
|
||||
- Manually recreate this environment and deploy built application files **OR**
|
||||
- Deploy to a cloud kubernetes provider like
|
||||
[Digital Ocean](https://www.digitalocean.com/products/kubernetes/) **OR**
|
||||
- [See a full list of cloud providers here](https://landscape.cncf.io/category=cloud&format=card-mode&grouping=category)
|
||||
- Find and follow your preferred provider's guide on setting up
|
||||
[swarms and stacks](https://docs.docker.com/get-started/)
|
||||
|
||||
### Adding SSL
|
||||
|
||||
Adding SSL registration and renewal for your domain with Let's Encrypt that
|
||||
terminates at Nginx is an incredibly important step toward securing your data.
|
||||
Here are some resources, specific to this setup, that may be helpful:
|
||||
|
||||
- [lua-resty-auto-ssl](https://github.com/GUI/lua-resty-auto-ssl)
|
||||
- [Let's Encrypt + Nginx](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/)
|
||||
|
||||
While we terminate SSL at Nginx, it may be worth using self-signed certificates
|
||||
for communication between services.
|
||||
|
||||
- [SSL Termination for TCP Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/)
|
||||
|
||||
### Use PostgresSQL w/ Orthanc
|
||||
|
||||
Orthanc can handle a large amount of data and requests, but if you find that
|
||||
requests start to slow as you add more and more studies, you may want to
|
||||
configure your Orthanc instance to use PostgresSQL. Instructions on how to do
|
||||
that can be found in the
|
||||
[`Orthanc Server Book`](http://book.orthanc-server.com/users/docker.html), under
|
||||
"PostgreSQL and Orthanc inside Docker"
|
||||
|
||||
### Improving This Guide
|
||||
|
||||
Here are some improvements this guide would benefit from, and that we would be
|
||||
more than happy to accept Pull Requests for:
|
||||
|
||||
- SSL Support
|
||||
- Complete configuration with `.env` file (or something similar)
|
||||
- Any security issues
|
||||
- One-click deploy to a cloud provider
|
||||
|
||||
## Resources
|
||||
|
||||
### Misc. Helpful Commands
|
||||
|
||||
_Check if `nginx.conf` is valid:_
|
||||
|
||||
```bash
|
||||
docker run --rm -t -a stdout --name my-openresty -v $PWD/config/:/usr/local/openresty/nginx/conf/:ro openresty/openresty:alpine-fat openresty -c /usr/local/openresty/nginx/conf/nginx.conf -t
|
||||
```
|
||||
|
||||
_Interact w/ running container:_
|
||||
|
||||
`docker exec -it CONTAINER_NAME bash`
|
||||
|
||||
_List running containers:_
|
||||
|
||||
`docker ps`
|
||||
|
||||
### Referenced Articles
|
||||
|
||||
For more documentation on the software we've chosen to use, you may find the
|
||||
following resources helpful:
|
||||
|
||||
- [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html)
|
||||
- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/)
|
||||
- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/)
|
||||
|
||||
For a different take on this setup, check out the repositories our community
|
||||
members put together:
|
||||
|
||||
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
|
||||
- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker)
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- DOCS -->
|
||||
[nginx]: https://www.nginx.com/resources/glossary/nginx/
|
||||
[understanding-cors]: https://medium.com/@baphemot/understanding-cors-18ad6b478e2b
|
||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||
<!-- SRC -->
|
||||
[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/app/.recipes/OpenResty-Orthanc/dockerfile
|
||||
[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/app/.recipes/OpenResty-Orthanc/config/nginx.conf
|
||||
[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/app/.recipes/OpenResty-Orthanc/config/orthanc.json
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Deploy Static Assets
|
||||
|
||||
> WARNING! All of these solutions stand-up a publicly accessible web viewer. Do
|
||||
> not hook your hosted viewer up to a sensitive source of data without
|
||||
> implementing authentication.
|
||||
|
||||
There are a lot of options for deploying static assets. Some services, like
|
||||
`netlify` and `surge.sh`, specialize in static websites. You'll notice that
|
||||
deploying with them requires much less time and effort, but comes at the cost of
|
||||
less product offerings.
|
||||
|
||||
While not required, it can simplify things to host your Web Viewer alongside
|
||||
your image archive. Services with more robust product offerings, like
|
||||
`Google Cloud`, `Microsoft's Azure`, and `Amazon Web Services (AWS)`, are able
|
||||
to accommodate this setup.
|
||||
|
||||
_Drag-n-drop_
|
||||
|
||||
- [Netlify: Drop](#netlify-drop)
|
||||
|
||||
_Easy_
|
||||
|
||||
- [Surge.sh](#surgesh)
|
||||
- [GitHub Pages](#github-pages)
|
||||
|
||||
_Advanced_
|
||||
|
||||
- [Deploy Static Assets](#deploy-static-assets)
|
||||
- [Drag-n-drop](#drag-n-drop)
|
||||
- [Netlify Drop](#netlify-drop)
|
||||
- [Easy](#easy)
|
||||
- [Surge.sh](#surgesh)
|
||||
- [GitHub Pages](#github-pages)
|
||||
- [Advanced](#advanced)
|
||||
- [AWS S3 + Cloudfront](#aws-s3--cloudfront)
|
||||
- [GCP + Cloudflare](#gcp--cloudflare)
|
||||
- [Azure](#azure)
|
||||
|
||||
## Drag-n-drop
|
||||
|
||||
### Netlify Drop
|
||||
|
||||
|
||||
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
|
||||
<iframe src="https://player.vimeo.com/video/843233793?badge=0&autopause=0&player_id=0&app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="measurement-report"></iframe>
|
||||
</div>
|
||||
|
||||
|
||||
_GIF demonstrating deployment with Netlify Drop_
|
||||
|
||||
1. https://app.netlify.com/drop
|
||||
2. Drag your `build/` folder on to the drop target
|
||||
3. ...
|
||||
4. _annnd you're done_
|
||||
|
||||
**Features:**
|
||||
|
||||
- Custom domains & HTTPS
|
||||
- Instant Git integration
|
||||
- Continuous deployment
|
||||
- Deploy previews
|
||||
- Access to add-ons
|
||||
|
||||
(Non-free tiers include identity, FaaS, Forms, etc.)
|
||||
|
||||
Learn more about [Netlify on their website](https://www.netlify.com/)
|
||||
|
||||
## Easy
|
||||
|
||||
### Surge.sh
|
||||
|
||||
> Static web publishing for Front-End Developers. Simple, single-command web
|
||||
> publishing. Publish HTML, CSS, and JS for free, without leaving the command
|
||||
> line.
|
||||
|
||||

|
||||
|
||||
_GIF demonstrating deployment with surge_
|
||||
|
||||
```shell
|
||||
# Add surge command
|
||||
yarn global add surge
|
||||
|
||||
# In the build directory
|
||||
surge
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- Free custom domain support
|
||||
- Free SSL for surge.sh subdomains
|
||||
- pushState support for single page apps
|
||||
- Custom 404.html pages
|
||||
- Barrier-free deployment through the CLI
|
||||
- Easy integration into your Grunt toolchain
|
||||
- Cross-origin resource support
|
||||
- And more…
|
||||
|
||||
Learn more about [surge.sh on their website](https://surge.sh/)
|
||||
|
||||
### GitHub Pages
|
||||
|
||||
> WARNING! While great for project sites and light use, it is not advised to use
|
||||
> GitHub Pages for production workloads. Please consider using a different
|
||||
> service for mission critical applications.
|
||||
|
||||
> Websites for you and your projects. Hosted directly from your GitHub
|
||||
> repository. Just edit, push, and your changes are live.
|
||||
|
||||
This deployment strategy makes more sense if you intend to maintain your project in
|
||||
a GitHub repository. It allows you to specify a `branch` or `folder` as the
|
||||
target for a GitHub Page's website. As you push code changes, the hosted content
|
||||
updates to reflect those changes.
|
||||
|
||||
1. Head over to GitHub.com and create a new repository, or go to an existing
|
||||
one. Click on the Settings tab.
|
||||
2. Scroll down to the GitHub Pages section. Choose the `branch` or `folder` you
|
||||
would like as the "root" of your website.
|
||||
3. Fire up a browser and go to `http://username.github.io/repository`
|
||||
|
||||
Configuring Your Site:
|
||||
|
||||
- [Setting up a custom domain](https://help.github.com/en/articles/using-a-custom-domain-with-github-pages)
|
||||
- [Setting up SSL](https://help.github.com/en/articles/securing-your-github-pages-site-with-https)
|
||||
|
||||
Learn more about [GitHub Pages on its website](https://pages.github.com/)
|
||||
|
||||
## Advanced
|
||||
|
||||
All of these options, while using providers with more service offerings,
|
||||
demonstrate how to host the viewer with their respective file storage and CDN
|
||||
offerings. While you can serve your static assets this way, if you're going
|
||||
through the trouble of using AWS/GCP/Azure, it's more likely you're doing so to
|
||||
avoid using a proxy or to simplify authentication.
|
||||
|
||||
If that is the case, check out some of our more advanced `docker` deployments
|
||||
that target these providers from the left-hand sidepanel.
|
||||
|
||||
These guides can be a bit longer and an update more frequently. To provide
|
||||
accurate documentation, we will link to each provider's own recommended steps:
|
||||
|
||||
### AWS S3 + Cloudfront
|
||||
|
||||
- [Host a Static Website](https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html)
|
||||
- [Speed Up Your Website with Cloudfront](https://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-cloudfront-walkthrough.html)
|
||||
|
||||
### GCP + Cloudflare
|
||||
|
||||
- [Things to Know Before Getting Started](https://code.luasoftware.com/tutorials/google-cloud-storage/things-to-know-before-hosting-static-website-on-google-cloud-storage/)
|
||||
- [Hosting a Static Website on GCP](https://cloud.google.com/storage/docs/hosting-static-website)
|
||||
|
||||
### Azure
|
||||
|
||||
- Deploying viewer to Azure blob storage as a static website:
|
||||
Refer to [Host a static website](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website)
|
||||
High level steps :
|
||||
1. Go to Azure portal and create a storage account.
|
||||
2. Under Overview->Capabilities, select Static website.
|
||||
3. Enable Static website. Set the index document as ‘index.html’.
|
||||
4. Copy the primary endpoint. This will serve as the root URL for the viewer.
|
||||
5. Save. A new container named ‘$web’ will be created.
|
||||
6. Copy OHIF viewer’s build output from ‘platform\app\dist’ folder to the ‘$web’ container.
|
||||
7. Open browser and navigate to the viewer root URL copied in the step above. It should display OHIF viewer with data from default data source.
|
||||
|
||||

|
||||
Special consideration while accessing DicomJson data source :
|
||||
• Due to the way routing is handled in react, it may error out in production when trying to display data through dicomJson data source. E.g. https://[Static Website endpoint]/viewer/dicomjson?url= https://ohif-dicom-json-example.s3.amazonaws.com/LIDC-IDRI-0001.json
|
||||
• Resolution to this is to set error page to ‘index.html’ at the website level. This will ensure that all errors are redirected to root and requests are further served from root path.
|
||||

|
||||
|
||||
- [Add SSL Support](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-https-custom-domain-cdn)
|
||||
- [Configure a Custom Domain](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-custom-domain-name)
|
||||
@@ -0,0 +1,292 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
---
|
||||
# User Account Control
|
||||
|
||||
> DISCLAIMER! We make no claims or guarantees of this approach's security. If in
|
||||
> doubt, enlist the help of an expert and conduct proper audits.
|
||||
|
||||
Making a viewer and its medical imaging data accessible on the open web can
|
||||
provide a lot of benefits, but requires additional security to make sure
|
||||
sensitive information can only be viewed by authorized individuals. Most image
|
||||
archives are equipped with basic security measures, but they are not
|
||||
robust/secure enough for the open web.
|
||||
|
||||
This guide covers one of many potential production setups that secure our
|
||||
sensitive data.
|
||||
|
||||
## Overview
|
||||
|
||||
This guide builds on top of our
|
||||
[Nginx + Image Archive guide](/deployment/recipes/nginx--image-archive.md),
|
||||
wherein we used a [`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy)
|
||||
to retrieve resources from our image archive (Orthanc).
|
||||
|
||||
To add support for "User Account Control" we introduce
|
||||
[Keycloak](https://www.keycloak.org/about.html). Keycloak is an open source
|
||||
Identity and Access Management solution that makes it easy to secure
|
||||
applications and services with little to no code. We improve upon our
|
||||
`reverse proxy` setup by integrating Keycloak and Nginx to create an
|
||||
`authenticating reverse proxy`.
|
||||
|
||||
> An authenticating reverse proxy is a reverse proxy that only retrieves the
|
||||
> resources on behalf of a client if the client has been authenticated. If a
|
||||
> client is not authenticated they can be redirected to a login page.
|
||||
|
||||
This setup allows us to create a setup similar to the one pictured below:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
- All web requests are routed through `nginx` on our `OpenResty` image
|
||||
- `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints
|
||||
- Requires valid `Authorization: Bearer <token>` header
|
||||
- `/pacs-admin` is a reverse proxy for `orthanc`'s Web Admin
|
||||
- `/auth` is a reverse proxy for `keycloak`
|
||||
- All static resources for OHIF Viewer are unprotected and accessible. We have
|
||||
application logic that will redirect unauthenticated users to the appropriate
|
||||
`keycloak` login screen.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Requirements
|
||||
|
||||
- Docker
|
||||
- [Docker for Mac](https://docs.docker.com/docker-for-mac/)
|
||||
- [Docker for Windows](https://docs.docker.com/docker-for-windows/)
|
||||
|
||||
_Not sure if you have `docker` installed already? Try running `docker --version`
|
||||
in command prompt or terminal_
|
||||
|
||||
### Setup
|
||||
|
||||
_Spin Things Up_
|
||||
|
||||
- Navigate to `<project-root>platform\app\.recipes\OpenResty-Orthanc-Keycloak` in your shell
|
||||
- Run `docker-compose up`
|
||||
|
||||
_Create Your First User_
|
||||
|
||||
- Navigate to: `http://127.0.0.1/auth/admin`
|
||||
- Sign in with: `admin`/`password`
|
||||
- From the top left dropdown, select the `Ohif` realm
|
||||
- From the left sidebar, under `Manage`, select `Users`
|
||||
- Click `Add User`
|
||||
- Username: `test`
|
||||
- Email Verified: `ON`
|
||||
- Click `Save`
|
||||
- Click the `Credentials` Tab
|
||||
- New Password: `test`
|
||||
- Password Confirmation: `test`
|
||||
- Temporary: `OFF`
|
||||
- Click: `Reset Password`
|
||||
- From the top right dropdown, select `Admin`, then `Sign Out`
|
||||
|
||||
_Sign In_
|
||||
|
||||
- Navigate to `http://127.0.0.1/`
|
||||
- Username: `test`, Password: `test`
|
||||
- Click `Log In`
|
||||
|
||||
_Upload Your First Study_
|
||||
|
||||
- Navigate to `http://127.0.0.1/pacs-admin`
|
||||
- If you're not already logged in, use `test`/`test`
|
||||
- From the top right, select "Upload"
|
||||
- Click "Select files to upload..." (DICOM)
|
||||
- Click "Start the upload"
|
||||
- Navigate back to `http://127.0.0.1/` to view your studies in the Study List
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
_Exit code 137_
|
||||
|
||||
This means Docker ran out of memory. Open Docker Desktop, go to the `advanced`
|
||||
tab, and increase the amount of Memory available.
|
||||
|
||||
_Cannot create container for service X_
|
||||
|
||||
Use this one with caution: `docker system prune`
|
||||
|
||||
_X is already running_
|
||||
|
||||
Stop running all containers:
|
||||
|
||||
- Win: `docker ps -a -q | ForEach { docker stop $_ }`
|
||||
- Linux: `docker stop $(docker ps -a -q)`
|
||||
|
||||
### Configuration
|
||||
|
||||
After verifying that everything runs with default configuration values, you will
|
||||
likely want to update:
|
||||
|
||||
- The domain: `http://127.0.0.1`
|
||||
- Set secure, non-default passwords
|
||||
- Regenerate Keycloak Client Secrets
|
||||
|
||||
#### OHIF Viewer
|
||||
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file. The
|
||||
configuration we use is set to a specific file when we build the viewer, and
|
||||
determined by the env variable: `APP_CONFIG`. You can see where we set its value
|
||||
in the `dockerfile` for this solution:
|
||||
|
||||
`ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js`
|
||||
|
||||
You can find the configuration we're using here:
|
||||
`/public/config/docker_openresty-orthanc-keycloak.js`
|
||||
|
||||
To rebuild the `webapp` image created by our `dockerfile` after updating the
|
||||
Viewer's configuration, you can run:
|
||||
|
||||
- `docker-compose build` OR
|
||||
- `docker-compose up --build`
|
||||
|
||||
#### Other
|
||||
|
||||
All other files are found in: `/docker/OpenResty-Orthanc-Keycloak/`
|
||||
|
||||
| Service | Configuration | Docs |
|
||||
| ----------------- | ------------------------------------------------ | ------------------------------------------- |
|
||||
| OHIF Viewer | [dockerfile][dockerfile] / [config.js][config] | You're reading them now! |
|
||||
| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] |
|
||||
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
|
||||
| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | |
|
||||
|
||||
\* These are the seed values for Keycloak. They can be manually updated at
|
||||
`http://127.0.0.1/auth/admin`
|
||||
|
||||
#### Keycloak Themeing
|
||||
|
||||
The `Login` screen for the `ohif-viewer` client is using a Custom Keycloak
|
||||
theme. You can find the source files for it in
|
||||
`/docker/OpenResty-Orthanc-Keycloak/volumes/keycloak-themes/`. You can see how
|
||||
we add it to Keycloak in the `docker-compose` file, and you can read up on how
|
||||
to leverage custom themes in
|
||||
[Keycloak's own docs](https://www.keycloak.org/docs/latest/server_development/index.html#_themes).
|
||||
|
||||
| Default Theme | OHIF Theme |
|
||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Deploying to Production
|
||||
|
||||
While these configuration and docker-compose files model an environment suitable
|
||||
for production, they are not easy to deploy "as is". You can either:
|
||||
|
||||
- Manually recreate this environment and deploy built application files **OR**
|
||||
- Deploy to a cloud kubernetes provider like
|
||||
[Digital Ocean](https://www.digitalocean.com/products/kubernetes/) **OR**
|
||||
- [See a full list of cloud providers here](https://landscape.cncf.io/category=cloud&format=card-mode&grouping=category)
|
||||
- Find and follow your preferred provider's guide on setting up
|
||||
[swarms and stacks](https://docs.docker.com/get-started/)
|
||||
|
||||
### Adding SSL
|
||||
|
||||
Adding SSL registration and renewal for your domain with Let's Encrypt that
|
||||
terminates at Nginx is an incredibly important step toward securing your data.
|
||||
Here are some resources, specific to this setup, that may be helpful:
|
||||
|
||||
- [lua-resty-auto-ssl](https://github.com/GUI/lua-resty-auto-ssl)
|
||||
- [Let's Encrypt + Nginx](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/)
|
||||
|
||||
While we terminate SSL at Nginx, it may be worth using self signed certificates
|
||||
for communication between services.
|
||||
|
||||
- [SSL Termination for TCP Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/)
|
||||
|
||||
### Use PostgresSQL w/ Orthanc
|
||||
|
||||
Orthanc can handle a large amount of data and requests, but if you find that
|
||||
requests start to slow as you add more and more studies, you may want to
|
||||
configure your Orthanc instance to use PostgresSQL. Instructions on how to do
|
||||
that can be found in the
|
||||
[`Orthanc Server Book`](http://book.orthanc-server.com/users/docker.html), under
|
||||
"PostgreSQL and Orthanc inside Docker"
|
||||
|
||||
### Improving This Guide
|
||||
|
||||
Here are some improvements this guide would benefit from, and that we would be
|
||||
more than happy to accept Pull Requests for:
|
||||
|
||||
- SSL Support
|
||||
- Complete configuration with `.env` file (or something similar)
|
||||
- Keycloak Theme improvements
|
||||
- Any security issues
|
||||
- One-click deploy to a cloud provider
|
||||
|
||||
## Resources
|
||||
|
||||
### Misc. Helpful Commands
|
||||
|
||||
_Check if `nginx.conf` is valid:_
|
||||
|
||||
```bash
|
||||
docker run --rm -t -a stdout --name my-openresty -v $PWD/config/:/usr/local/openresty/nginx/conf/:ro openresty/openresty:alpine-fat openresty -c /usr/local/openresty/nginx/conf/nginx.conf -t
|
||||
```
|
||||
|
||||
_Interact w/ running container:_
|
||||
|
||||
`docker exec -it CONTAINER_NAME bash`
|
||||
|
||||
_List running containers:_
|
||||
|
||||
`docker ps`
|
||||
|
||||
_Clear Keycloak DB so you can re-seed values:_
|
||||
|
||||
- `docker volume prune` OR
|
||||
- `docker volume ls` and `docker volume rm VOLUME_NAME VOLUME_NAME`
|
||||
|
||||
### Referenced Articles
|
||||
|
||||
The inspiration for our setup was driven largely by these articles:
|
||||
|
||||
- [Securing Nginx with Keycloak](https://edhull.co.uk/blog/2018-06-06/keycloak-nginx)
|
||||
- [Authenticating Reverse Proxy with Keycloak](https://eclipsesource.com/blogs/2018/01/11/authenticating-reverse-proxy-with-keycloak/)
|
||||
- [Securing APIs with Kong and Keycloak](https://www.jerney.io/secure-apis-kong-keycloak-1/)
|
||||
|
||||
For more documentation on the software we've chosen to use, you may find the
|
||||
following resources helpful:
|
||||
|
||||
- [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html)
|
||||
- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/)
|
||||
- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/)
|
||||
- [Auth0: Picking a Grant Type](https://auth0.com/docs/api-auth/which-oauth-flow-to-use)
|
||||
|
||||
We chose to use a generic OpenID Connect library on the client, but it's worth
|
||||
noting that Keycloak comes packaged with its own:
|
||||
|
||||
- [oidc-client-js](https://github.com/IdentityModel/oidc-client-js/wiki)
|
||||
- [Keycloak JavaScript Adapter](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter)
|
||||
|
||||
If you're not already drowning in links, here are some good security resources
|
||||
for OAuth:
|
||||
|
||||
- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660)
|
||||
- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code)
|
||||
|
||||
For a different take on this setup, check out the repositories our community
|
||||
members put together:
|
||||
|
||||
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
|
||||
- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker)
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- DOCS -->
|
||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||
<!-- SRC -->
|
||||
[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js
|
||||
[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/dockerfile
|
||||
[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/nginx.conf
|
||||
[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/orthanc.json
|
||||
[config-keycloak]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json
|
||||
<!-- prettier-ignore-end -->
|
||||
Reference in New Issue
Block a user