feat(auth): Add Authorization Code Flow and new Keycloak recipes with new video tutorials (#4234)

Co-authored-by: Alireza <ar.sedghi@gmail.com>
This commit is contained in:
IbrahimandAlireza authored and GitHub committed 2024-07-09 14:33:37 -04:00
1 parent 998302eb85
commit aefa6d94df
81 files changed
+6525 -471

No files matched your search

@@ -0,0 +1,6 @@
logs/*
volumes/*
config/letsencrypt/*
config/certbot/*
!config/letsencrypt/.gitkeep
!config/certbot/.gitkeep
@@ -0,0 +1,7 @@
#!/bin/sh
# Start oauth2-proxy
oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg &
# Start nginx
nginx -g "daemon off;"
@@ -0,0 +1,240 @@
worker_processes auto;
error_log /var/logs/nginx/error.log debug;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include '/etc/nginx/mime.types';
default_type application/octet-stream;
keepalive_timeout 65;
keepalive_requests 100000;
tcp_nopush on;
tcp_nodelay on;
proxy_buffers 16 16k;
proxy_buffer_size 32k;
proxy_busy_buffers_size 64k;
proxy_max_temp_file_size 128k;
gzip on;
gzip_disable "msie6";
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
server {
listen 80;
server_name YOUR_DOMAIN;
client_max_body_size 0;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name YOUR_DOMAIN;
ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem;
root /var/www/html;
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_comp_level 9;
etag on;
location /sw.js {
add_header Cache-Control "no-cache";
proxy_cache_bypass $http_pragma;
proxy_cache_revalidate on;
expires off;
access_log off;
}
location /oauth2 {
expires -1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
proxy_pass http://localhost:4180$uri$is_args$args;
}
location /oauth2/callback {
proxy_pass http://localhost:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /oauth2/sign_out {
expires -1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Auth-Request-Redirect /oauth2/sign_in;
proxy_pass http://localhost:4180;
}
location /pacs/ {
auth_request /oauth2/auth;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
expires 0;
add_header Cache-Control private;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept' always;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
rewrite ^/pacs/(.*) /dcm4chee-arc/aets/DCM4CHEE/rs/$1 break;
proxy_pass http://arc:8080;
}
location /pacs-admin {
return 301 /pacs-admin/;
}
# Redirect /pacs-admin to /dcm4chee-arc/ui2/
location = /pacs-admin {
return 301 $scheme://$host/dcm4chee-arc/ui2/;
}
# Handle /pacs-admin/ requests
location /pacs-admin/ {
return 301 $scheme://$host/dcm4chee-arc/ui2/;
}
# Proxy pass for /dcm4chee-arc/ui2/
location /dcm4chee-arc/ui2/ {
error_page 401 = /oauth2/sign_in?rd=$scheme://$host$request_uri;
auth_request /oauth2/auth?allowed_groups=pacsadmin;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $token $upstream_http_x_auth_request_access_token;
auth_request_set $auth_cookie $upstream_http_set_cookie;
proxy_set_header X-User $user;
proxy_set_header X-Access-Token $token;
add_header Set-Cookie $auth_cookie;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
expires 0;
add_header Cache-Control private;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept' always;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://arc:8080;
}
# Proxy pass for other /dcm4chee-arc/ requests
location /dcm4chee-arc/ {
proxy_pass http://arc:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /pacs {
return 301 /pacs/;
}
location /ohif-viewer/ {
expires -1;
error_page 401 = /oauth2/sign_in?rd=$scheme://$host$request_uri;
auth_request /oauth2/auth;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $token $upstream_http_x_auth_request_access_token;
auth_request_set $auth_cookie $upstream_http_set_cookie;
proxy_set_header X-User $user;
proxy_set_header X-Access-Token $token;
add_header Set-Cookie $auth_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto $scheme;
index index.html;
try_files $uri $uri/ /index.html;
}
location /ohif-viewer {
return 301 /ohif-viewer/;
}
location = / {
return 301 /ohif-viewer/;
}
location / {
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
location /keycloak/ {
proxy_pass http://keycloak:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /keycloak {
return 301 /keycloak/;
}
}
}
@@ -0,0 +1,22 @@
http_address="0.0.0.0:4180"
cookie_secret="GENERATEACOOKIESECRET----------------------="
email_domains=["*"]
cookie_secure="false"
cookie_expire="9m30s"
cookie_refresh="5m"
client_secret="2Xtlde7aozdkzzYHdIxQNfPDr0wNPTgg"
client_id="ohif_viewer"
redirect_url="http://YOUR_DOMAIN/oauth2/callback"
ssl_insecure_skip_verify = true
insecure_oidc_allow_unverified_email = true
pass_access_token = true
provider="keycloak-oidc"
provider_display_name="Keycloak"
user_id_claim="oid"
oidc_email_claim="sub"
scope="openid"
pass_host_header=true
code_challenge_method="S256"
oidc_issuer_url="http://YOUR_DOMAIN/keycloak/realms/ohif"
insecure_oidc_skip_issuer_verification = true
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,161 @@
version: '3.8'
services:
ldap:
image: dcm4che/slapd-dcm4chee:2.6.3-29.0
logging:
driver: json-file
options:
max-size: "10m"
ports:
- "389:389"
env_file: docker-compose.env
volumes:
- ~/dcm4chee-arc/ldap:/var/lib/ldap
- ~/dcm4chee-arc/slapd.d:/etc/ldap/slapd.d
db:
image: dcm4che/postgres-dcm4chee:14.5-29
logging:
driver: json-file
options:
max-size: "10m"
ports:
- "5432:5432"
env_file: docker-compose.env
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ~/dcm4chee-arc/db:/var/lib/postgresql/data
arc:
image: dcm4che/dcm4chee-arc-psql:5.29.0
logging:
driver: json-file
options:
max-size: "10m"
ports:
- "8080:8080"
- "8443:8443"
- "9990:9990"
- "9993:9993"
- "11112:11112"
- "2762:2762"
- "2575:2575"
- "12575:12575"
env_file: docker-compose.env
environment:
WILDFLY_CHOWN: /opt/wildfly/standalone /storage
WILDFLY_WAIT_FOR: ldap:389 db:5432
depends_on:
- ldap
- db
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ~/dcm4chee-arc/wildfly:/opt/wildfly/standalone
- ~/dcm4chee-arc/storage:/storage
ohif_viewer:
build:
context: ./../../../../
dockerfile: ./platform/app/.recipes/Nginx-Dcm4chee-Keycloak/dockerfile
image: webapp:latest
container_name: webapp
ports:
- '443:443' # SSL
- '80:80' # Web
depends_on:
keycloak:
condition: service_healthy
restart: on-failure
networks:
- default
extra_hosts:
- 'host.docker.internal:host-gateway'
environment:
- OAUTH2_PROXY_SKIP_PROVIDER_BUTTON=true
volumes:
- ./config/nginx.conf:/etc/nginx/nginx.conf
- ./config/oauth2-proxy.cfg:/etc/oauth2-proxy/oauth2-proxy.cfg
- ./config/letsencrypt:/etc/letsencrypt
- ./config/certbot:/var/www/certbot
keycloak:
image: quay.io/keycloak/keycloak:24.0.5
command: 'start-dev --import-realm'
hostname: keycloak
container_name: keycloak
volumes:
- ./config/ohif-keycloak-realm.json:/opt/keycloak/data/import/ohif-keycloak-realm.json
environment:
KC_DB_URL_HOST: postgres
KC_DB: postgres
KC_DB_URL: 'jdbc:postgresql://postgres:5432/keycloak'
KC_DB_SCHEMA: public
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: password
KC_HOSTNAME_ADMIN_URL: http://YOUR_DOMAIN/keycloak/
KC_HOSTNAME_URL: http://YOUR_DOMAIN/keycloak/
KC_HOSTNAME_STRICT_BACKCHANNEL: true
KC_HOSTNAME_STRICT_HTTPS: false
KC_HTTP_ENABLED: true
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_HEALTH_ENABLED: true
KC_METRICS_ENABLED: true
KC_PROXY: edge
KC_PROXY_HEADERS: xforwarded
KEYCLOAK_JDBC_PARAMS: connectTimeout=40000
KC_LOG_LEVEL: INFO
KC_HOSTNAME_DEBUG: true
PROXY_ADDRESS_FORWARDING: true
ports:
- 8081:8080
depends_on:
- postgres
restart: unless-stopped
networks:
- default
extra_hosts:
- 'host.docker.internal:host-gateway'
healthcheck:
test:
[
"CMD-SHELL",
"exec 3<>/dev/tcp/YOUR_DOMAIN/8080;echo -e \"GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n\" >&3;grep \"HTTP/1.1 200 OK\" <&3"
]
interval: 1s
timeout: 5s
retries: 10
start_period: 60s
postgres:
image: postgres:15
hostname: postgres
container_name: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
restart: unless-stopped
networks:
- default
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./config/letsencrypt:/etc/letsencrypt
- ./config/certbot:/var/www/certbot
entrypoint: /bin/sh -c "trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;"
volumes:
postgres_data:
driver: local
networks:
default:
driver: bridge
@@ -0,0 +1,50 @@
# Stage 1: Build the application
FROM node:18.16.1-slim as builder
# Setup the working directory
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
# Install dependencies
RUN apt-get update && apt-get install -y build-essential python3
# Copy the entire project
COPY ./ /usr/src/app/
# Install node dependencies
RUN yarn config set workspaces-experimental true
RUN yarn install
# Set the environment for the build
ENV APP_CONFIG=config/docker-nginx-dcm4chee-keycloak.js
# Build the application
RUN yarn run build
# Stage 2: Setup the NGINX environment with OAuth2 Proxy
FROM nginx:alpine
# Install dependencies for oauth2-proxy
RUN apk add --no-cache curl
# Create necessary directories
RUN mkdir -p /var/logs/nginx /var/www/html /etc/oauth2-proxy
# Download and install oauth2-proxy
RUN curl -L https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.4.0/oauth2-proxy-v7.4.0.linux-amd64.tar.gz -o oauth2-proxy.tar.gz && \
tar -xvzf oauth2-proxy.tar.gz && \
mv oauth2-proxy-v7.4.0.linux-amd64/oauth2-proxy /usr/local/bin/ && \
rm -rf oauth2-proxy-v7.4.0.linux-amd64 oauth2-proxy.tar.gz
# Copy the built application
COPY --from=builder /usr/src/app/platform/app/dist /var/www/html
# Copy the entrypoint script
COPY ./platform/app/.recipes/Nginx-Dcm4chee-Keycloak/config/entrypoint.sh /entrypoint.sh
# Expose necessary ports
EXPOSE 80 443 4180
# Set the entrypoint script as the entrypoint
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
@@ -0,0 +1 @@
America/New_York
@@ -0,0 +1,86 @@
worker_processes auto;
error_log /var/logs/nginx/error.log debug;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/logs/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
gzip on;
gzip_disable "msie6";
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
client_max_body_size 0;
# Handle /pacs requests and rewrite them to the correct dcm4chee-arc UI path
# This allows accessing the dcm4chee-arc UI through the /pacs URL
location /pacs {
rewrite ^/pacs(.*)$ /dcm4chee-arc/ui2$1 break;
proxy_pass http://arc:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
expires 0;
add_header Cache-Control private;
}
# Proxy all dcm4chee-arc requests
# This block handles all API requests and general dcm4chee-arc paths
location /dcm4chee-arc/ {
proxy_pass http://arc:8080/dcm4chee-arc/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
}
location /sw.js {
add_header Cache-Control "no-cache";
proxy_cache_bypass $http_pragma;
proxy_cache_revalidate on;
expires off;
access_log off;
}
location / {
root /var/www/html;
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header Cross-Origin-Opener-Policy 'same-origin' always;
add_header Cross-Origin-Embedder-Policy 'require-corp' always;
}
}
}
@@ -0,0 +1,4 @@
STORAGE_DIR=/storage/fs1
POSTGRES_DB=pacsdb
POSTGRES_USER=pacs
POSTGRES_PASSWORD=pacs
@@ -1,50 +1,45 @@
version: '3.5'
services:
ldap:
image: dcm4che/slapd-dcm4chee:2.4.44-15.0
image: dcm4che/slapd-dcm4chee:2.6.3-29.0
logging:
driver: json-file
options:
max-size: '10m'
max-size: "10m"
ports:
- '389:389'
env_file: ./dcm4che/docker-compose-dcm4che.env
- "389:389"
env_file: docker-compose.env
volumes:
- ./dcm4che/etc/localtime:/etc/localtime:ro
- ./dcm4che/etc/timezone:/etc/timezone:ro
- ./dcm4che/dcm4che-arc/ldap:/var/lib/ldap
- ./dcm4che/dcm4che-arc/slapd.d:/etc/ldap/slapd.d
networks:
- dcm4che_default
- ~/dcm4chee-arc/ldap:/var/lib/ldap
- ~/dcm4chee-arc/slapd.d:/etc/ldap/slapd.d
db:
image: dcm4che/postgres-dcm4chee:11.1-15
image: dcm4che/postgres-dcm4chee:14.5-29
logging:
driver: json-file
options:
max-size: '10m'
max-size: "10m"
ports:
- '5432:5432'
env_file: ./dcm4che/docker-compose-dcm4che.env
- "5432:5432"
env_file: docker-compose.env
volumes:
- ./dcm4che/etc/localtime:/etc/localtime:ro
- ./dcm4che/etc/timezone:/etc/timezone:ro
- ./dcm4che/dcm4che-arc/db:/var/lib/postgresql/data
networks:
- dcm4che_default
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ~/dcm4chee-arc/db:/var/lib/postgresql/data
arc:
image: dcm4che/dcm4chee-arc-psql:5.15.0
image: dcm4che/dcm4chee-arc-psql:5.29.0
logging:
driver: json-file
options:
max-size: '10m'
max-size: "10m"
ports:
- '8080:8080'
- '8443:8443'
- '9990:9990'
- '11112:11112'
- '2575:2575'
env_file: ./dcm4che/docker-compose-dcm4che.env
- "8080:8080"
- "8443:8443"
- "9990:9990"
- "9993:9993"
- "11112:11112"
- "2762:2762"
- "2575:2575"
- "12575:12575"
env_file: docker-compose.env
environment:
WILDFLY_CHOWN: /opt/wildfly/standalone /storage
WILDFLY_WAIT_FOR: ldap:389 db:5432
@@ -52,26 +47,27 @@ services:
- ldap
- db
volumes:
- ./dcm4che/etc/localtime:/etc/localtime:ro
- ./dcm4che/etc/timezone:/etc/timezone:ro
- ./dcm4che/dcm4che-arc/wildfly:/opt/wildfly/standalone
- ./dcm4che/dcm4che-arc/storage:/storage
networks:
- dcm4che_default
viewer:
container_name: ohif-viewer
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ~/dcm4chee-arc/wildfly:/opt/wildfly/standalone
- ~/dcm4chee-arc/storage:/storage
ohif_viewer:
build:
context: ../
dockerfile: Dockerfile
# Project root
context: ./../../../../
# Relative to context
dockerfile: ./platform/app/.recipes/Nginx-Dcm4chee/dockerfile
image: webapp:latest
container_name: ohif_dcm4chee
volumes:
# Nginx config
- ./config/nginx.conf:/etc/nginx/nginx.conf
# Logs
- ./logs/nginx:/var/logs/nginx
# Let's Encrypt
# - letsencrypt_certificates:/etc/letsencrypt
# - letsencrypt_challenges:/var/www/letsencrypt
ports:
- '80:80'
# depends_on:
# - orthanc
environment:
- NODE_ENV=production
- APP_CONFIG=config/local_dcm4chee
restart: always
networks:
- dcm4che_default
networks: dcm4che_default:
- '443:443' # SSL
- '80:80' # Web
restart: on-failure
@@ -0,0 +1,43 @@
# Stage 1: Build the application
FROM node:18.16.1-slim as builder
# Setup the working directory
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
# Install dependencies
# apt-get update is combined with apt-get install to avoid using outdated packages
RUN apt-get update && apt-get install -y build-essential python3
# Copy package.json and other dependency-related files first
# Assuming your package.json and yarn.lock or similar are located in the project root
COPY ./ /usr/src/app/
# Install node dependencies
RUN yarn config set workspaces-experimental true
RUN yarn install
# Copy the rest of the application code
# set QUICK_BUILD to true to make the build faster for dev
ENV APP_CONFIG=config/docker-nginx-dcm4chee.js
# Build the application
RUN yarn run build
# # Stage 2: Bundle the built application into a Docker container which runs NGINX using Alpine Linux
FROM nginx:alpine
# # Create directories for logs and html content if they don't already exist
RUN mkdir -p /var/log/nginx /var/www/html
# # Copy build output to serve static files
COPY --from=builder /usr/src/app/platform/app/dist /var/www/html
# # Expose HTTP and HTTPS ports
EXPOSE 80 443
# # Start NGINX
CMD ["nginx", "-g", "daemon off;"]
@@ -1,49 +0,0 @@
events {
worker_connections 4096; ## Default: 1024
}
http {
server {
listen 80 default_server;
server_name localhost;
#
# Wide-open CORS config for nginx
#
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Allow-Headers' 'Authorization';
add_header 'Access-Control-Allow-Credentials' true;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
add_header 'Access-Control-Allow-Headers' 'Authorization';
add_header 'Access-Control-Allow-Credentials' true;
}
proxy_pass http://orthanc:8042;
}
}
}
@@ -0,0 +1,6 @@
logs/*
volumes/*
config/letsencrypt/*
config/certbot/*
!config/letsencrypt/.gitkeep
!config/certbot/.gitkeep
@@ -0,0 +1,7 @@
#!/bin/sh
# Start oauth2-proxy
oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg &
# Start nginx
nginx -g "daemon off;"
@@ -0,0 +1,210 @@
worker_processes 2;
error_log /var/logs/nginx/mydomain.error.log;
pid /var/run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include '/etc/nginx/mime.types';
default_type application/octet-stream;
keepalive_timeout 65;
keepalive_requests 100000;
tcp_nopush on;
tcp_nodelay on;
proxy_buffers 16 16k;
proxy_buffer_size 32k;
proxy_busy_buffers_size 64k;
proxy_max_temp_file_size 128k;
server {
listen 80;
server_name YOUR_DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name YOUR_DOMAIN;
ssl_certificate /etc/letsencrypt/live/ohifviewer.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ohifviewer.duckdns.org/privkey.pem;
root /var/www/html;
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_comp_level 9;
etag on;
location /sw.js {
add_header Cache-Control "no-cache";
proxy_cache_bypass $http_pragma;
proxy_cache_revalidate on;
expires off;
access_log off;
}
location /oauth2 {
expires -1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
proxy_pass http://localhost:4180$uri$is_args$args;
}
location /oauth2/callback {
proxy_pass http://localhost:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /oauth2/sign_out {
expires -1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Auth-Request-Redirect /oauth2/sign_in;
proxy_pass http://localhost:4180;
}
location /pacs-admin/ {
error_page 401 = /oauth2/sign_in?rd=$scheme://$host$request_uri;
auth_request /oauth2/auth?allowed_groups=pacsadmin;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $token $upstream_http_x_auth_request_access_token;
auth_request_set $auth_cookie $upstream_http_set_cookie;
proxy_set_header X-User $user;
proxy_set_header X-Access-Token $token;
add_header Set-Cookie $auth_cookie;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
expires 0;
add_header Cache-Control private;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept' always;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://orthanc:8042/;
}
location /pacs-admin {
return 301 /pacs-admin/;
}
location /pacs/ {
auth_request /oauth2/auth;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
expires 0;
add_header Cache-Control private;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept' always;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Origin, X-Requested-With, Content-Type, Accept';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://orthanc:8042/dicom-web/;
}
location /pacs {
return 301 /pacs/;
}
location /ohif-viewer/ {
expires -1;
error_page 401 = /oauth2/sign_in?rd=$scheme://$host$request_uri;
auth_request /oauth2/auth;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $token $upstream_http_x_auth_request_access_token;
auth_request_set $auth_cookie $upstream_http_set_cookie;
proxy_set_header X-User $user;
proxy_set_header X-Access-Token $token;
add_header Set-Cookie $auth_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto $scheme;
index index.html;
try_files $uri $uri/ /index.html;
}
location /ohif-viewer {
return 301 /ohif-viewer/;
}
location = / {
return 301 /ohif-viewer/;
}
location / {
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
location /keycloak/ {
proxy_pass http://keycloak:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /keycloak {
return 301 /keycloak/;
}
}
}
@@ -0,0 +1,22 @@
http_address="0.0.0.0:4180"
cookie_secret="GENERATEACOOKIESECRET----------------------="
email_domains=["*"]
cookie_secure="false"
cookie_expire="9m30s"
cookie_refresh="5m"
client_secret="2Xtlde7aozdkzzYHdIxQNfPDr0wNPTgg"
client_id="ohif_viewer"
redirect_url="http://YOUR_DOMAIN/oauth2/callback"
ssl_insecure_skip_verify = true
insecure_oidc_allow_unverified_email = true
pass_access_token = true
provider="keycloak-oidc"
provider_display_name="Keycloak"
user_id_claim="oid"
oidc_email_claim="sub"
scope="openid"
pass_host_header=true
code_challenge_method="S256"
oidc_issuer_url="http://YOUR_DOMAIN/keycloak/realms/ohif"
insecure_oidc_skip_issuer_verification = true
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,126 @@
services:
ohif_viewer:
build:
context: ./../../../../
dockerfile: ./platform/app/.recipes/Nginx-Orthanc-Keycloak/dockerfile
image: webapp:latest
container_name: webapp
ports:
- '443:443' # SSL
- '80:80' # Web
depends_on:
keycloak:
condition: service_healthy
restart: on-failure
networks:
- default
extra_hosts:
- 'host.docker.internal:host-gateway'
environment:
- OAUTH2_PROXY_SKIP_PROVIDER_BUTTON=true
volumes:
# - ../../app/dist /var/www/html
- ./config/nginx.conf:/etc/nginx/nginx.conf
- ./config/oauth2-proxy.cfg:/etc/oauth2-proxy/oauth2-proxy.cfg
- ./config/letsencrypt:/etc/letsencrypt
- ./config/certbot:/var/www/certbot
orthanc:
image: jodogne/orthanc-plugins
hostname: orthanc
container_name: orthanc
volumes:
- ./config/orthanc.json:/etc/orthanc/orthanc.json:ro
- ./volumes/orthanc-db/:/var/lib/orthanc/db/
restart: unless-stopped
networks:
- default
keycloak:
image: quay.io/keycloak/keycloak:24.0.5
command: 'start-dev --import-realm'
hostname: keycloak
container_name: keycloak
volumes:
- ./config/ohif-keycloak-realm.json:/opt/keycloak/data/import/ohif-keycloak-realm.json
environment:
# Database
KC_DB_URL_HOST: postgres
KC_DB: postgres
KC_DB_URL: 'jdbc:postgresql://postgres:5432/keycloak'
KC_DB_SCHEMA: public
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: password
KC_HOSTNAME_ADMIN_URL: http://YOUR_DOMAIN/keycloak/
KC_HOSTNAME_URL: http://YOUR_DOMAIN/keycloak/
KC_HOSTNAME_STRICT_BACKCHANNEL: true
KC_HOSTNAME_STRICT_HTTPS: false
KC_HTTP_ENABLED: true
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_HEALTH_ENABLED: true
KC_METRICS_ENABLED: true
KC_PROXY: edge
KC_PROXY_HEADERS: xforwarded
KEYCLOAK_JDBC_PARAMS: connectTimeout=40000
KC_LOG_LEVEL: INFO
KC_HOSTNAME_DEBUG: true
# added later
PROXY_ADDRESS_FORWARDING: true
ports:
- 8080:8080
depends_on:
- postgres
restart: unless-stopped
networks:
- default
extra_hosts:
- 'host.docker.internal:host-gateway'
healthcheck:
test: [
'CMD-SHELL',
"exec 3<>/dev/tcp/YOUR_DOMAIN/8080;echo -e \"GET /health/ready HTTP/1.1\r
host: http://localhost\r
Connection: close\r
\r
\" >&3;grep \"HTTP/1.1 200 OK\" <&3",
]
interval: 1s
timeout: 5s
retries: 10
start_period: 60s
postgres:
image: postgres:15
hostname: postgres
container_name: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
restart: unless-stopped
networks:
- default
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./config/letsencrypt:/etc/letsencrypt
- ./config/certbot:/var/www/certbot
entrypoint:
/bin/sh -c "trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;"
volumes:
postgres_data:
driver: local
networks:
default:
driver: bridge
@@ -0,0 +1,57 @@
# Stage 1: Build the application
FROM node:18.16.1-slim as builder
# Setup the working directory
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
# Install dependencies
# apt-get update is combined with apt-get install to avoid using outdated packages
RUN apt-get update && apt-get install -y build-essential python3
# Copy package.json and other dependency-related files first
# Assuming your package.json and yarn.lock or similar are located in the project root
# Todo: this probably can get improved by copying
# only the package json files and running yarn install before
# copying the rest of the files but having a monorepo setup
# makes this a bit more complicated, i wasn't able to get it working
COPY ./ /usr/src/app/
# Install node dependencies
RUN yarn config set workspaces-experimental true
RUN yarn install
# Copy the rest of the application code
# set QUICK_BUILD to true to make the build faster for dev
ENV APP_CONFIG=config/docker-nginx-orthanc-keycloak.js
# Build the application
RUN yarn run build
# Use nginx as the base image
FROM nginx:alpine
# Install dependencies for oauth2-proxy
RUN apk add --no-cache curl
# Create necessary directories
RUN mkdir -p /var/logs/nginx /var/www/html /etc/oauth2-proxy
# Download and install oauth2-proxy
RUN curl -L https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.4.0/oauth2-proxy-v7.4.0.linux-amd64.tar.gz -o oauth2-proxy.tar.gz && \
tar -xvzf oauth2-proxy.tar.gz && \
mv oauth2-proxy-v7.4.0.linux-amd64/oauth2-proxy /usr/local/bin/ && \
rm -rf oauth2-proxy-v7.4.0.linux-amd64 oauth2-proxy.tar.gz
COPY --from=builder /usr/src/app/platform/app/dist /var/www/html
# Copy the entrypoint script
COPY ./platform/app/.recipes/Nginx-Orthanc-Keycloak/config/entrypoint.sh /entrypoint.sh
# Expose necessary ports
EXPOSE 80 443 4180
# Set the entrypoint script as the entrypoint
RUN chmod +x entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
@@ -0,0 +1,26 @@
# Docker compose files
# Build
Using docker compose you can build the image with the following command:
```bash
docker-compose build
```
# Run
To run the container use the following command:
```bash
docker-compose up
```
# Routes
http://localhost/ -> OHIF
localhost/pacs -> Orthanc
See [here](../../../docs/docs/deployment/nginx--image-archive.md) for more information about this recipe.
@@ -12,20 +12,19 @@ RUN apt-get update && apt-get install -y build-essential python3
# Copy package.json and other dependency-related files first
# Assuming your package.json and yarn.lock or similar are located in the project root
COPY . .
COPY ./ /usr/src/app/
# Install node dependencies
# RUN yarn config set workspaces-experimental true
# RUN yarn install
RUN yarn config set workspaces-experimental true
RUN yarn install
# Copy the rest of the application code
# set QUICK_BUILD to true to make the build faster for dev
ENV APP_CONFIG=config/docker_nginx-orthanc-keycloak.js
ENV APP_CONFIG=config/docker-nginx-orthanc.js
# Build the application
# RUN yarn run build
RUN yarn run build
# # Stage 2: Bundle the built application into a Docker container which runs NGINX using Alpine Linux
FROM nginx:alpine
@@ -35,7 +34,7 @@ RUN mkdir -p /var/log/nginx /var/www/html
# # Copy build output to serve static files
# COPY --from=builder /usr/src/app/platform/app/dist /var/www/html
COPY --from=builder /usr/src/app/platform/app/dist /var/www/html
# # Expose HTTP and HTTPS ports
EXPOSE 80 443
@@ -1,5 +0,0 @@
# Docker ENV and ARG Variables
# ----------------------------
# https://vsupalov.com/docker-arg-env-variable-guide/
#
#
@@ -1,5 +0,0 @@
# Docker ENV and ARG Variables
# ----------------------------
# https://vsupalov.com/docker-arg-env-variable-guide/
#
#
-53
View File
@@ -1,53 +0,0 @@
# Docker compose files
This folder contains docker-compose files used to spin up OHIF-Viewer with
different options such as locally or with any PAS you desire to
## Public Server
#### build
`$ docker-compose -f docker-compose-publicserver.yml build`
#### run
`$ docker-compose -f docker-compose-publicserver.yml up -d`
then, access the application at [http://localhost](http://localhost)
## Local Orthanc
### Build
`$ docker-compose -f docker-compose-orthanc.yml build`
### Run
Starts containers and leaves them running in the background.
`$ docker-compose -f docker-compose-orthanc.yml up -d`
then, access the application at [http://localhost](http://localhost)
**remember that you have to access orthanc application and include your studies
there**
## Local Dcm4chee
#### build
`$ docker-compose -f docker-compose-dcm4chee.yml build`
#### run
`$ docker-compose -f docker-compose-dcm4chee.yml up -d`
then, access the application at [http://localhost](http://localhost)
**remember that you have to access dcm4chee application and include your studies
there** You can use the following command to import your studies into dcm4che
`$ docker run -v {YOUR_STUDY_FOLDER}:/tmp --rm --network=docker_dcm4che_default dcm4che/dcm4che-tools:5.14.0 storescu -cDCM4CHEE@arc:11112 /tmp`
**make sure that your Docker network name is docker_dcm4chee_default or change
it to the right one**
@@ -0,0 +1,89 @@
{
"Name": "Orthanc inside Docker",
"StorageDirectory": "/var/lib/orthanc/db",
"IndexDirectory": "/var/lib/orthanc/db",
"StorageCompression": false,
"MaximumStorageSize": 0,
"MaximumPatientCount": 0,
"LuaScripts": [],
"Plugins": ["/usr/share/orthanc/plugins", "/usr/local/share/orthanc/plugins"],
"ConcurrentJobs": 2,
"HttpServerEnabled": true,
"HttpPort": 8042,
"HttpDescribeErrors": true,
"HttpCompressionEnabled": true,
"DicomServerEnabled": true,
"DicomAet": "ORTHANC",
"DicomCheckCalledAet": false,
"DicomPort": 4242,
"DefaultEncoding": "Latin1",
"DeflatedTransferSyntaxAccepted": true,
"JpegTransferSyntaxAccepted": true,
"Jpeg2000TransferSyntaxAccepted": true,
"JpegLosslessTransferSyntaxAccepted": true,
"JpipTransferSyntaxAccepted": true,
"Mpeg2TransferSyntaxAccepted": true,
"RleTransferSyntaxAccepted": true,
"UnknownSopClassAccepted": false,
"DicomScpTimeout": 30,
"RemoteAccessAllowed": true,
"SslEnabled": false,
"SslCertificate": "certificate.pem",
"AuthenticationEnabled": false,
"RegisteredUsers": {
"test": "test"
},
"DicomModalities": {},
"DicomModalitiesInDatabase": false,
"DicomAlwaysAllowEcho": true,
"DicomAlwaysAllowStore": true,
"DicomCheckModalityHost": false,
"DicomScuTimeout": 10,
"OrthancPeers": {},
"OrthancPeersInDatabase": false,
"HttpProxy": "",
"HttpVerbose": true,
"HttpTimeout": 10,
"HttpsVerifyPeers": true,
"HttpsCACertificates": "",
"UserMetadata": {},
"UserContentType": {},
"StableAge": 60,
"StrictAetComparison": false,
"StoreMD5ForAttachments": true,
"LimitFindResults": 0,
"LimitFindInstances": 0,
"LimitJobs": 10,
"LogExportedResources": false,
"KeepAlive": true,
"TcpNoDelay": true,
"HttpThreadsCount": 50,
"StoreDicom": true,
"DicomAssociationCloseDelay": 5,
"QueryRetrieveSize": 10,
"CaseSensitivePN": false,
"LoadPrivateDictionary": true,
"Dictionary": {},
"SynchronousCMove": true,
"JobsHistorySize": 10,
"SaveJobs": true,
"OverwriteInstances": false,
"MediaArchiveSize": 1,
"StorageAccessOnFind": "Always",
"MetricsEnabled": true,
"DicomWeb": {
"Enable": true,
"Root": "/dicom-web/",
"EnableWado": true,
"WadoRoot": "/wado",
"Host": "127.0.0.1",
"Ssl": false,
"StowMaxInstances": 10,
"StowMaxSize": 10,
"QidoCaseSensitive": false
}
}
+4 -3
View File
@@ -24,13 +24,13 @@
"build:viewer:ci": "cross-env NODE_ENV=production PUBLIC_URL=/ APP_CONFIG=config/netlify.js QUICK_BUILD=false yarn run build",
"build:viewer:qa": "cross-env NODE_ENV=production APP_CONFIG=config/google.js yarn run build",
"build:viewer:demo": "cross-env NODE_ENV=production APP_CONFIG=config/demo.js HTML_TEMPLATE=rollbar.html QUICK_BUILD=false yarn run build",
"build": "node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
"build": "node --max_old_space_size=8096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack serve --config .webpack/webpack.pwa.js",
"dev:no:cache": "cross-env NODE_ENV=development webpack serve --no-cache --config .webpack/webpack.pwa.js",
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack serve --config .webpack/webpack.pwa.js",
"dev:orthanc:no:cache": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack serve --no-cache --config .webpack/webpack.pwa.js",
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker-nginx-orthanc.js webpack serve --config .webpack/webpack.pwa.js",
"dev:orthanc:no:cache": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker-nginx-orthanc.js webpack serve --no-cache --config .webpack/webpack.pwa.js",
"dev:dcm4chee": "cross-env NODE_ENV=development APP_CONFIG=config/local_dcm4chee.js webpack serve --config .webpack/webpack.pwa.js",
"dev:static": "cross-env NODE_ENV=development APP_CONFIG=config/local_static.js webpack serve --config .webpack/webpack.pwa.js",
"dev:viewer": "yarn run dev",
@@ -88,6 +88,7 @@
"i18next-browser-languagedetector": "^3.0.1",
"lodash.isequal": "4.5.0",
"oidc-client": "1.11.5",
"oidc-client-ts": "^3.0.1",
"prop-types": "^15.7.2",
"query-string": "^6.12.1",
"react": "^18.3.1",
@@ -36,19 +36,4 @@ window.config = {
},
],
// This is an array, but we'll only use the first entry for now
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`
// "Authorization Code Flow"
// Resource: https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660
response_type: 'code',
scope: 'openid', // email profile openid
// ~ OPTIONAL
post_logout_redirect_uri: '/logout-redirect.html',
},
],
};
@@ -0,0 +1,35 @@
/** @type {AppTypes.Config} */
window.config = {
routerBasename: '/ohif-viewer/',
showStudyList: true,
customizationService: {
dicomUploadComponent:
'@ohif/extension-cornerstone.customizationModule.cornerstoneDicomUploadComponent',
},
extensions: [],
modes: [],
// below flag is for performance reasons, but it might not work for all servers
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
strictZSpacingForVolumeViewport: true,
defaultDataSourceName: 'dicomweb',
dataSources: [
{
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'Dcm4chee Server',
name: 'Dcm4chee',
wadoUriRoot: 'http://127.0.0.1/pacs',
qidoRoot: 'http://127.0.0.1/pacs',
wadoRoot: 'http://127.0.0.1/pacs',
qidoSupportsIncludeField: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
dicomUploadEnabled: true,
omitQuotationForMultipartRequest: true,
},
},
],
};
@@ -0,0 +1,35 @@
/** @type {AppTypes.Config} */
window.config = {
routerBasename: '/',
showStudyList: true,
customizationService: {
dicomUploadComponent:
'@ohif/extension-cornerstone.customizationModule.cornerstoneDicomUploadComponent',
},
extensions: [],
modes: [],
// below flag is for performance reasons, but it might not work for all servers
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
strictZSpacingForVolumeViewport: true,
defaultDataSourceName: 'dicomweb',
dataSources: [
{
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'Dcm4chee Server',
name: 'Dcm4chee',
wadoUriRoot: '/dcm4chee-arc/aets/DCM4CHEE/wado',
qidoRoot: '/dcm4chee-arc/aets/DCM4CHEE/rs',
wadoRoot: '/dcm4chee-arc/aets/DCM4CHEE/rs',
qidoSupportsIncludeField: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
dicomUploadEnabled: true,
omitQuotationForMultipartRequest: true,
},
},
],
};
@@ -0,0 +1,52 @@
/** @type {AppTypes.Config} */
window.config = {
routerBasename: '/ohif-viewer',
extensions: [],
modes: [],
customizationService: {},
showStudyList: true,
maxNumberOfWebWorkers: 3,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
strictZSpacingForVolumeViewport: true,
groupEnabledModesFirst: true,
maxNumRequests: {
interaction: 100,
thumbnail: 75,
prefetch: 25,
},
defaultDataSourceName: 'dicomweb',
dataSources: [
{
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'Local Orthanc',
name: 'Orthanc',
wadoUriRoot: 'http://127.0.0.1/pacs',
qidoRoot: 'http://127.0.0.1/pacs',
wadoRoot: 'http://127.0.0.1/pacs',
qidoSupportsIncludeField: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
enableStudyLazyLoad: true,
supportsFuzzyMatching: false,
supportsWildcard: true,
staticWado: true,
singlepart: 'bulkdata,video',
// whether the data source should use retrieveBulkData to grab metadata,
// and in case of relative path, what would it be relative to, options
// are in the series level or study level (some servers like series some study)
bulkDataURI: {
enabled: true,
},
omitQuotationForMultipartRequest: true,
},
},
],
httpErrorHandler: error => {
console.warn(error.status);
console.warn('test, navigate to https://ohif.org/');
},
};
@@ -29,8 +29,8 @@ window.config = {
friendlyName: 'Orthanc Server',
name: 'Orthanc',
wadoUriRoot: '/wado',
qidoRoot: '/dicom-web',
wadoRoot: '/dicom-web',
qidoRoot: '/pacs/dicom-web',
wadoRoot: '/pacs/dicom-web',
qidoSupportsIncludeField: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
+1 -1
View File
@@ -24,7 +24,7 @@ import {
UserAuthenticationProvider,
ToolboxProvider,
} from '@ohif/ui';
import { ThemeWrapper as ThemeWrapperNext, NotificationProvider } from '@ohif/ui-next';
import { ThemeWrapper as ThemeWrapperNext } from '@ohif/ui-next';
// Viewer Project
// TODO: Should this influence study list?
import { AppConfigProvider } from '@state';
+7 -5
View File
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
function CallbackPage({ userManager, onRedirectSuccess }) {
@@ -6,10 +6,12 @@ function CallbackPage({ userManager, onRedirectSuccess }) {
throw new Error(error);
};
userManager
.signinRedirectCallback()
.then(user => onRedirectSuccess(user))
.catch(error => onRedirectError(error));
useEffect(() => {
userManager
.signinRedirectCallback()
.then(user => onRedirectSuccess(user))
.catch(error => onRedirectError(error));
}, [userManager, onRedirectSuccess]);
return null;
}
-1
View File
@@ -1,4 +1,3 @@
import React from 'react';
import { useUserAuthentication } from '@ohif/ui';
export const PrivateRoute = ({ children, handleUnauthenticated }) => {
+1 -1
View File
@@ -81,7 +81,7 @@ export default function buildModeRoutes({
routes.push({
path,
children,
private: true, // todo: all mode routes are private for now
private: true,
});
});
+6 -4
View File
@@ -138,18 +138,20 @@ const createRoutes = ({
const { userAuthenticationService } = servicesManager.services;
// Note: PrivateRoutes in react-router-dom 6.x should be defined within
// a Route element
// All routes are private by default and then we let the user auth service
// to check if it is enabled or not
// Todo: I think we can remove the second public return below
return (
<Routes>
{allRoutes.map((route, i) => {
return route.private === true ? (
<Route
key={i}
exact
path={route.path}
element={
<PrivateRoute handleUnauthenticated={() => userAuthenticationService.handleUnauthenticated()}>
<PrivateRoute
handleUnauthenticated={() => userAuthenticationService.handleUnauthenticated()}
>
<RouteWithErrorBoundary route={route} />
</PrivateRoute>
}
+14 -5
View File
@@ -3,7 +3,8 @@ import { useEffect } from 'react';
import { Route, Routes, useLocation, useNavigate } from 'react-router';
import CallbackPage from '../routes/CallbackPage';
import SignoutCallbackComponent from '../routes/SignoutCallbackComponent';
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js';
import LegacyClient from './legacyOIDCClient';
import NextClient from './nextOIDCClient';
function _isAbsoluteUrl(url) {
return url.includes('http://') || url.includes('https://');
@@ -43,7 +44,9 @@ const initUserManager = (oidc, routerBasename) => {
post_logout_redirect_uri: _makeAbsoluteIfNecessary(post_logout_redirect_uri, baseUri),
});
return getUserManagerForOpenIdConnectClient(openIdConnectConfiguration);
const client = firstOpenIdClient.useAuthorizationCodeFlow ? NextClient: LegacyClient
return client(openIdConnectConfiguration);
};
function LogoutComponent(props) {
@@ -147,12 +150,18 @@ function OpenIdConnectRoutes({ oidc, routerBasename, userAuthenticationService }
const location = useLocation();
const { pathname, search } = location;
const redirect_uri = new URL(userManager.settings._redirect_uri).pathname.replace(
const redirectURI = userManager.settings._redirect_uri ?? userManager.settings.redirect_uri;
const silentRedirectURI =
userManager.settings._silent_redirect_uri ?? userManager.settings.silent_redirect_uri;
const postLogoutRedirectURI =
userManager.settings._post_logout_redirect_uri ?? userManager.settings.post_logout_redirect_uri;
const redirect_uri = new URL(redirectURI).pathname.replace(
routerBasename !== '/' ? routerBasename : '',
''
);
const silent_refresh_uri = new URL(userManager.settings._silent_redirect_uri).pathname; //.replace(routerBasename,'')
const post_logout_redirect_uri = new URL(userManager.settings._post_logout_redirect_uri).pathname; //.replace(routerBasename,'');
const silent_refresh_uri = new URL(silentRedirectURI).pathname; //.replace(routerBasename,'')
const post_logout_redirect_uri = new URL(postLogoutRedirectURI).pathname; //.replace(routerBasename,'');
// const pathnameRelative = pathname.replace(routerBasename,'');
+39
View File
@@ -0,0 +1,39 @@
import { UserManager } from 'oidc-client-ts';
/**
* Creates a userManager from oidcSettings
* LINK: https://github.com/IdentityModel/oidc-client-js/wiki#configuration
*
* @param {Object} oidcSettings
* @param {string} oidcSettings.authServerUrl,
* @param {string} oidcSettings.clientId,
* @param {string} oidcSettings.authRedirectUri,
* @param {string} oidcSettings.postLogoutRedirectUri,
* @param {string} oidcSettings.responseType,
* @param {string} oidcSettings.extraQueryParams,
*/
export default function getUserManagerForOpenIdConnectClient(oidcSettings) {
if (!oidcSettings) {
return;
}
if (!oidcSettings.authority || !oidcSettings.client_id || !oidcSettings.redirect_uri) {
console.error('Missing required oidc settings: authority, client_id, redirect_uri');
return;
}
const settings = {
...oidcSettings,
// The next client always use the code flow with PKCE
response_type: 'code',
revokeTokensOnSignout: oidcSettings.revokeAccessTokenOnSignout ?? true,
filterProtocolClaims: true,
loadUserInfo: true,
// the followings are default values in the lib so no need to set them
// automaticSilentRenew: true,
};
const userManager = new UserManager(settings);
return userManager;
}