From 415836f3fc904eb24b46e1d5e30c12f19838b98d Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 12 Feb 2024 11:35:04 -0600 Subject: [PATCH 01/24] allow react sub-route --- frontend/src/app.config.ts | 4 +++ frontend/src/routes.tsx | 48 +++++++++++---------------------- frontend/webpack.config.dev.js | 4 +++ frontend/webpack.config.prod.js | 4 +++ 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/frontend/src/app.config.ts b/frontend/src/app.config.ts index 87a9ef34c..0389d0555 100644 --- a/frontend/src/app.config.ts +++ b/frontend/src/app.config.ts @@ -3,6 +3,7 @@ import { EventListenerJobStatus } from "./types/data"; interface Config { hostname: string; + baseUrlRoute: string; apikey: string; GHIssueBaseURL: string; KeycloakBaseURL: string; @@ -32,10 +33,13 @@ const hostname = process.env.CLOWDER_REMOTE_HOSTNAME || `${window.location.protocol}//${window.location.host}`; +const baseUrlRoute = process.env.BASE_URL_ROUTE || ""; + // TODO when add auth piece remove this env const apikey = process.env.APIKEY || ""; config["hostname"] = hostname; +config["baseUrlRoute"] = baseUrlRoute; config["apikey"] = apikey; V2.OpenAPI.BASE = config.hostname; diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx index 3bab422a1..5b45a3716 100644 --- a/frontend/src/routes.tsx +++ b/frontend/src/routes.tsx @@ -8,9 +8,9 @@ import { useParams, } from "react-router-dom"; import { Dataset as DatasetComponent } from "./components/datasets/Dataset"; -import {PublicDataset as PublicDatasetComponent} from "./components/datasets/PublicDataset"; +import { PublicDataset as PublicDatasetComponent } from "./components/datasets/PublicDataset"; import { File as FileComponent } from "./components/files/File"; -import {PublicFile as PublicFileComponent} from "./components/files/PublicFile"; +import { PublicFile as PublicFileComponent } from "./components/files/PublicFile"; import { CreateDataset } from "./components/datasets/CreateDataset"; import { Groups as GroupListComponent } from "./components/groups/Groups"; import { Group as GroupComponent } from "./components/groups/Group"; @@ -30,7 +30,7 @@ import { resetLogout, } from "./actions/common"; import { Explore } from "./components/Explore"; -import {Public} from "./components/Public"; +import { Public } from "./components/Public"; import { ExtractionHistory } from "./components/listeners/ExtractionHistory"; import { fetchDatasetRole, fetchFileRole } from "./actions/authorization"; import { PageNotFound } from "./components/errors/PageNotFound"; @@ -111,32 +111,21 @@ const PrivateRoute = (props): JSX.Element => { export const AppRoutes = (): JSX.Element => { return ( - + - - } - /> - {isAuthorized()? - ( - - - - } - /> - ) : + } /> + {isAuthorized() ? ( + + + } /> - } + ) : ( + } /> + )} { /> - } + element={} /> { } /> - - } - /> + } /> } /> } /> } /> diff --git a/frontend/webpack.config.dev.js b/frontend/webpack.config.dev.js index 688ca512b..383a5ede5 100644 --- a/frontend/webpack.config.dev.js +++ b/frontend/webpack.config.dev.js @@ -8,6 +8,9 @@ import ESLintPlugin from "eslint-webpack-plugin"; console.log( `the current CLOWDER_REMOTE_HOSTNAME environment variable is ${process.env.CLOWDER_REMOTE_HOSTNAME}` ); +console.log( + `the current BASE_URL_ROUTE environment variable is ${process.env.BASE_URL_ROUTE}` +); export default { mode: "development", @@ -40,6 +43,7 @@ export default { CLOWDER_REMOTE_HOSTNAME: JSON.stringify( process.env.CLOWDER_REMOTE_HOSTNAME ), + BASE_URL_ROUTE: JSON.stringify(process.env.BASE_URL_ROUTE), APIKEY: JSON.stringify(process.env.APIKEY), KeycloakBaseURL: JSON.stringify(process.env.KeycloakBaseURL), }, diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index 8102de89c..f0206c9e2 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -11,6 +11,9 @@ import TerserPlugin from "terser-webpack-plugin"; console.log( `the current CLOWDER_REMOTE_HOSTNAME environment variable is ${process.env.CLOWDER_REMOTE_HOSTNAME}` ); +console.log( + `the current BASE_URL_ROUTE environment variable is ${process.env.BASE_URL_ROUTE}` +); export default { mode: "production", @@ -47,6 +50,7 @@ export default { CLOWDER_REMOTE_HOSTNAME: JSON.stringify( process.env.CLOWDER_REMOTE_HOSTNAME ), + BASE_URL_ROUTE: JSON.stringify(process.env.BASE_URL_ROUTE), APIKEY: JSON.stringify(process.env.APIKEY), KeycloakBaseURL: JSON.stringify(process.env.KeycloakBaseURL), }, From da9a0027f7075bf7c0d0c983ea1ba432f475183e Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 12 Feb 2024 12:15:59 -0600 Subject: [PATCH 02/24] use router link instead of material ui link --- frontend/src/components/Layout.tsx | 6 +-- frontend/src/components/PublicLayout.tsx | 16 ++---- .../src/components/auth/RedirectLogout.tsx | 24 +++++---- frontend/src/components/errors/Forbidden.tsx | 49 +++++++++++-------- .../src/components/errors/PageNotFound.tsx | 49 +++++++++++-------- frontend/src/components/navigation/TopBar.tsx | 7 +-- 6 files changed, 84 insertions(+), 67 deletions(-) diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 3c11f0ede..7115c0115 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -175,7 +175,7 @@ export default function PersistentDrawerLeft(props) { > - + {loggedOut ? ( <> - + Register - + Login diff --git a/frontend/src/components/PublicLayout.tsx b/frontend/src/components/PublicLayout.tsx index 2bffb28e2..1ca6e1c7f 100644 --- a/frontend/src/components/PublicLayout.tsx +++ b/frontend/src/components/PublicLayout.tsx @@ -12,7 +12,6 @@ import IconButton from "@mui/material/IconButton"; import MenuIcon from "@mui/icons-material/Menu"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import SearchDatasetIcon from "@mui/icons-material/Search"; import ListItem from "@mui/material/ListItem"; import ListItemButton from "@mui/material/ListItemButton"; import ListItemIcon from "@mui/material/ListItemIcon"; @@ -21,13 +20,8 @@ import { Link, Menu, MenuItem, MenuList } from "@mui/material"; import { Link as RouterLink, useLocation } from "react-router-dom"; import { useSelector } from "react-redux"; import { RootState } from "../types/data"; -import { AddBox, Explore } from "@material-ui/icons"; -import HistoryIcon from "@mui/icons-material/History"; -import GroupIcon from "@mui/icons-material/Group"; -import Gravatar from "react-gravatar"; +import { Explore } from "@material-ui/icons"; import PersonIcon from "@mui/icons-material/Person"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { getCurrEmail } from "../utils/common"; import VpnKeyIcon from "@mui/icons-material/VpnKey"; import LogoutIcon from "@mui/icons-material/Logout"; import { EmbeddedSearch } from "./search/EmbeddedSearch"; @@ -148,7 +142,7 @@ export default function PersistentDrawerLeft(props) { > - + - + Register - + Login @@ -240,7 +234,7 @@ export default function PersistentDrawerLeft(props) { - + {/*search commented out for now*/} {/**/} diff --git a/frontend/src/components/auth/RedirectLogout.tsx b/frontend/src/components/auth/RedirectLogout.tsx index 6440e7b19..5d5ee5a76 100644 --- a/frontend/src/components/auth/RedirectLogout.tsx +++ b/frontend/src/components/auth/RedirectLogout.tsx @@ -1,10 +1,11 @@ -import React, {useEffect} from "react"; -import {logout} from "../../actions/user"; +import React, { useEffect } from "react"; +import { logout } from "../../actions/user"; import TopBar from "../navigation/TopBar"; -import {useDispatch} from "react-redux"; +import { useDispatch } from "react-redux"; +import { Link as RouterLink } from "react-router-dom"; +import { Link } from "@mui/material"; export const RedirectLogout = (): JSX.Element => { - const dispatch = useDispatch(); const logUserOut = () => dispatch(logout()); @@ -14,12 +15,15 @@ export const RedirectLogout = (): JSX.Element => { return (
- +
-

You have logged out. Log in again.

+

+ You have logged out.{" "} + + Log in again. + +

-
- - ) -} + ); +}; diff --git a/frontend/src/components/errors/Forbidden.tsx b/frontend/src/components/errors/Forbidden.tsx index f54dafcb8..8825e5146 100644 --- a/frontend/src/components/errors/Forbidden.tsx +++ b/frontend/src/components/errors/Forbidden.tsx @@ -1,25 +1,34 @@ import React from "react"; import { Grid, Link, Typography } from "@mui/material"; import Layout from "../Layout"; - +import { Link as RouterLink } from "react-router-dom"; export const Forbidden = (): JSX.Element => { - return ( - - - - Access Denied - You do not have permission to access this resource. - Go back home - - - - ); -} + return ( + + + + + Access Denied + + + You do not have permission to access this resource. + + + Go back{" "} + + home + + + + + + ); +}; diff --git a/frontend/src/components/errors/PageNotFound.tsx b/frontend/src/components/errors/PageNotFound.tsx index 176013efb..1617b41f1 100644 --- a/frontend/src/components/errors/PageNotFound.tsx +++ b/frontend/src/components/errors/PageNotFound.tsx @@ -1,25 +1,34 @@ import React from "react"; import { Grid, Link, Typography } from "@mui/material"; import Layout from "../Layout"; - +import { Link as RouterLink } from "react-router-dom"; export const PageNotFound = (): JSX.Element => { - return ( - - - - Page Not Found - The page you are looking for does not exist. - Go back home - - - - ); -} + return ( + + + + + Page Not Found + + + The page you are looking for does not exist. + + + Go back{" "} + + home + + + + + + ); +}; diff --git a/frontend/src/components/navigation/TopBar.tsx b/frontend/src/components/navigation/TopBar.tsx index c0f1e8f90..ed5b7bf68 100644 --- a/frontend/src/components/navigation/TopBar.tsx +++ b/frontend/src/components/navigation/TopBar.tsx @@ -1,5 +1,6 @@ import React from "react"; import { AppBar, Link, Toolbar } from "@mui/material"; +import { Link as RouterLink } from "react-router-dom"; import Box from "@mui/material/Box"; import { useSelector } from "react-redux"; import { RootState } from "../../types/data"; @@ -40,15 +41,15 @@ export default function TopBar() {
{loggedOut ? ( <> - + Register - + Login ) : ( - + Logout )} From 51259e421d084dc2b6bd83290cfd2cbb48f714b1 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 12 Feb 2024 12:24:03 -0600 Subject: [PATCH 03/24] update docker-compose --- docker-compose.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2687c71fa..fe6d9cab2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,18 +51,18 @@ services: RABBITMQ_USER: ${RABBITMQ_USER:-guest} RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} API_HOST: ${API_HOST:-http://localhost} - elasticsearch_url: http://elasticsearch:9200 - auth_base: http://localhost - auth_realm: clowder - auth_client_id: clowder2-backend - auth_redirect_uri: http://localhost:80/api/v2/auth - auth_url: http://localhost/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code - oauth2_scheme_auth_url: http://keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code - auth_register_url: http://localhost/keycloak/realms/clowder/protocol/openid-connect/registrations?client_id=clowder2-backend&response_type=code - auth_token_url: http://keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/token - auth_server_url: http://keycloak:8080/keycloak/ - keycloak_base: http://localhost/api - frontend_url: http://localhost + elasticsearch_url: ${elasticsearch_url:-http://elasticsearch:9200} + auth_base: ${auth_base:-http://localhost} + auth_realm: ${auth_realm:-clowder} + auth_client_id: ${auth_client_id:-clowder2-backend} + auth_redirect_uri: ${auth_redirect_uri:-http://localhost:80/api/v2/auth} + auth_url: ${auth_url:-http://localhost/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code} + oauth2_scheme_auth_url: ${oauth2_scheme_auth_url:-http://keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code} + auth_register_url: ${auth_register_url:-http://localhost/keycloak/realms/clowder/protocol/openid-connect/registrations?client_id=clowder2-backend&response_type=code} + auth_token_url: ${auth_token_url:-http://keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/token} + auth_server_url: ${auth_server_url:-http://keycloak:8080/keycloak/} + keycloak_base: ${keycloak_base:-http://localhost/api} + frontend_url: ${frontend_url:-http://localhost} depends_on: - mongo - minio-nginx @@ -88,9 +88,10 @@ services: environment: NODE_ENV: production CLOWDER_REMOTE_HOSTNAME: ${CLOWDER_REMOTE_HOSTNAME:-http://localhost} + BASE_URL_ROUTE: ${BASE_URL_ROUTE:-} labels: - "traefik.enable=true" - - "traefik.http.routers.frontend.rule=PathPrefix(`/`)" + - "traefik.http.routers.frontend.rule=PathPrefix(`${BASE_URL_ROUTE}/`)" - "traefik.http.services.frontend.loadbalancer.server.port=80" - "traefik.http.routers.frontend.priority=1" From c02365d9d445b082098ad052c522337826042542 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 12 Feb 2024 12:26:26 -0600 Subject: [PATCH 04/24] add to k8s deployment --- .../charts/clowder2/templates/frontend/deployment.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml b/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml index eab44f6be..fd7a76b15 100644 --- a/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml +++ b/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml @@ -35,6 +35,9 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} + env: + - name: BASE_URL_ROUTE + value: "" ports: - name: http containerPort: 80 From 01557677cc7f7c34d52a23b868d3c03542515d1d Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 11:17:31 -0500 Subject: [PATCH 05/24] change all internal link to use react router dom link --- docker-compose.yml | 3 ++- frontend/clowder.conf | 9 +++++---- frontend/src/components/Public.tsx | 11 ++++++++--- frontend/src/components/auth/RedirectLogout.tsx | 7 ++++++- frontend/src/components/auth/legacy/Login.tsx | 10 +++++++--- frontend/src/components/auth/legacy/Register.tsx | 8 +++++--- frontend/src/components/errors/Forbidden.tsx | 3 ++- frontend/src/components/errors/PageNotFound.tsx | 3 ++- 8 files changed, 37 insertions(+), 17 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 44ec3b1ee..1ac484175 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,7 +77,8 @@ services: - "traefik.http.routers.backend.priority=5" frontend: - image: "clowder/clowder2-frontend:2.0.0-beta.2" +# image: "clowder/clowder2-frontend:2.0.0-beta.2" + image: "clowder/clowder2-frontend:basename" restart: unless-stopped build: context: ./frontend diff --git a/frontend/clowder.conf b/frontend/clowder.conf index 4c373a118..aff058478 100644 --- a/frontend/clowder.conf +++ b/frontend/clowder.conf @@ -3,13 +3,14 @@ server { root /usr/share/nginx/html/; - location / { + location /${BASE_URL_ROUTE:-/}/ { root /usr/share/nginx/html/; index index.html; - try_files $uri /index.html; + try_files $uri ${BASE_URL_ROUTE:-/}/index.html; } - location /public/ { - root /usr/share/nginx/html/; + + location /${BASE_URL_ROUTE}/public/ { + root /usr/share/nginx/html/${BASE_URL_ROUTE:-/}/; } error_page 500 502 503 504 /50x.html; diff --git a/frontend/src/components/Public.tsx b/frontend/src/components/Public.tsx index a0cbecb24..28d6067fe 100644 --- a/frontend/src/components/Public.tsx +++ b/frontend/src/components/Public.tsx @@ -104,9 +104,14 @@ export const Public = (): JSX.Element => {

No public datasets available.{" "} - Login or{" "} - register to create - datasets.{" "} + + Login + {" "} + or{" "} + + register + {" "} + to create datasets.{" "}

)} diff --git a/frontend/src/components/auth/RedirectLogout.tsx b/frontend/src/components/auth/RedirectLogout.tsx index ec71a8cd0..6ce1dbe86 100644 --- a/frontend/src/components/auth/RedirectLogout.tsx +++ b/frontend/src/components/auth/RedirectLogout.tsx @@ -2,6 +2,8 @@ import React, { useEffect } from "react"; import { logout } from "../../actions/user"; import TopBar from "../navigation/TopBar"; import { useDispatch } from "react-redux"; +import { Link } from "@mui/material"; +import { Link as RouterLink } from "react-router-dom"; export const RedirectLogout = (): JSX.Element => { const dispatch = useDispatch(); @@ -16,7 +18,10 @@ export const RedirectLogout = (): JSX.Element => {

- You have logged out. Log in again. + You have logged out.{" "} + + Log in again. +

diff --git a/frontend/src/components/auth/legacy/Login.tsx b/frontend/src/components/auth/legacy/Login.tsx index 818bcfb30..fc6fbe1a9 100644 --- a/frontend/src/components/auth/legacy/Login.tsx +++ b/frontend/src/components/auth/legacy/Login.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, Link as RouterLink } from "react-router-dom"; import { Avatar, Button, @@ -113,8 +113,10 @@ export const Login = (): JSX.Element => { onChange={changePassword} onKeyPress={handleKeyPressed} /> + { > Sign In + { onChange={changePasswordConfirm} /> { Register { @@ -22,7 +23,7 @@ export const Forbidden = (): JSX.Element => { Go back{" "} - + home diff --git a/frontend/src/components/errors/PageNotFound.tsx b/frontend/src/components/errors/PageNotFound.tsx index 62c37d20e..1617b41f1 100644 --- a/frontend/src/components/errors/PageNotFound.tsx +++ b/frontend/src/components/errors/PageNotFound.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Grid, Link, Typography } from "@mui/material"; import Layout from "../Layout"; +import { Link as RouterLink } from "react-router-dom"; export const PageNotFound = (): JSX.Element => { return ( @@ -22,7 +23,7 @@ export const PageNotFound = (): JSX.Element => { Go back{" "} - + home From a8546bd7d6d7fa1579c46442021458892dfb7ea1 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 11:31:58 -0500 Subject: [PATCH 06/24] update dockerfile --- frontend/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 946aa798a..76cffb766 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -3,7 +3,8 @@ # ---------------------------------------------------------------------- FROM node:16.15.1 AS clowder-build -#ENV NODE_ENV=production +# This is Optional +ENV BASE_URL_ROUTE=${BASE_URL_ROUTE} WORKDIR /usr/src/app # development or production From e6794aacc7fcf09d6151685e511dfbb7061b0b6f Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 11:51:07 -0500 Subject: [PATCH 07/24] set default --- docker-compose.yml | 2 +- frontend/Dockerfile | 3 --- frontend/clowder.conf | 8 ++++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1ac484175..d91d1bee3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -92,7 +92,7 @@ services: BASE_URL_ROUTE: ${BASE_URL_ROUTE:-} labels: - "traefik.enable=true" - - "traefik.http.routers.frontend.rule=PathPrefix(`${BASE_URL_ROUTE}/`)" + - "traefik.http.routers.frontend.rule=PathPrefix(`/${BASE_URL_ROUTE:-}`)" - "traefik.http.services.frontend.loadbalancer.server.port=80" - "traefik.http.routers.frontend.priority=1" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 76cffb766..4663285aa 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -3,8 +3,6 @@ # ---------------------------------------------------------------------- FROM node:16.15.1 AS clowder-build -# This is Optional -ENV BASE_URL_ROUTE=${BASE_URL_ROUTE} WORKDIR /usr/src/app # development or production @@ -28,7 +26,6 @@ RUN npm run build # ---------------------------------------------------------------------- FROM nginx:alpine as clowder-runtime - RUN rm -rf /usr/share/nginx/html/ && \ mkdir /usr/share/nginx/html && \ mkdir /usr/share/nginx/html/public \ diff --git a/frontend/clowder.conf b/frontend/clowder.conf index aff058478..ba2f872f2 100644 --- a/frontend/clowder.conf +++ b/frontend/clowder.conf @@ -3,14 +3,14 @@ server { root /usr/share/nginx/html/; - location /${BASE_URL_ROUTE:-/}/ { + location ${BASE_URL_ROUTE:-}/ { root /usr/share/nginx/html/; index index.html; - try_files $uri ${BASE_URL_ROUTE:-/}/index.html; + try_files $uri ${BASE_URL_ROUTE:-}/index.html; } - location /${BASE_URL_ROUTE}/public/ { - root /usr/share/nginx/html/${BASE_URL_ROUTE:-/}/; + location ${BASE_URL_ROUTE:-}/public/ { + root /usr/share/nginx/html/${BASE_URL_ROUTE:-}; } error_page 500 502 503 504 /50x.html; From da787e7f747f68bf4f5c0c7ad50250fe376a4098 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 13:07:36 -0500 Subject: [PATCH 08/24] update dockefile --- frontend/Dockerfile | 26 ++++++++++++++++++-------- frontend/clowder.conf | 20 -------------------- frontend/clowder.conf.template | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 28 deletions(-) delete mode 100644 frontend/clowder.conf create mode 100644 frontend/clowder.conf.template diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 4663285aa..10def3d5e 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -26,12 +26,22 @@ RUN npm run build # ---------------------------------------------------------------------- FROM nginx:alpine as clowder-runtime + +# Set the environment variable with a default value if not provided +ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-/} + +# Adjust the paths based on BASE_URL_ROUTE RUN rm -rf /usr/share/nginx/html/ && \ - mkdir /usr/share/nginx/html && \ - mkdir /usr/share/nginx/html/public \ - mkdir /usr/share/nginx/html/styles - -COPY --from=clowder-build /usr/src/app/dist/ /usr/share/nginx/html/ -COPY src/public /usr/share/nginx/html/public/ -COPY src/styles /usr/share/nginx/html/styles/ -COPY clowder.conf /etc/nginx/conf.d/default.conf + mkdir -p /usr/share/nginx/html${BASE_URL_ROUTE}/public && \ + mkdir -p /usr/share/nginx/html${BASE_URL_ROUTE}/styles + +# Copy the built application from the previous stage +COPY --from=clowder-build /usr/src/app/dist/ /usr/share/nginx/html${BASE_URL_ROUTE}/ +COPY src/public /usr/share/nginx/html${BASE_URL_ROUTE}/public/ +COPY src/styles /usr/share/nginx/html${BASE_URL_ROUTE}/styles/ + +# Copy the NGINX configuration template +COPY clowder.conf.template /etc/nginx/conf.d/default.conf.template + +# Use envsubst to substitute environment variables in the NGINX config template +CMD ["/bin/sh", "-c", "envsubst '${BASE_URL_ROUTE}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"] diff --git a/frontend/clowder.conf b/frontend/clowder.conf deleted file mode 100644 index ba2f872f2..000000000 --- a/frontend/clowder.conf +++ /dev/null @@ -1,20 +0,0 @@ -server { - listen 80; - - root /usr/share/nginx/html/; - - location ${BASE_URL_ROUTE:-}/ { - root /usr/share/nginx/html/; - index index.html; - try_files $uri ${BASE_URL_ROUTE:-}/index.html; - } - - location ${BASE_URL_ROUTE:-}/public/ { - root /usr/share/nginx/html/${BASE_URL_ROUTE:-}; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } -} diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template new file mode 100644 index 000000000..9bc66dc4b --- /dev/null +++ b/frontend/clowder.conf.template @@ -0,0 +1,20 @@ +server { + listen 80; + + root /usr/share/nginx/html; + + location ${BASE_URL_ROUTE}/ { + root /usr/share/nginx/html/; + index index.html; + try_files $uri ${BASE_URL_ROUTE}/index.html; + } + + location ${BASE_URL_ROUTE}/public/ { + root /usr/share/nginx/html; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} From 33ff88ce4f09a0d62569b6f048a1fac0c59564ef Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 13:35:21 -0500 Subject: [PATCH 09/24] need to set build arg --- frontend/Dockerfile | 7 ++++--- frontend/clowder.conf.template | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 10def3d5e..7ecdf9819 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -5,9 +5,8 @@ FROM node:16.15.1 AS clowder-build WORKDIR /usr/src/app -# development or production -#ARG DEPLOY_ENV="" -#ENV DEPLOY_ENV="${DEPLOY_ENV:-}" +ARG BASE_URL_ROUTE +ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-/} # copy only package for caching purposes COPY ["package.json", "package-lock.json*", "./"] @@ -28,7 +27,9 @@ RUN npm run build FROM nginx:alpine as clowder-runtime # Set the environment variable with a default value if not provided +ARG BASE_URL_ROUTE ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-/} +RUN echo "BASE_URL_ROUTE: ${BASE_URL_ROUTE}" # Adjust the paths based on BASE_URL_ROUTE RUN rm -rf /usr/share/nginx/html/ && \ diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template index 9bc66dc4b..235e29317 100644 --- a/frontend/clowder.conf.template +++ b/frontend/clowder.conf.template @@ -10,7 +10,11 @@ server { } location ${BASE_URL_ROUTE}/public/ { - root /usr/share/nginx/html; + root /usr/share/nginx/html/${BASE_URL_ROUTE}; + } + + location ${BASE_URL_ROUTE}/public/ { + root /usr/share/nginx/html/${BASE_URL_ROUTE}; } error_page 500 502 503 504 /50x.html; From cf34a968bf52419c38dc1986c77c8801efa76714 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 14:00:10 -0500 Subject: [PATCH 10/24] handling slash --- frontend/Dockerfile | 5 ++++- frontend/clowder.conf.template | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 7ecdf9819..b372eea66 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -7,6 +7,8 @@ WORKDIR /usr/src/app ARG BASE_URL_ROUTE ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-/} +RUN BASE_URL_ROUTE=${BASE_URL_ROUTE%/} && BASE_URL_ROUTE=${BASE_URL_ROUTE#/} && BASE_URL_ROUTE=/${BASE_URL_ROUTE} +RUN echo "BASE_URL_ROUTE: ${BASE_URL_ROUTE}" # copy only package for caching purposes COPY ["package.json", "package-lock.json*", "./"] @@ -29,10 +31,11 @@ FROM nginx:alpine as clowder-runtime # Set the environment variable with a default value if not provided ARG BASE_URL_ROUTE ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-/} +RUN BASE_URL_ROUTE=${BASE_URL_ROUTE%/} && BASE_URL_ROUTE=${BASE_URL_ROUTE#/} && BASE_URL_ROUTE=/${BASE_URL_ROUTE} RUN echo "BASE_URL_ROUTE: ${BASE_URL_ROUTE}" # Adjust the paths based on BASE_URL_ROUTE -RUN rm -rf /usr/share/nginx/html/ && \ +RUN rm -rf /usr/share/nginx/html/ && \ mkdir -p /usr/share/nginx/html${BASE_URL_ROUTE}/public && \ mkdir -p /usr/share/nginx/html${BASE_URL_ROUTE}/styles diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template index 235e29317..13b7b8809 100644 --- a/frontend/clowder.conf.template +++ b/frontend/clowder.conf.template @@ -10,11 +10,11 @@ server { } location ${BASE_URL_ROUTE}/public/ { - root /usr/share/nginx/html/${BASE_URL_ROUTE}; + root /usr/share/nginx/html${BASE_URL_ROUTE}; } location ${BASE_URL_ROUTE}/public/ { - root /usr/share/nginx/html/${BASE_URL_ROUTE}; + root /usr/share/nginx/html${BASE_URL_ROUTE}; } error_page 500 502 503 504 /50x.html; From 818a5dbec459483c99360eef9763e17f4aea7497 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 14:18:12 -0500 Subject: [PATCH 11/24] fix typo --- frontend/clowder.conf.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template index 13b7b8809..bc430bcc3 100644 --- a/frontend/clowder.conf.template +++ b/frontend/clowder.conf.template @@ -6,14 +6,14 @@ server { location ${BASE_URL_ROUTE}/ { root /usr/share/nginx/html/; index index.html; - try_files $uri ${BASE_URL_ROUTE}/index.html; + try_files $uri $uri/ ${BASE_URL_ROUTE}/index.html; } location ${BASE_URL_ROUTE}/public/ { root /usr/share/nginx/html${BASE_URL_ROUTE}; } - location ${BASE_URL_ROUTE}/public/ { + location ${BASE_URL_ROUTE}/style/ { root /usr/share/nginx/html${BASE_URL_ROUTE}; } From ba56d10889b1d4c214e98da031b797166d881b5f Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 14:26:00 -0500 Subject: [PATCH 12/24] add public path --- frontend/webpack.config.prod.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index d8c2abf43..7f3b4caf1 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -35,6 +35,7 @@ export default { filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", path: path.resolve(__dirname, "dist"), + publicPath: process.env.BASE_URL_ROUTE || "/", // Set the public path for all assets }, plugins: [ // NOTE: `npm run preinstall` currently runs eslint @@ -82,6 +83,7 @@ export default { // Note that you can add custom options here if you need to handle other custom logic in index.html // To track JavaScript errors via TrackJS, sign up for a free trial at TrackJS.com and enter your token below. trackJSToken: "", + publicPath: process.env.BASE_URL_ROUTE || "/", // Ensure paths are prefixed correctly }), new webpack.LoaderOptionsPlugin({ From fb4dba980b07144c4081405eb01399da8c8e45af Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Fri, 16 Aug 2024 17:20:06 -0500 Subject: [PATCH 13/24] routing end with slash --- frontend/src/index.ejs | 2 +- frontend/webpack.config.dev.js | 1 + frontend/webpack.config.prod.js | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/index.ejs b/frontend/src/index.ejs index 3c538c52a..d832b7456 100644 --- a/frontend/src/index.ejs +++ b/frontend/src/index.ejs @@ -26,7 +26,7 @@ rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> - + Clowder v2 diff --git a/frontend/webpack.config.dev.js b/frontend/webpack.config.dev.js index 2edf79110..e7becf6ce 100644 --- a/frontend/webpack.config.dev.js +++ b/frontend/webpack.config.dev.js @@ -64,6 +64,7 @@ export default { collapseWhitespace: true, }, inject: true, + publicPath: process.env.BASE_URL_ROUTE || "", // Ensure paths are prefixed correctly }), new webpack.LoaderOptionsPlugin({ debug: true, diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index 7f3b4caf1..350b1532d 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -35,7 +35,7 @@ export default { filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", path: path.resolve(__dirname, "dist"), - publicPath: process.env.BASE_URL_ROUTE || "/", // Set the public path for all assets + publicPath: process.env.BASE_URL_ROUTE || "", // Set the public path for all assets }, plugins: [ // NOTE: `npm run preinstall` currently runs eslint @@ -83,7 +83,7 @@ export default { // Note that you can add custom options here if you need to handle other custom logic in index.html // To track JavaScript errors via TrackJS, sign up for a free trial at TrackJS.com and enter your token below. trackJSToken: "", - publicPath: process.env.BASE_URL_ROUTE || "/", // Ensure paths are prefixed correctly + publicPath: process.env.BASE_URL_ROUTE || "", // Ensure paths are prefixed correctly }), new webpack.LoaderOptionsPlugin({ From 204a14f5b98859f37a4338ba7f9c5e2c72b31ccd Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 11:27:01 -0500 Subject: [PATCH 14/24] temp --- frontend/src/index.ejs | 2 +- frontend/webpack.config.dev.js | 10 +++++++--- frontend/webpack.config.prod.js | 8 ++++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/frontend/src/index.ejs b/frontend/src/index.ejs index d832b7456..cba500ebd 100644 --- a/frontend/src/index.ejs +++ b/frontend/src/index.ejs @@ -26,7 +26,7 @@ rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> - + Clowder v2 diff --git a/frontend/webpack.config.dev.js b/frontend/webpack.config.dev.js index e7becf6ce..637c42fe9 100644 --- a/frontend/webpack.config.dev.js +++ b/frontend/webpack.config.dev.js @@ -32,7 +32,9 @@ export default { target: "web", output: { path: path.resolve(__dirname, "dist"), - publicPath: "", + publicPath: process.env.BASE_URL_ROUTE + ? `${process.env.BASE_URL_ROUTE}/` + : "/", // Ensure trailing slash filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", }, @@ -58,13 +60,15 @@ export default { new webpack.HotModuleReplacementPlugin(), new HtmlWebpackPlugin({ template: "src/index.ejs", - favicon: "./src/public/favicon.ico", + favicon: "src/public/favicon.ico", minify: { removeComments: true, collapseWhitespace: true, }, inject: true, - publicPath: process.env.BASE_URL_ROUTE || "", // Ensure paths are prefixed correctly + publicPath: process.env.BASE_URL_ROUTE + ? `${process.env.BASE_URL_ROUTE}/` + : "/", // Ensure trailing slash }), new webpack.LoaderOptionsPlugin({ debug: true, diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index 350b1532d..8d6ad237d 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -35,7 +35,9 @@ export default { filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", path: path.resolve(__dirname, "dist"), - publicPath: process.env.BASE_URL_ROUTE || "", // Set the public path for all assets + publicPath: process.env.BASE_URL_ROUTE + ? `${process.env.BASE_URL_ROUTE}/` + : "/", // Ensure trailing slash }, plugins: [ // NOTE: `npm run preinstall` currently runs eslint @@ -83,7 +85,9 @@ export default { // Note that you can add custom options here if you need to handle other custom logic in index.html // To track JavaScript errors via TrackJS, sign up for a free trial at TrackJS.com and enter your token below. trackJSToken: "", - publicPath: process.env.BASE_URL_ROUTE || "", // Ensure paths are prefixed correctly + publicPath: process.env.BASE_URL_ROUTE + ? `${process.env.BASE_URL_ROUTE}/` + : "/", // Ensure trailing slash }), new webpack.LoaderOptionsPlugin({ From 1153545d5b90215b3cd4db9ec20504422241906c Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 12:59:33 -0500 Subject: [PATCH 15/24] dev works now --- frontend/src/index.ejs | 3 +-- frontend/webpack.config.dev.js | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/frontend/src/index.ejs b/frontend/src/index.ejs index cba500ebd..9c16eb0d5 100644 --- a/frontend/src/index.ejs +++ b/frontend/src/index.ejs @@ -26,8 +26,7 @@ rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> - - + Clowder v2 diff --git a/frontend/webpack.config.dev.js b/frontend/webpack.config.dev.js index 637c42fe9..58043ba39 100644 --- a/frontend/webpack.config.dev.js +++ b/frontend/webpack.config.dev.js @@ -32,9 +32,6 @@ export default { target: "web", output: { path: path.resolve(__dirname, "dist"), - publicPath: process.env.BASE_URL_ROUTE - ? `${process.env.BASE_URL_ROUTE}/` - : "/", // Ensure trailing slash filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", }, @@ -66,9 +63,6 @@ export default { collapseWhitespace: true, }, inject: true, - publicPath: process.env.BASE_URL_ROUTE - ? `${process.env.BASE_URL_ROUTE}/` - : "/", // Ensure trailing slash }), new webpack.LoaderOptionsPlugin({ debug: true, From 8f4800c9c569fd133153d9f487f2ff23b1f4f8e2 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 14:25:00 -0500 Subject: [PATCH 16/24] try another pattern --- frontend/Dockerfile | 29 +++++++++++++++-------------- frontend/clowder.conf.template | 16 ++++------------ frontend/src/index.ejs | 2 +- frontend/webpack.config.prod.js | 8 +------- 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index b372eea66..729b6b0b8 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -5,10 +5,11 @@ FROM node:16.15.1 AS clowder-build WORKDIR /usr/src/app -ARG BASE_URL_ROUTE -ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-/} -RUN BASE_URL_ROUTE=${BASE_URL_ROUTE%/} && BASE_URL_ROUTE=${BASE_URL_ROUTE#/} && BASE_URL_ROUTE=/${BASE_URL_ROUTE} -RUN echo "BASE_URL_ROUTE: ${BASE_URL_ROUTE}" +# Define the argument and environment variable +# Make sure BASE_URL_ROUTE starts with a slash and does not end with a slash +ARG BASE_URL_ROUTE="" +ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-} +RUN echo "Build time BASE_URL_ROUTE: ${BASE_URL_ROUTE}" # copy only package for caching purposes COPY ["package.json", "package-lock.json*", "./"] @@ -28,21 +29,21 @@ RUN npm run build FROM nginx:alpine as clowder-runtime -# Set the environment variable with a default value if not provided -ARG BASE_URL_ROUTE -ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-/} -RUN BASE_URL_ROUTE=${BASE_URL_ROUTE%/} && BASE_URL_ROUTE=${BASE_URL_ROUTE#/} && BASE_URL_ROUTE=/${BASE_URL_ROUTE} -RUN echo "BASE_URL_ROUTE: ${BASE_URL_ROUTE}" +# Define the argument and environment variable +# Make sure BASE_URL_ROUTE starts with a slash and does not end with a slash +ARG BASE_URL_ROUTE="" +ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-} +RUN echo "Runtime BASE_URL_ROUTE: ${BASE_URL_ROUTE}" # Adjust the paths based on BASE_URL_ROUTE RUN rm -rf /usr/share/nginx/html/ && \ - mkdir -p /usr/share/nginx/html${BASE_URL_ROUTE}/public && \ - mkdir -p /usr/share/nginx/html${BASE_URL_ROUTE}/styles + mkdir -p /usr/share/nginx/html/${BASE_URL_ROUTE}/public && \ + mkdir -p /usr/share/nginx/html/${BASE_URL_ROUTE}/styles # Copy the built application from the previous stage -COPY --from=clowder-build /usr/src/app/dist/ /usr/share/nginx/html${BASE_URL_ROUTE}/ -COPY src/public /usr/share/nginx/html${BASE_URL_ROUTE}/public/ -COPY src/styles /usr/share/nginx/html${BASE_URL_ROUTE}/styles/ +COPY --from=clowder-build /usr/src/app/dist/ /usr/share/nginx/html/${BASE_URL_ROUTE} +COPY src/public /usr/share/nginx/html/${BASE_URL_ROUTE}/public/ +COPY src/styles /usr/share/nginx/html/${BASE_URL_ROUTE}/styles/ # Copy the NGINX configuration template COPY clowder.conf.template /etc/nginx/conf.d/default.conf.template diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template index bc430bcc3..670e43995 100644 --- a/frontend/clowder.conf.template +++ b/frontend/clowder.conf.template @@ -3,18 +3,10 @@ server { root /usr/share/nginx/html; - location ${BASE_URL_ROUTE}/ { - root /usr/share/nginx/html/; - index index.html; - try_files $uri $uri/ ${BASE_URL_ROUTE}/index.html; - } - - location ${BASE_URL_ROUTE}/public/ { - root /usr/share/nginx/html${BASE_URL_ROUTE}; - } - - location ${BASE_URL_ROUTE}/style/ { - root /usr/share/nginx/html${BASE_URL_ROUTE}; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri ${BASE_URL_ROUTE}/index.html; } error_page 500 502 503 504 /50x.html; diff --git a/frontend/src/index.ejs b/frontend/src/index.ejs index 9c16eb0d5..5bfebe72f 100644 --- a/frontend/src/index.ejs +++ b/frontend/src/index.ejs @@ -26,7 +26,7 @@ rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> - + Clowder v2 diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index 8d6ad237d..fcdee8db0 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -35,9 +35,6 @@ export default { filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", path: path.resolve(__dirname, "dist"), - publicPath: process.env.BASE_URL_ROUTE - ? `${process.env.BASE_URL_ROUTE}/` - : "/", // Ensure trailing slash }, plugins: [ // NOTE: `npm run preinstall` currently runs eslint @@ -68,7 +65,7 @@ export default { // Generate HTML file that contains references to generated bundles. See here for how this works: https://github.com/ampedandwired/html-webpack-plugin#basic-usage new HtmlWebpackPlugin({ template: "src/index.ejs", - favicon: "./src/public/favicon.ico", + favicon: `src/public/favicon.ico`, minify: { removeComments: true, collapseWhitespace: true, @@ -85,9 +82,6 @@ export default { // Note that you can add custom options here if you need to handle other custom logic in index.html // To track JavaScript errors via TrackJS, sign up for a free trial at TrackJS.com and enter your token below. trackJSToken: "", - publicPath: process.env.BASE_URL_ROUTE - ? `${process.env.BASE_URL_ROUTE}/` - : "/", // Ensure trailing slash }), new webpack.LoaderOptionsPlugin({ From 757fdc51657e6d84b196175be2460a04bfa3142c Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 14:40:56 -0500 Subject: [PATCH 17/24] try new pattern updated --- frontend/clowder.conf.template | 25 +++++++++++++------------ frontend/webpack.config.prod.js | 1 + 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template index 670e43995..220d87395 100644 --- a/frontend/clowder.conf.template +++ b/frontend/clowder.conf.template @@ -1,16 +1,17 @@ server { - listen 80; + listen 80; + server_name localhost; - root /usr/share/nginx/html; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri ${BASE_URL_ROUTE}/index.html; + } - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri ${BASE_URL_ROUTE}/index.html; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } } diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index fcdee8db0..c35bb969e 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -35,6 +35,7 @@ export default { filename: "[name].bundle.js", chunkFilename: "[name].chunk.bundle.js", path: path.resolve(__dirname, "dist"), + publicPath: "", }, plugins: [ // NOTE: `npm run preinstall` currently runs eslint From c160ecb74681769ba46132f4632af7d4736994b4 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 14:57:53 -0500 Subject: [PATCH 18/24] finally it should work --- frontend/clowder.conf.template | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template index 220d87395..6b6d9d52c 100644 --- a/frontend/clowder.conf.template +++ b/frontend/clowder.conf.template @@ -2,10 +2,10 @@ server { listen 80; server_name localhost; - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri ${BASE_URL_ROUTE}/index.html; + location / { + root /usr/share/nginx/html/${BASE_URL_ROUTE}; + index index.html; + try_files $uri $uri/ /index.html; } # redirect server error pages to the static page /50x.html From 80e1a082853f4e888e340814a0c4efbd15ef7b4e Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 15:37:07 -0500 Subject: [PATCH 19/24] the frontend setting is finally working --- docker-compose.yml | 6 ++---- frontend/clowder.conf.template | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d91d1bee3..f74ad11b6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,8 +77,7 @@ services: - "traefik.http.routers.backend.priority=5" frontend: -# image: "clowder/clowder2-frontend:2.0.0-beta.2" - image: "clowder/clowder2-frontend:basename" + image: "clowder/clowder2-frontend:2.0.0-beta.2" restart: unless-stopped build: context: ./frontend @@ -89,10 +88,9 @@ services: environment: NODE_ENV: production CLOWDER_REMOTE_HOSTNAME: ${CLOWDER_REMOTE_HOSTNAME:-http://localhost} - BASE_URL_ROUTE: ${BASE_URL_ROUTE:-} labels: - "traefik.enable=true" - - "traefik.http.routers.frontend.rule=PathPrefix(`/${BASE_URL_ROUTE:-}`)" + - "traefik.http.routers.frontend.rule=PathPrefix(`/`)" - "traefik.http.services.frontend.loadbalancer.server.port=80" - "traefik.http.routers.frontend.priority=1" diff --git a/frontend/clowder.conf.template b/frontend/clowder.conf.template index 6b6d9d52c..e12a685e7 100644 --- a/frontend/clowder.conf.template +++ b/frontend/clowder.conf.template @@ -12,6 +12,6 @@ server { # error_page 500 502 503 504 /50x.html; location = /50x.html { - root /usr/share/nginx/html; + root /usr/share/nginx/html/${BASE_URL_ROUTE}; } } From 98753683dc04cf30209c9967f3f1d8497bfde810 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 16:11:26 -0500 Subject: [PATCH 20/24] final update --- .../charts/clowder2/templates/frontend/deployment.yaml | 3 --- docker-compose.yml | 7 +++++-- frontend/Dockerfile | 5 +++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml b/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml index fd7a76b15..eab44f6be 100644 --- a/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml +++ b/deployments/kubernetes/charts/clowder2/templates/frontend/deployment.yaml @@ -35,9 +35,6 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} - env: - - name: BASE_URL_ROUTE - value: "" ports: - name: http containerPort: 80 diff --git a/docker-compose.yml b/docker-compose.yml index f74ad11b6..008b42fd6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,9 @@ services: auth_server_url: http://keycloak:8080/keycloak/ keycloak_base: http://localhost/api frontend_url: http://localhost +# For deploy to subdirectory + BASE_URL_ROUTE: ${BASE_URL_ROUTE:-} + API_V2_STR: "/${BASE_URL_ROUTE}/api/v2" depends_on: - mongo - minio-nginx @@ -71,8 +74,8 @@ services: - rabbitmq labels: - "traefik.enable=true" - - "traefik.http.routers.backend.rule=PathPrefix(`/api`)" - - "traefik.http.routers.swagger.rule=PathPrefix(`/docs`)" + - "traefik.http.routers.backend.rule=PathPrefix(`${BASE_URL_ROUTE}/api`)" + - "traefik.http.routers.swagger.rule=PathPrefix(`${BASE_URL_ROUTE}/docs`)" - "traefik.http.services.backend.loadbalancer.server.port=80" - "traefik.http.routers.backend.priority=5" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 729b6b0b8..0596c10c8 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -11,6 +11,11 @@ ARG BASE_URL_ROUTE="" ENV BASE_URL_ROUTE=${BASE_URL_ROUTE:-} RUN echo "Build time BASE_URL_ROUTE: ${BASE_URL_ROUTE}" +# Add the build argument for CLOWDER_REMOTE_HOSTNAME to work with backend at a different host +ARG CLOWDER_REMOTE_HOSTNAME="" +ENV CLOWDER_REMOTE_HOSTNAME=${CLOWDER_REMOTE_HOSTNAME:-} +RUN echo "Build time CLOWDER_REMOTE_HOSTNAME: ${CLOWDER_REMOTE_HOSTNAME}" + # copy only package for caching purposes COPY ["package.json", "package-lock.json*", "./"] COPY tools/ /usr/src/app/tools/ From bb308864696a7e5bce9cbc31aa858d2ea0adcdb0 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 17:09:22 -0500 Subject: [PATCH 21/24] write down instructions --- backend/app/main.py | 3 ++ docker-compose.yml | 2 +- docs/docs/admins/deployatsubdirectory.md | 57 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 docs/docs/admins/deployatsubdirectory.md diff --git a/backend/app/main.py b/backend/app/main.py index 84f54d38b..ee7d88a70 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -261,6 +261,9 @@ ) api_router.include_router(status.router, prefix="/status", tags=["status"]) api_router.include_router(keycloak.router, prefix="/auth", tags=["auth"]) + +if not settings.API_V2_STR.startswith("/"): + settings.API_V2_STR = "/" + settings.API_V2_STR app.include_router(api_router, prefix=settings.API_V2_STR) diff --git a/docker-compose.yml b/docker-compose.yml index 008b42fd6..7bbb02a55 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,7 +65,7 @@ services: frontend_url: http://localhost # For deploy to subdirectory BASE_URL_ROUTE: ${BASE_URL_ROUTE:-} - API_V2_STR: "/${BASE_URL_ROUTE}/api/v2" + API_V2_STR: "${BASE_URL_ROUTE}/api/v2" depends_on: - mongo - minio-nginx diff --git a/docs/docs/admins/deployatsubdirectory.md b/docs/docs/admins/deployatsubdirectory.md new file mode 100644 index 000000000..0dfec3901 --- /dev/null +++ b/docs/docs/admins/deployatsubdirectory.md @@ -0,0 +1,57 @@ +# Deploying Application to Subdirectory "clowder" + +This guide will walk you through the steps to deploy your frontend and backend applications to the +subdirectory `/clowder`, ensuring that both the frontend and backend are accessible under this path. + +## Step 1: Build the Frontend Image + +To deploy the frontend application under `/clowder`, build the Docker image with the appropriate build arguments: + +```bash +docker build --no-cache -t clowder/clowder2-frontend:basename \ + --build-arg BASE_URL_ROUTE=clowder \ + --build-arg CLOWDER_REMOTE_HOSTNAME="http://localhost/clowder" \ + . +``` + +- **`BASE_URL_ROUTE=clowder`**: Configures the frontend to operate under the `/clowder` subdirectory. +- **`CLOWDER_REMOTE_HOSTNAME=clowder`**: Point to the backend service that frontend will request, which should match + the subdirectory "/clowder" as well. + +## Step 2: Modify `docker-compose.yml` to Use the Built Image + +In your `docker-compose.yml`, update the frontend service to use the newly built image: + +```yaml +frontend: + image: "clowder/clowder2-frontend:basename" +``` + +This ensures that the correct image, configured for the `/clowder` subdirectory, is used when deploying. + +## Step 3: Set the `BASE_URL_ROUTE` Environment Variable + +Before running Docker Compose, make sure the `BASE_URL_ROUTE` environment variable is set to `clowder`. This ensures +that both the frontend and backend services are configured to operate under the `/clowder` subdirectory. + +```bash +export BASE_URL_ROUTE=clowder +echo $BASE_URL_ROUTE +``` + +- This environment variable ensures that the backend is also aware that it needs to operate under the `/clowder` + subdirectory. + +## Step 4: Start the Services with Docker Compose + +Now, start your services using Docker Compose: + +```bash +docker-compose up +``` + +- **Backend**: The backend service will be accessible at `http://localhost/clowder/api/v2`. +- **Frontend**: The frontend service will be accessible at `http://localhost/clowder`. + +By following these steps, both your backend and frontend services will be correctly deployed under the `/clowder` +subdirectory, making them accessible through the configured paths. From c3d4c56c68069990c959fe5501f20b2ac9606ac3 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 17:31:54 -0500 Subject: [PATCH 22/24] backend not working --- backend/app/main.py | 6 +++--- docker-compose.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/app/main.py b/backend/app/main.py index ee7d88a70..2aaaaeb9d 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -80,6 +80,9 @@ logger = logging.getLogger(__name__) +if not settings.API_V2_STR.startswith("/"): + settings.API_V2_STR = "/" + settings.API_V2_STR + app = FastAPI( title=settings.APP_NAME, openapi_url=f"{settings.API_V2_STR}/openapi.json", @@ -261,9 +264,6 @@ ) api_router.include_router(status.router, prefix="/status", tags=["status"]) api_router.include_router(keycloak.router, prefix="/auth", tags=["auth"]) - -if not settings.API_V2_STR.startswith("/"): - settings.API_V2_STR = "/" + settings.API_V2_STR app.include_router(api_router, prefix=settings.API_V2_STR) diff --git a/docker-compose.yml b/docker-compose.yml index 7bbb02a55..637d66832 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -74,8 +74,8 @@ services: - rabbitmq labels: - "traefik.enable=true" - - "traefik.http.routers.backend.rule=PathPrefix(`${BASE_URL_ROUTE}/api`)" - - "traefik.http.routers.swagger.rule=PathPrefix(`${BASE_URL_ROUTE}/docs`)" + - "traefik.http.routers.backend.rule=PathPrefix(`/api`)" + - "traefik.http.routers.swagger.rule=PathPrefix(`/docs`)" - "traefik.http.services.backend.loadbalancer.server.port=80" - "traefik.http.routers.backend.priority=5" From 9bf6febd297fcc833e35388a9eb06e00104e82a6 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 17:53:14 -0500 Subject: [PATCH 23/24] seperate out subdirectory deployment docker compose --- docker-compose.subdirectory.yml | 271 ++++++++++++++++++++++++++++++++ docker-compose.yml | 3 - 2 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 docker-compose.subdirectory.yml diff --git a/docker-compose.subdirectory.yml b/docker-compose.subdirectory.yml new file mode 100644 index 000000000..c34f5930e --- /dev/null +++ b/docker-compose.subdirectory.yml @@ -0,0 +1,271 @@ +version: '3.7' + +# Settings and configurations that are common for all minio containers +x-minio-common: &minio-common + image: quay.io/minio/minio:RELEASE.2022-01-25T19-56-04Z + command: server --console-address ":9001" http://minio{1...4}/data + restart: unless-stopped + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + networks: + - clowder2 + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ] + interval: 30s + timeout: 20s + retries: 3 + +services: + + traefik: + image: traefik:v2.5 + restart: unless-stopped + command: + - --api.insecure=true + - --providers.docker + # - --entryPoints.web.address=:80 + ports: + # The HTTP port + - "80:80" + # The Web UI (enabled by --api.insecure=true) + - "8080:8080" + networks: + - clowder2 + volumes: + # So that Traefik can listen to the Docker events + - /var/run/docker.sock:/var/run/docker.sock + + backend: + image: 'clowder/clowder2-backend:2.0.0-beta.2' + restart: unless-stopped + build: + context: ./backend + networks: + - clowder2 + environment: + MONGODB_URL: mongodb://mongo:27017 + MINIO_SERVER_URL: minio-nginx:9000 + MINIO_EXTERNAL_SERVER_URL: minio-nginx:9000 + RABBITMQ_HOST: ${RABBITMQ_HOST:-rabbitmq} + RABBITMQ_USER: ${RABBITMQ_USER:-guest} + RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} + API_HOST: ${API_HOST:-http://backend} + elasticsearch_url: http://elasticsearch:9200 + auth_base: http://localhost + auth_realm: clowder + auth_client_id: clowder2-backend + auth_redirect_uri: http://localhost:80/${BASE_URL_ROUTE}/api/v2/auth + auth_url: http://localhost/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code + oauth2_scheme_auth_url: http://${BASE_URL_ROUTE}/keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code + auth_register_url: http://localhost/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/registrations?client_id=clowder2-backend&response_type=code&redirect_uri=http://localhost:80/api/v2/auth&scope=openid%20email + auth_token_url: http://keycloak:8080/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/token + auth_server_url: http://keycloak:8080/${BASE_URL_ROUTE}/keycloak/ + keycloak_base: http://localhost/${BASE_URL_ROUTE}/api + frontend_url: http://localhost/${BASE_URL_ROUTE} + API_V2_STR: /${BASE_URL_ROUTE}/api/v2 + depends_on: + - mongo + - minio-nginx + - keycloak + - elasticsearch + - rabbitmq + labels: + - "traefik.enable=true" + - "traefik.http.routers.backend.rule=PathPrefix(`/${BASE_URL_ROUTE}/api`)" + - "traefik.http.routers.swagger.rule=PathPrefix(`/${BASE_URL_ROUTE}/docs`)" + - "traefik.http.services.backend.loadbalancer.server.port=80" + - "traefik.http.routers.backend.priority=5" + + frontend: + image: "clowder/clowder2-frontend:basename" + restart: unless-stopped + build: + context: ./frontend + networks: + - clowder2 + depends_on: + - backend + environment: + NODE_ENV: production + labels: + - "traefik.enable=true" + - "traefik.http.routers.frontend.rule=PathPrefix(`/`)" + - "traefik.http.services.frontend.loadbalancer.server.port=80" + - "traefik.http.routers.frontend.priority=1" + + extractors-heartbeat: + image: "clowder/clowder2-heartbeat:main" + build: + context: backend + dockerfile: heartbeat.Dockerfile + networks: + - clowder2 + restart: unless-stopped + environment: + MONGODB_URL: mongodb://mongo:27017 + RABBITMQ_HOST: ${RABBITMQ_HOST:-rabbitmq} + RABBITMQ_USER: ${RABBITMQ_USER:-guest} + RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} + depends_on: + - mongo + - rabbitmq + + extractors-messages: + image: "clowder/clowder2-messages:main" + build: + context: backend + dockerfile: messages.Dockerfile + environment: + MONGODB_URL: mongodb://mongo:27017 + RABBITMQ_HOST: ${RABBITMQ_HOST:-rabbitmq} + RABBITMQ_USER: ${RABBITMQ_USER:-guest} + RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} + networks: + - clowder2 + restart: unless-stopped + depends_on: + - mongo + - rabbitmq + + mongo: + image: mongo:5.0 + restart: unless-stopped + networks: + - clowder2 + volumes: + - mongo:/data/db + + # environment: + # MONGO_INITDB_ROOT_USERNAME: root + # MONGO_INITDB_ROOT_PASSWORD: example + + minio1: + <<: *minio-common + hostname: minio1 + volumes: + - data1:/data + + minio2: + <<: *minio-common + hostname: minio2 + volumes: + - data2:/data + + minio3: + <<: *minio-common + hostname: minio3 + volumes: + - data3:/data + + minio4: + <<: *minio-common + hostname: minio4 + volumes: + - data4:/data + + minio-nginx: + image: nginx:1.19.2-alpine + restart: unless-stopped + hostname: nginx + networks: + - clowder2 + volumes: + - ./deployments/docker/minio-nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + - minio1 + - minio2 + - minio3 + - minio4 + + postgres: + image: postgres + restart: unless-stopped + networks: + - clowder2 + volumes: + - postgres_data:/var/lib/postgresql/data + environment: + POSTGRES_DB: keycloak_prod + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: password + + keycloak: + image: quay.io/keycloak/keycloak:20.0 + restart: unless-stopped + networks: + - clowder2 + volumes: + - ./scripts/keycloak/clowder-realm-prod.json:/opt/keycloak/data/import/realm.json:ro + - ./scripts/keycloak/clowder-theme/:/opt/keycloak/themes/clowder-theme/:ro + command: + - start-dev + - --http-relative-path /${BASE_URL_ROUTE}/keycloak + - --import-realm + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + KC_DB: postgres + KC_DB_URL_HOST: postgres + KC_DB_URL_DATABASE: keycloak_prod + KC_DB_USERNAME: keycloak + KC_DB_PASSWORD: password + depends_on: + - postgres + labels: + - "traefik.enable=true" + - "traefik.http.routers.keycloak.rule=PathPrefix(`/${BASE_URL_ROUTE}/keycloak`)" + - "traefik.http.services.keycloak.loadbalancer.server.port=8080" + - "traefik.http.routers.keycloak.priority=10" + + # message broker + rabbitmq: + image: rabbitmq:3-management-alpine + environment: + - RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=-rabbitmq_management path_prefix "/rabbitmq" + - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER:-guest} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASS:-guest} + expose: + - 5672 + - 15672 + # TODO remove + ports: + - "5672:5672" + - "15672:15672" + healthcheck: + test: [ "CMD", "nc", "-z", "localhost", "5672" ] + interval: 3s + timeout: 10s + retries: 3 + networks: + - clowder2 + volumes: + - rabbitmq:/var/lib/rabbitmq + + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.3.3 + restart: unless-stopped + networks: + - clowder2 + environment: + - "cluster.name=clowder2" + - "discovery.type=single-node" + - "xpack.security.enabled=false" + - "xpack.security.http.ssl.enabled=false" + volumes: + - elasticsearch:/usr/share/elasticsearch/data + +## By default this config uses default local driver, +## For custom volumes replace with volume driver configuration. +volumes: + mongo: + data1: + data2: + data3: + data4: + postgres_data: + elasticsearch: + rabbitmq: + +networks: + clowder2: diff --git a/docker-compose.yml b/docker-compose.yml index 637d66832..f74ad11b6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,9 +63,6 @@ services: auth_server_url: http://keycloak:8080/keycloak/ keycloak_base: http://localhost/api frontend_url: http://localhost -# For deploy to subdirectory - BASE_URL_ROUTE: ${BASE_URL_ROUTE:-} - API_V2_STR: "${BASE_URL_ROUTE}/api/v2" depends_on: - mongo - minio-nginx From bff1682a681a28a792a9ab4f2fb0c6a51f58f5c3 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 19 Aug 2024 18:09:41 -0500 Subject: [PATCH 24/24] configure keycloak --- docker-compose.subdirectory.yml | 6 +- docs/docs/admins/deployatsubdirectory.md | 34 +- .../clowder-realm-subdirectory-example.json | 2191 +++++++++++++++++ 3 files changed, 2224 insertions(+), 7 deletions(-) create mode 100644 scripts/keycloak/clowder-realm-subdirectory-example.json diff --git a/docker-compose.subdirectory.yml b/docker-compose.subdirectory.yml index c34f5930e..a71744f1e 100644 --- a/docker-compose.subdirectory.yml +++ b/docker-compose.subdirectory.yml @@ -52,12 +52,12 @@ services: RABBITMQ_PASS: ${RABBITMQ_PASS:-guest} API_HOST: ${API_HOST:-http://backend} elasticsearch_url: http://elasticsearch:9200 - auth_base: http://localhost + auth_base: http://localhost/${BASE_URL_ROUTE} auth_realm: clowder auth_client_id: clowder2-backend auth_redirect_uri: http://localhost:80/${BASE_URL_ROUTE}/api/v2/auth auth_url: http://localhost/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code - oauth2_scheme_auth_url: http://${BASE_URL_ROUTE}/keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code + oauth2_scheme_auth_url: http://keycloak:8080/keycloak/realms/clowder/protocol/openid-connect/auth?client_id=clowder2-backend&response_type=code auth_register_url: http://localhost/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/registrations?client_id=clowder2-backend&response_type=code&redirect_uri=http://localhost:80/api/v2/auth&scope=openid%20email auth_token_url: http://keycloak:8080/${BASE_URL_ROUTE}/keycloak/realms/clowder/protocol/openid-connect/token auth_server_url: http://keycloak:8080/${BASE_URL_ROUTE}/keycloak/ @@ -196,7 +196,7 @@ services: networks: - clowder2 volumes: - - ./scripts/keycloak/clowder-realm-prod.json:/opt/keycloak/data/import/realm.json:ro + - ./scripts/keycloak/clowder-realm-subdirectory-example.json:/opt/keycloak/data/import/realm.json:ro - ./scripts/keycloak/clowder-theme/:/opt/keycloak/themes/clowder-theme/:ro command: - start-dev diff --git a/docs/docs/admins/deployatsubdirectory.md b/docs/docs/admins/deployatsubdirectory.md index 0dfec3901..43c1672d3 100644 --- a/docs/docs/admins/deployatsubdirectory.md +++ b/docs/docs/admins/deployatsubdirectory.md @@ -18,9 +18,10 @@ docker build --no-cache -t clowder/clowder2-frontend:basename \ - **`CLOWDER_REMOTE_HOSTNAME=clowder`**: Point to the backend service that frontend will request, which should match the subdirectory "/clowder" as well. -## Step 2: Modify `docker-compose.yml` to Use the Built Image +## Step 2: Modify `docker-compose.subdirectory.yml` to Use the Built Image -In your `docker-compose.yml`, update the frontend service to use the newly built image: +In your `docker-compose.subdirectory.yml`, update the frontend service to use the newly built image: +e.g. ```yaml frontend: @@ -47,11 +48,36 @@ echo $BASE_URL_ROUTE Now, start your services using Docker Compose: ```bash -docker-compose up +docker compose -f docker-compose.subdirectory.yml up ``` - **Backend**: The backend service will be accessible at `http://localhost/clowder/api/v2`. -- **Frontend**: The frontend service will be accessible at `http://localhost/clowder`. +- **Frontend**: The frontend service will be accessible at `http://localhost/clowder/`. +- **Keycloak**: The Keycloak admin console will be accessible at `http://localhost/clowder/keycloak/`. + +## Step 5: Configuring Keycloak (Optional) + +If you're deploying Keycloak under a subdirectory other than `/clowder`, follow these steps to adjust the Keycloak +settings. If you're using `/clowder` as your subdirectory, this step is optional since the provided Docker Compose +setup will automatically load a pre-configured realm JSON file. + +1. **Login to Keycloak Admin Console**: Navigate to `http://localhost/clowder/keycloak` in your browser. +2. **Login Credentials**: + - **Username**: `admin` + - **Password**: `admin` +3. **Navigate to the Clowder Realm**: Go to `Realm Settings` > `General`. + 4.**Change Frontend URL**: + - Update the `Frontend URL` field to `http://localhost/clowder/keycloak`. + - Click `Save` to apply the changes. +5. **Find the Client**: + - Go to `Clients` in the Keycloak admin console. + - Select the client with `clientId`: `clowder2-backend`. +6. **Update the Root URL**: + - Set the `Root URL` to `http://localhost/clowder`. +7. **Update Redirect URIs**: + - Ensure that the `Redirect URIs` field includes `http://localhost/clowder/api/v2/auth`. +8. **Update Web Origins**: + - Ensure that the `Web Origins` field includes `http://localhost/clowder`. By following these steps, both your backend and frontend services will be correctly deployed under the `/clowder` subdirectory, making them accessible through the configured paths. diff --git a/scripts/keycloak/clowder-realm-subdirectory-example.json b/scripts/keycloak/clowder-realm-subdirectory-example.json new file mode 100644 index 000000000..8940b5969 --- /dev/null +++ b/scripts/keycloak/clowder-realm-subdirectory-example.json @@ -0,0 +1,2191 @@ +{ + "id": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "realm": "clowder", + "notBefore": 1647451804, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "registrationEmailAsUsername": true, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "255cadda-90be-4be8-aabe-8a1437e8db36", + "name": "default-roles-clowder", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ] + }, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "attributes": {} + }, + { + "id": "f5cb44bb-0164-4eea-9890-8a7f45de866f", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "attributes": {} + }, + { + "id": "d2e9fd55-a43c-4ba3-8404-6ce15df1bf9a", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "ad9e68f2-2ae8-49f9-89bc-2bc9b01e8cc8", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "29cbf565-9522-4c62-99dc-ac72af7fb8a7", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "be2054cf-2df5-48a3-b88a-67fc4e0ac4a4", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "ab761a56-8aab-4ab7-a96e-0e24261ed665", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "52936cf0-7ff9-4a31-b445-ba11d50c1ada", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "dea4489f-fd53-4228-9365-643e74fdad6a", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "84f86cee-d957-46af-bb07-02264f8cb319", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "b17a67e6-5dc0-47f5-9849-d477978dc6c6", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "41c03bf6-1d4c-4827-8e11-e244741685d5", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "6acd5468-07d3-4132-8ed3-e53e1b6e4fa3", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "57ed79be-16b2-435e-bb05-c306cdbd6caf", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "9fec4324-c1cf-4da1-b27f-2b0cf22c16eb", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "cf7e8b5b-228f-4982-baa6-d1d9c4477dc2", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "75522763-9eda-48b4-8b88-49d25accc722", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "f469e893-a3cf-4d5a-8f13-17f555496ef6", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "93d934d7-a455-423c-ac53-c21cabff1fc2", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "9bf9f1dd-bd30-42fb-84bd-b4c3674a133b", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "6ec7320f-75a2-404b-968b-fd4cefd3bdc0", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "view-realm", + "query-clients", + "query-groups", + "view-events", + "manage-clients", + "view-clients", + "impersonation", + "manage-events", + "query-realms", + "view-users", + "view-identity-providers", + "query-users", + "view-authorization", + "create-client", + "manage-users", + "manage-identity-providers", + "manage-realm", + "manage-authorization" + ] + } + }, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + }, + { + "id": "94051464-a27f-4c02-9992-310ee0570648", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "clowder2-backend": [], + "account-console": [], + "broker": [], + "account": [ + { + "id": "1b32f3e7-1cea-4e06-b366-1466707a8786", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "c0dbd9a3-d750-4ff1-aa42-ac778df83ef6", + "attributes": {} + }, + { + "id": "5abeaebe-0017-42ed-9496-8b6ffcf2539a", + "name": "manage-account", + "composite": false, + "clientRole": true, + "containerId": "c0dbd9a3-d750-4ff1-aa42-ac778df83ef6", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "255cadda-90be-4be8-aabe-8a1437e8db36", + "name": "default-roles-clowder", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "a95a9b0c-dfc0-42fc-a89d-6fab3a37fcf3" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "c0dbd9a3-d750-4ff1-aa42-ac778df83ef6", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/clowder/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/clowder/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "c6d18450-47f3-4d2a-87ef-97d0f3414efe", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/clowder/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/clowder/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "8268dd83-7d4a-4aa4-9b32-803a881c11a2", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "89a47015-9ef6-46e5-8059-874b675059ef", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d2185eda-0e48-47c1-bda5-150c199865c7", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "9d3fa7e0-6aa2-45c1-865d-4eee673eb6bf", + "clientId": "clowder2-backend", + "rootUrl": "http://localhost/clowder", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "http://localhost/clowder/api/v2/auth" + ], + "webOrigins": [ + "http://localhost/clowder" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "frontchannel.logout.session.required": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature.keyinfo.ext": "false", + "use.refresh.tokens": "true", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "false", + "client_credentials.use_refresh_token": "false", + "require.pushed.authorization.requests": "false", + "saml.client.signature": "false", + "saml.allow.ecp.flow": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", + "acr.loa.map": "{}", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email", + "openid" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "0c9efac7-4df3-4116-a1e3-c421fb4bf9df", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "cf8424c8-b84a-43d4-8e0f-93c3f394fe84", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/clowder/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/clowder/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "c50de445-699e-4af6-958d-4dec02312a1c", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "b2fed6ad-3dfe-4522-b1ba-8a9b7c1b2de2", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "52c045ac-ded8-4961-ae54-b8b7206ca36c", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "bae6d53b-dc40-4819-95c2-99cc8addff6c", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "e73aabcb-994e-4201-93fd-66bb9a75842a", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "0a8f056e-36df-4aaf-8eee-289e13ec473c", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "57a6b29f-e66a-4d32-93e6-d2c3b1fe4823", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "c0dc7048-d769-4d98-8fd8-a85e4bbfa62f", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "27b15f8c-ea65-4288-bfd5-bd88eb1d05cf", + "name": "openid", + "description": "", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "gui.order": "", + "consent.screen.text": "" + } + }, + { + "id": "aeeafc25-f5bc-4f8d-88c5-0b4150415d50", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d808f1a4-7485-4abc-bc96-730ffaa3d541", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "c97eee7b-93bb-46e3-b11e-e07bd9a88034", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "e3ad1110-ec92-4cec-bfe2-03fd49c25ccc", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "a5673fb0-526e-41b4-a1d7-47b53e7fe4d6", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "825256af-e9c1-4faf-91ba-b31cf812f625", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "667fc7f5-4230-44fa-b8b5-29943e77a102", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "a3f2ce5f-88c0-4b6d-b908-8a4d933c9f48", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "668b1177-85cf-4b3b-b893-d75a5c697495", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "cb7b6bbc-1506-4fab-ba0f-fd65cfda5789", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "91896308-0855-4152-9a16-b4e63093b07c", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "95e292af-9fa5-44ff-892e-a32a42183627", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "b58166e6-87c2-474b-9b04-ba0d813b45b3", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "30c5a069-1d72-4bb8-85ce-11eac851ec61", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "c8328579-ef1c-4737-83f4-cb129c8c7831", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "c54898da-56be-4c37-ba26-d07a9824c4f2", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "72a8da7e-bad0-4e79-be75-dd48c8900499", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "2e64287f-7bb2-48cd-94ab-dc86fa0c7c88", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "446734d4-3ad8-4c97-aaea-44e21319402e", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "eb6c022f-2578-4828-87f1-e05927678120", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "9d242859-9217-4c9a-9889-08d49634784d", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "7cc66e50-4e9f-4940-b532-7bcadbdcb279", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "ad46489a-f89f-4f61-b393-3a340106ac25", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "5b3ba25d-d6fe-4672-b7bd-f87a738eefea", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "b8374915-aed5-4224-b143-ff0fba5145bb", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "42ddc8a7-f421-4f55-8b48-a4584ecb706c", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "70b9810a-ac2d-4cb0-ba9d-a0af109ed2c6", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "9517fbc2-4bc7-44ad-bd3c-2134e6385b01", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "3553f990-ba52-4713-b8d3-299b83f2a7a2", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "f054f73a-24c3-4ffa-af87-243ddffd0b5a", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr", + "openid" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": { + "host": "maildev", + "from": "devnull@ncsa.illinois.edu", + "starttls": "", + "auth": "", + "ssl": "" + }, + "loginTheme": "clowder-theme", + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [ + { + "alias": "cilogon", + "displayName": "CILogon", + "internalId": "165a05f4-f6d7-44ae-a906-285cba64bae7", + "providerId": "oidc", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": true, + "addReadTokenRoleOnCreate": true, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "config": { + "userInfoUrl": "https://cilogon.org/oauth2/userinfo", + "clientId": "cilogon:/client_id/165f54b200b7bc4bf77635fe56237902", + "tokenUrl": "https://cilogon.org/oauth2/token", + "authorizationUrl": "https://cilogon.org/authorize", + "clientAuthMethod": "client_secret_post", + "syncMode": "IMPORT", + "clientSecret": "**********", + "defaultScope": "openid profile org.cilogon.userinfo email", + "useJwksUrl": "true" + } + }, + { + "alias": "globus", + "displayName": "Globus", + "internalId": "1f4df120-221f-4ed9-ab4a-f40bfeedafbb", + "providerId": "oidc", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": true, + "addReadTokenRoleOnCreate": true, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "config": { + "clientId": "fa3320ff-4730-4395-a4ec-fc7fe23ec8a7", + "tokenUrl": "https://auth.globus.org/v2/oauth2/token", + "authorizationUrl": "https://auth.globus.org/v2/oauth2/authorize", + "clientAuthMethod": "client_secret_post", + "syncMode": "IMPORT", + "clientSecret": "**********", + "defaultScope": "openid profile email", + "useJwksUrl": "true" + } + } + ], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "e2dae728-1a1a-45af-9065-620384959e13", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "2a2f8ffa-334e-4736-8ce7-9669f3c1b57b", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "b61aee8f-89b9-429f-a00c-0e8ad8d397e8", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "587e5d10-5f94-4b55-9eab-af3757126452", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper", + "saml-user-attribute-mapper", + "saml-role-list-mapper", + "oidc-address-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper" + ] + } + }, + { + "id": "42de5d51-e99f-48c5-a347-8a943b55b11d", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "ec10fb40-0867-403f-bf83-0dd804d9658c", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "65145f79-7a4b-4ee0-823b-e6c901ea050f", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-address-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper" + ] + } + }, + { + "id": "768e411f-2942-4de1-8072-7c1a18ad158a", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "25f8382e-c4c6-4a98-b2ec-c1381a946e2c", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "5c263375-6225-41c5-8243-c13a4aa17fa4", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "5e332177-04bc-4d36-9ef6-93a2262a845b", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "32c7819c-b8fe-4187-8d4d-0620e17732c9", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [ + "" + ], + "authenticationFlows": [ + { + "id": "400aee4e-856a-422f-b1a0-eb6ce32f69e3", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "bfa7993c-039d-427d-8b17-4b97d810be7e", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "16b00e1b-3b22-4d2e-b5bf-9c8b220b3304", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "962c17fa-c368-48dc-b98e-8d5c925737a3", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "15cbf8fe-29c0-4a40-bfc4-047bd64561de", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "4cc602f5-ce74-4fac-a45e-52aa4fb7cdcb", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "12a01691-1248-457e-a3ed-d5246a6f3982", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "34ddada2-d38c-4f8b-9df8-ad2cd8aaea35", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "7faed55c-c6f0-4cfc-ac1b-27ee2fb0048c", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "c6401558-ae19-4607-9d2a-42e1c777517e", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "ad12f274-58d8-474a-a841-760a5c937f87", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "c3a84b9f-de66-48e1-b3ee-097a711387a2", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "95111482-c6b0-47ab-affa-b8ad9b98ab2e", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "21832c48-5d22-465b-a147-61f9d22651e4", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "deebfeac-43f7-4c33-ad8c-7f3e948480dd", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "6209d001-17fe-4eae-8e49-c9fa96861e99", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Authentication Options", + "userSetupAllowed": false + } + ] + }, + { + "id": "1d9bdfb5-e2e4-44fe-891a-a3239c7d4efa", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "17f77ee5-6665-4ebd-bb25-1d43ae653c54", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "6f9bb247-aaf4-4123-be3f-8732a55cc060", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "d4b93ed0-2aef-4cbe-a455-cd6cd76f3927", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "006fdb06-4dba-4478-9863-e51020f2a2ab", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "510e58e8-bb74-4f51-a217-850f4dca371f", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "userProfileEnabled": "false", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "http://localhost/clowder/keycloak" + }, + "keycloakVersion": "18.0.0", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +}