diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 178b4d1..9774c61 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -55,7 +55,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -68,4 +68,4 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 998105f..af8410a 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,11 +6,11 @@ jobs: pre-commit: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install asdf - uses: asdf-vm/actions/setup@v1 + uses: asdf-vm/actions/setup@v3 - name: Cache tools - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: | /home/runner/.asdf @@ -29,7 +29,7 @@ jobs: commitlint: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: wagoid/commitlint-github-action@v2 + - uses: wagoid/commitlint-github-action@v5 diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml index 298b5d9..1fbafb9 100644 --- a/.github/workflows/publish-image.yml +++ b/.github/workflows/publish-image.yml @@ -21,11 +21,11 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: hmarr/debug-action@v2 - - uses: actions/checkout@v2 + - uses: hmarr/debug-action@v3 + - uses: actions/checkout@v4 - name: Log in to the GitHub Container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + uses: docker/login-action@v3 with: registry: ${{ env.GITHUB_REGISTRY }} username: ${{ github.actor }} @@ -33,7 +33,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + uses: docker/metadata-action@v5 with: images: ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }} @@ -54,17 +54,17 @@ jobs: echo "release_tag=${{ endsWith(github.ref, '/main') && steps.release.outputs.tag_name || steps.meta.outputs.version }}" >> $GITHUB_OUTPUT - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: ${{ runner.os }}-buildx- - name: Build and push Docker image - uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc + uses: docker/build-push-action@v5 with: context: app push: true diff --git a/.github/workflows/publish-metabase.yml b/.github/workflows/publish-metabase.yml index 9d76edb..5d64573 100644 --- a/.github/workflows/publish-metabase.yml +++ b/.github/workflows/publish-metabase.yml @@ -15,8 +15,8 @@ jobs: contents: read steps: - - uses: hmarr/debug-action@v2 - - uses: actions/checkout@v2 + - uses: hmarr/debug-action@v3 + - uses: actions/checkout@v4 - name: Authenticate and set context uses: redhat-actions/oc-login@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a518f3e..a5aec32 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ jobs: unit-test: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 18 diff --git a/app/components/Modal.tsx b/app/components/Modal.tsx index c555e11..6fe6a81 100644 --- a/app/components/Modal.tsx +++ b/app/components/Modal.tsx @@ -85,6 +85,13 @@ export default function GlobalModal({ setModalConfig, modalConfig }: Props) { const { showCancelButton, showConfirmButton, onConfirm } = modalConfig; const hasButtons = showCancelButton || showConfirmButton; + const closeModal = () => { + if (modalConfig.onClose) { + modalConfig.onClose(); + } + clearModal(); + }; + const onConfirmClick = async () => { if (!onConfirm) return; setWaiting(true); @@ -99,14 +106,14 @@ export default function GlobalModal({ setModalConfig, modalConfig }: Props) { return ( -
+
{modalConfig.title}
- +
{modalConfig.body} diff --git a/app/context/modal.ts b/app/context/modal.ts index 8943047..55fbe4f 100644 --- a/app/context/modal.ts +++ b/app/context/modal.ts @@ -6,6 +6,7 @@ export interface ModalConfig { body: string; showConfirmButton?: boolean; showCancelButton?: boolean; + onClose?: () => void; onConfirm?: () => Promise; } diff --git a/app/layout/Layout.tsx b/app/layout/Layout.tsx index d333571..aaefba7 100644 --- a/app/layout/Layout.tsx +++ b/app/layout/Layout.tsx @@ -8,10 +8,12 @@ import styled from 'styled-components'; import BCSans from './BCSans'; import Navigation from './Navigation'; import BottomAlertProvider from './BottomAlert'; -import { getSession, useSession } from 'next-auth/react'; -import { useEffect } from 'react'; +import { useSession } from 'next-auth/react'; +import { useEffect, useContext } from 'react'; import { User } from 'next-auth'; -import { wikiURL } from 'utils/helpers'; +import { formatWikiURL } from 'utils/helpers'; +import { useIdleTimer } from 'react-idle-timer'; +import { ModalContext } from 'context/modal'; const headerPlusFooterHeight = '152px'; @@ -150,23 +152,32 @@ const RightMenuItems = () => ( - + ); + +const modalContent = { + title: `Session Expiring`, + body: `Your session will expire soon and you will be signed out automatically. Do you want to stay signed in?`, + showCancelButton: false, + showConfirmButton: true, +}; + // identity_provider, idir_userid, client_roles, family_name, given_name function Layout({ children, onLoginClick, onLogoutClick }: any) { const router = useRouter(); const session = useSession(); const currentUser: Partial = session?.data?.user!; const pathname = router.pathname; + const { setModalConfig } = useContext(ModalContext); const checkSession = async () => { - if (Date.now() > session?.data?.accessTokenExpiry) { - const session = await getSession(); - if (session?.error === 'RefreshAccessTokenError') { + if (Date.now() > session?.data?.accessTokenExpiry * 1000) { + const updatedSession = await session.update(); + if (updatedSession?.error === 'RefreshAccessTokenError') { onLogoutClick(); } } @@ -179,6 +190,32 @@ function Layout({ children, onLoginClick, onLogoutClick }: any) { } }); + useIdleTimer({ + onPrompt: (_event, timer) => { + setModalConfig({ + ...modalContent, + show: true, + onConfirm: async () => { + await session.update(); + timer?.reset(); + }, + onClose: () => { + onLogoutClick(); + }, + }); + }, + onIdle: () => { + setModalConfig({ + ...modalContent, + show: false, + }); + onLogoutClick(); + }, + timeout: 30 * 60 * 1000, + promptBeforeIdle: 5 * 60 * 1000, + disabled: !['authenticated', 'loading'].includes(session?.status), + }); + const rightSide = currentUser ? (
Welcome {`${currentUser?.given_name} ${currentUser?.family_name}`}
@@ -207,7 +244,7 @@ function Layout({ children, onLoginClick, onLogoutClick }: any) {    - + diff --git a/app/next.config.js b/app/next.config.js index 0e63562..a00bcee 100644 --- a/app/next.config.js +++ b/app/next.config.js @@ -44,6 +44,7 @@ module.exports = { ches_password: process.env.CHES_PASSWORD, bceid_service_id: process.env.BCEID_SERVICE_ID, + bceid_web_service_url: process.env.BCEID_WEB_SERVICE_URL || '', bceid_service_basic_auth: process.env.BCEID_SERVICE_BASIC_AUTH, idir_jwks_uri: process.env.IDIR_JWKS_URI, idir_issuer: process.env.IDIR_ISSUER, diff --git a/app/package.json b/app/package.json index 33bfd70..711f6f5 100644 --- a/app/package.json +++ b/app/package.json @@ -49,6 +49,7 @@ "react-bootstrap": "^2.4.0", "react-dom": "18.1.0", "react-hook-form": "^7.31.3", + "react-idle-timer": "^5.7.2", "react-loader-spinner": "^6.0.0-0", "react-select": "^5.8.0", "react-typography": "^0.16.20", diff --git a/app/pages/api/bceid-service/bceid.ts b/app/pages/api/bceid-service/bceid.ts index c6dd417..582dc97 100644 --- a/app/pages/api/bceid-service/bceid.ts +++ b/app/pages/api/bceid-service/bceid.ts @@ -7,11 +7,10 @@ import get from 'lodash.get'; import { getIdirUserGuid } from 'utils/jwt'; const { serverRuntimeConfig = {} } = getConfig() || {}; -const { bceid_service_id, bceid_service_basic_auth } = serverRuntimeConfig; +const { bceid_service_id, bceid_service_basic_auth, bceid_web_service_url } = serverRuntimeConfig; const parseStringSync = promisify(parseString); -const serviceUrl = 'https://gws2.test.bceid.ca/webservices/client/V10/BCeIDService.asmx?WSDL'; const defaultHeaders = { 'Content-Type': 'text/xml;charset=UTF-8', authorization: `Basic ${bceid_service_basic_auth}`, @@ -52,7 +51,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const idirUserGuid = await getIdirUserGuid(token); const xml = generateXML(property, accountType, matchKey, idirUserGuid); const { response }: any = await soapRequest({ - url: serviceUrl, + url: bceid_web_service_url, headers: defaultHeaders, xml, timeout: 10000, diff --git a/app/pages/index.tsx b/app/pages/index.tsx index 1fb511a..32273ee 100644 --- a/app/pages/index.tsx +++ b/app/pages/index.tsx @@ -9,7 +9,7 @@ import ResponsiveContainer, { MediaRule } from 'components/ResponsiveContainer'; import IntroRealms from 'svg/IntroRealms'; import { signIn, useSession } from 'next-auth/react'; import NextLink from 'next/link'; -import { wikiURL } from 'utils/helpers'; +import { formatWikiURL } from 'utils/helpers'; const JumbotronH1 = styled.h1` font-size: 2.5rem; @@ -113,10 +113,10 @@ const Home = () => {

Do you want to request a new Custom Realm?

Over 90% of our clients benefit from our Standard Service, please visit our{' '} - information to ensure this is not a fit for you. To maintain our{' '} - service levels, we need to evaluate - every single custom realm request coming to us. Please fill out the form to start the conversation - with us. + information to ensure this is not a fit for you. To maintain our{' '} + service levels, we need to + evaluate every single custom realm request coming to us. Please fill out the form to start the + conversation with us.

Request a Custom Realm diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index 6ae2284..ddee8c8 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -5,7 +5,10 @@ import getConfig from 'next/config'; const { serverRuntimeConfig = {} } = getConfig() || {}; const { dev_kc_url, test_kc_url, prod_kc_url } = serverRuntimeConfig; -export const wikiURL = 'https://mvp.developer.gov.bc.ca/docs/default/component/css-docs'; +export const formatWikiURL = (page?: string) => + `https://mvp.developer.gov.bc.ca/docs/default/component/css-docs/${ + page ?? '' + }?utm_source=sso-wiki&utm_medium=web&utm_campaign=retirement-notice-sso`; export enum RoleEnum { ADMIN = 'admin', diff --git a/app/utils/idir.ts b/app/utils/idir.ts index bdf3889..238a7e3 100644 --- a/app/utils/idir.ts +++ b/app/utils/idir.ts @@ -6,11 +6,10 @@ import get from 'lodash.get'; import map from 'lodash.map'; const { serverRuntimeConfig = {} } = getConfig() || {}; -const { bceid_service_id, bceid_service_basic_auth } = serverRuntimeConfig; +const { bceid_service_id, bceid_service_basic_auth, bceid_web_service_url } = serverRuntimeConfig; export const parseStringSync = promisify(parseString); -export const serviceUrl = 'https://gws2.test.bceid.ca/webservices/client/V10/BCeIDService.asmx?WSDL'; export const defaultHeaders = { 'Content-Type': 'text/xml;charset=UTF-8', authorization: `Basic ${bceid_service_basic_auth}`, @@ -101,7 +100,7 @@ export function parseAccount(data: any) { export const makeSoapRequest = async (xmlPayload: string) => { return await soapRequest({ - url: serviceUrl, + url: bceid_web_service_url, headers: defaultHeaders, xml: xmlPayload, timeout: 10000, diff --git a/app/utils/mailer.ts b/app/utils/mailer.ts index d9a8a63..9568828 100644 --- a/app/utils/mailer.ts +++ b/app/utils/mailer.ts @@ -3,7 +3,7 @@ import getConfig from 'next/config'; import { Session } from 'next-auth'; import { RealmProfile } from 'types/realm-profile'; import { Roster } from '@prisma/client'; -import { generateRealmLinksByEnv, generateMasterRealmLinksByEnv, wikiURL } from './helpers'; +import { generateRealmLinksByEnv, generateMasterRealmLinksByEnv, formatWikiURL } from './helpers'; const { serverRuntimeConfig = {} } = getConfig() || {}; const { app_env, sso_logout_redirect_uri } = serverRuntimeConfig; @@ -109,7 +109,7 @@ const emailFooter = ` stroke="#0e3468" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> - Knowledge Base + Knowledge Base
diff --git a/app/yarn.lock b/app/yarn.lock index ebdd0c6..4e5fcdd 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -6698,6 +6698,11 @@ react-hook-form@^7.31.3: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.48.2.tgz#01150354d2be61412ff56a030b62a119283b9935" integrity sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A== +react-idle-timer@^5.7.2: + version "5.7.2" + resolved "https://registry.yarnpkg.com/react-idle-timer/-/react-idle-timer-5.7.2.tgz#f506db28a86645dd1b87987116501703e512142b" + integrity sha512-+BaPfc7XEUU5JFkwZCx6fO1bLVK+RBlFH+iY4X34urvIzZiZINP6v2orePx3E6pAztJGE7t4DzvL7if2SL/0GQ== + react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.3, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" diff --git a/helm/webapp/templates/deployment.yaml b/helm/webapp/templates/deployment.yaml index 66f1bd3..37a921d 100644 --- a/helm/webapp/templates/deployment.yaml +++ b/helm/webapp/templates/deployment.yaml @@ -37,6 +37,7 @@ spec: {{`{{ with secret "`}}{{ .Values.vault.vaultSecretEngine }}/{{ .Values.vault.realmRegistrySecret }}{{`" -}} export BCEID_SERVICE_BASIC_AUTH="{{ .Data.data.BCEID_SERVICE_BASIC_AUTH }}" export BCEID_SERVICE_ID="{{ .Data.data.BCEID_SERVICE_ID}}" + export BCEID_WEB_SERVICE_URL="{{ .Data.data.BCEID_WEB_SERVICE_URL}}" export DEV_KC_USERNAME="{{ .Data.data.DEV_KC_USERNAME}}" export DEV_KC_PASSWORD="{{ .Data.data.DEV_KC_PASSWORD}}" export DEV_KC_URL="{{ .Data.data.DEV_KC_URL}}"