Skip to content

Commit

Permalink
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
Browse files Browse the repository at this point in the history
…/warn-flag
  • Loading branch information
igoroctaviano committed Nov 4, 2024
2 parents d13759b + 3a6979e commit 29cd176
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 16 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,40 @@ Users can authenticate and authorize the application to access data via [OpenID

## Configuration

### Server Configuration

The app can be configured via a `public/config/{name}.js` JavaScript configuration file (see for example the default `public/config/local.js`).
Please refer to the [AppConfig.d.ts](src/AppConfig.d.ts) file for configuration options.

The configuration can be changed at build-time using the `REACT_APP_CONFIG` environment variable.

### Handling Mixed Content and HTTPS

When deploying SLIM with HTTPS, you may encounter mixed content scenarios where your PACS/VNA server returns HTTP URLs in its responses. This commonly occurs when:

- The PACS server populates bulkdataURI fields with internal HTTP URLs
- Your viewer is running on HTTPS but needs to communicate with services that respond with HTTP URLs
- You're using a reverse proxy that terminates SSL

To handle these scenarios, SLIM provides the `upgradeInsecureRequests` option in the server configuration:

```js
window.config = {
servers: [{
id: "local",
url: "https://your-server.com/dcm4chee-arc/aets/MYAET/rs",
upgradeInsecureRequests: true // Enable automatic HTTP -> HTTPS upgrade
}]
}
```

When `upgradeInsecureRequests` is set to `true` and at least one of your URLs (service URL, QIDO, WADO, or STOW prefixes) uses HTTPS, the viewer will automatically:

1. Add the `Content-Security-Policy: upgrade-insecure-requests` header to requests
2. Attempt to upgrade any HTTP responses to HTTPS

This feature was implemented in response to [issue #159](https://github.com/ImagingDataCommons/slim/issues/159) where PACS servers would return HTTP bulkdata URIs even when accessed via HTTPS.

## Deployment

Download the latest release from [github.com/imagingdatacommons/slim/releases](https://github.com/imagingdatacommons/slim/releases) and then run the following commands to install build dependencies and build the app:
Expand Down Expand Up @@ -223,7 +252,6 @@ Create an [OIDC client ID for web application](https://developers.google.com/ide
Note that Google's OIDC implementation does currently not yet support the authorization code grant type with PKCE challenge for private clients.
For the time being, the legacy implicit grand type has to be used.


## Development

To install requirements and run the app for local development, run the following commands:
Expand Down
1 change: 1 addition & 0 deletions src/AppConfig.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export interface ServerSettings {
retry?: RetryRequestSettings
errorMessages?: ErrorMessageSettings[]
storageClasses?: string[]
upgradeInsecureRequests?: boolean
}

export interface OidcSettings {
Expand Down
19 changes: 19 additions & 0 deletions src/DicomWebManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,20 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
)
)
}

const hasHttpsUrl = (url?: string): boolean => url?.startsWith('https') ?? false

const clientSettings: dwc.api.DICOMwebClientOptions = {
url: serviceUrl
}

const shouldUpgradeInsecure = serverSettings.upgradeInsecureRequests === true && [
serviceUrl,
serverSettings.qidoPathPrefix,
serverSettings.wadoPathPrefix,
serverSettings.stowPathPrefix
].some(hasHttpsUrl)

if (serverSettings.qidoPathPrefix !== undefined) {
clientSettings.qidoURLPrefix = serverSettings.qidoPathPrefix
}
Expand All @@ -70,6 +81,14 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
if (serverSettings.stowPathPrefix !== undefined) {
clientSettings.stowURLPrefix = serverSettings.stowPathPrefix
}

if (shouldUpgradeInsecure) {
clientSettings.headers = {
...clientSettings.headers,
'Content-Security-Policy': 'upgrade-insecure-requests'
}
}

if (serverSettings.retry !== undefined) {
clientSettings.requestHooks = [getXHRRetryHook(serverSettings.retry)]
}
Expand Down
31 changes: 16 additions & 15 deletions types/dicomweb-client/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ declare module 'dicomweb-client' {
export type DICOMwebClientRequestHook = (request: XMLHttpRequest, metadata: DICOMwebClientRequestHookMetadata) => XMLHttpRequest

export interface DICOMwebClientOptions {
url: string|undefined
url: string | undefined
qidoURLPrefix?: string
wadoURLPrefix?: string
stowURLPrefix?: string
headers?: {
Authorization?: string
'Content-Security-Policy'?: string
}
requestHooks?: DICOMwebClientRequestHook[]
errorInterceptor?: (request: DICOMwebClientError) => void
Expand Down Expand Up @@ -133,49 +134,49 @@ declare module 'dicomweb-client' {
export type Dataset = ArrayBuffer

export interface DICOMwebClient {
headers: {[key: string]: string}
headers: { [key: string]: string }
baseURL: string
// STOW-RS
storeInstances (options: StoreInstancesOptions): Promise<void>
storeInstances(options: StoreInstancesOptions): Promise<void>
// QIDO-RS
searchForStudies (
searchForStudies(
options: SearchForStudiesOptions
): Promise<Study[]>
searchForSeries (
searchForSeries(
options: SearchForSeriesOptions
): Promise<Series[]>
searchForInstances (
searchForInstances(
options: SearchForInstancesOptions
): Promise<Instance[]>
// WADO-RS
retrieveStudyMetadata (
retrieveStudyMetadata(
options: RetrieveStudyMetadataOptions
): Promise<Metadata[]>
retrieveSeriesMetadata (
retrieveSeriesMetadata(
options: RetrieveSeriesMetadataOptions
): Promise<Metadata[]>
retrieveInstanceMetadata (
retrieveInstanceMetadata(
options: RetrieveInstanceMetadataOptions
): Promise<Metadata[]>
retrieveInstance (
retrieveInstance(
options: RetrieveInstanceOptions
): Promise<Dataset>
retrieveInstanceFrames (
retrieveInstanceFrames(
options: RetrieveInstanceFramesOptions
): Promise<Pixeldata[]>
retrieveInstanceRendered (
retrieveInstanceRendered(
options: RetrieveInstanceRenderedOptions
): Promise<Pixeldata>
retrieveInstanceFramesRendered (
retrieveInstanceFramesRendered(
options: RetrieveInstanceFramesRenderedOptions
): Promise<Pixeldata>
retrieveBulkData (
retrieveBulkData(
options: RetrieveBulkDataOptions
): Promise<Bulkdata[]>
}

export class DICOMwebClient implements DICOMwebClient {
constructor (options: DICOMwebClientOptions)
constructor(options: DICOMwebClientOptions)
}

export interface MetadataElement {
Expand Down

0 comments on commit 29cd176

Please sign in to comment.