diff --git a/internal/lookout/ui/src/App.tsx b/internal/lookout/ui/src/App.tsx index c7123b52699..44bb91651cf 100644 --- a/internal/lookout/ui/src/App.tsx +++ b/internal/lookout/ui/src/App.tsx @@ -1,15 +1,16 @@ -import { Dispatch, ReactNode, SetStateAction, useEffect, useState } from "react" +import { useEffect } from "react" import { CssBaseline, styled, ThemeProvider } from "@mui/material" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { SnackbarProvider } from "notistack" -import { UserManager, WebStorageStateStore, UserManagerSettings, User } from "oidc-client-ts" -import { BrowserRouter, Navigate, Route, Routes, useNavigate } from "react-router-dom" +import { UserManager, WebStorageStateStore, UserManagerSettings } from "oidc-client-ts" +import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom" import NavBar from "./components/NavBar" import JobSetsContainer from "./containers/JobSetsContainer" import { JobsTableContainer } from "./containers/lookoutV2/JobsTableContainer" -import { UserManagerContext, useUserManager } from "./oidc" +import { OidcAuthProvider } from "./oidcAuth" +import { OIDC_REDIRECT_PATHNAME } from "./oidcAuth/OidcAuthProvider" import { Services, ServicesProvider } from "./services/context" import { theme } from "./theme/theme" import { CommandSpec, OidcConfig, withRouter } from "./utils" @@ -43,71 +44,11 @@ type AppProps = { commandSpecs: CommandSpec[] } -// Handling authentication on page opening - -interface AuthWrapperProps { - children: ReactNode - userManager: UserManager | undefined - isAuthenticated: boolean -} - -interface OidcCallbackProps { - setIsAuthenticated: Dispatch> -} - -export const UserManagerProvider = UserManagerContext.Provider - -function OidcCallback({ setIsAuthenticated }: OidcCallbackProps): JSX.Element { - const navigate = useNavigate() - const userManager = useUserManager() - const [error, setError] = useState() - - useEffect(() => { - if (!userManager) return - userManager - .signinRedirectCallback() - .then(() => { - setIsAuthenticated(true) - navigate("/") - }) - .catch((e) => { - setError(`${e}`) - console.error(e) - }) - }, [navigate, userManager, setIsAuthenticated]) - - if (error) return

Something went wrong; more details are available in the console.

- return

Authenticating...

-} - -function AuthWrapper({ children, userManager, isAuthenticated }: AuthWrapperProps) { - useEffect(() => { - if (!userManager || isAuthenticated) return // Skip if userManager is not available or user is already authenticated - - const handleAuthentication = async () => { - try { - const user = await userManager.getUser() - if (!user || user.expired) { - await userManager.signinRedirect() - } - } catch (error) { - console.error("Error during authentication:", error) - } - } - - ;(async () => { - await handleAuthentication() - })() - }, [userManager, isAuthenticated]) - - return <>{children} -} - export function createUserManager(config: OidcConfig): UserManager { const userManagerSettings: UserManagerSettings = { authority: config.authority, client_id: config.clientId, - redirect_uri: `${window.location.origin}/oidc`, + redirect_uri: `${window.location.origin}${OIDC_REDIRECT_PATHNAME}`, scope: config.scope, userStore: new WebStorageStateStore({ store: window.localStorage }), loadUserInfo: true, @@ -121,90 +62,68 @@ export function createUserManager(config: OidcConfig): UserManager { const V2Redirect = withRouter(({ router }) => ) export function App(props: AppProps): JSX.Element { - const [userManager, setUserManager] = useState(undefined) - const [isAuthenticated, setIsAuthenticated] = useState(false) - const [username, setUsername] = useState(undefined) - - useEffect(() => { - if (!userManager && props.oidcConfig) { - const userManagerInstance = createUserManager(props.oidcConfig) - setUserManager(userManagerInstance) - - userManagerInstance.getUser().then((user: User | null) => { - if (user) { - setUsername(user.profile.sub) - } - }) - } - }, [props.oidcConfig]) - useEffect(() => { if (props.customTitle) { document.title = `${props.customTitle} - Armada Lookout` } }, [props.customTitle]) - const result = ( + return ( - - - - - - - - - - } - /> - - } - /> - } /> - } /> - - } - /> - - - - - - - + + + + + + + + + } + /> + + } + /> + } /> + + } + /> + + + + + + ) - - return result } diff --git a/internal/lookout/ui/src/components/NavBar.tsx b/internal/lookout/ui/src/components/NavBar.tsx index a33828b1f2a..c290b28c12d 100644 --- a/internal/lookout/ui/src/components/NavBar.tsx +++ b/internal/lookout/ui/src/components/NavBar.tsx @@ -5,6 +5,7 @@ import { AppBar, IconButton, Tab, Tabs, Toolbar, Typography } from "@mui/materia import { Link } from "react-router-dom" import { SettingsDialog } from "./SettingsDialog" +import { useUsername } from "../oidcAuth" import { Router, withRouter } from "../utils" import "./NavBar.css" @@ -46,13 +47,13 @@ function locationFromIndex(pages: Page[], index: number): string { interface NavBarProps { customTitle: string router: Router - username?: string } -function NavBar({ customTitle, router, username }: NavBarProps) { +function NavBar({ customTitle, router }: NavBarProps) { const currentLocation = router.location.pathname const currentValue = locationMap.has(currentLocation) ? locationMap.get(currentLocation) : 0 const [settingsOpen, setSettingsOpen] = useState(false) + const username = useUsername() return ( <> diff --git a/internal/lookout/ui/src/components/lookoutV2/CancelDialog.tsx b/internal/lookout/ui/src/components/lookoutV2/CancelDialog.tsx index 5a2552fcce6..8fe498f0606 100644 --- a/internal/lookout/ui/src/components/lookoutV2/CancelDialog.tsx +++ b/internal/lookout/ui/src/components/lookoutV2/CancelDialog.tsx @@ -10,7 +10,7 @@ import dialogStyles from "./DialogStyles.module.css" import { JobStatusTable } from "./JobStatusTable" import { useCustomSnackbar } from "../../hooks/useCustomSnackbar" import { isTerminatedJobState, Job, JobFilter, JobId } from "../../models/lookoutV2Models" -import { getAccessToken, useUserManager } from "../../oidc" +import { useGetAccessToken } from "../../oidcAuth" import { IGetJobsService } from "../../services/lookoutV2/GetJobsService" import { UpdateJobsService } from "../../services/lookoutV2/UpdateJobsService" import { pl, waitMillis, PlatformCancelReason } from "../../utils" @@ -41,7 +41,7 @@ export const CancelDialog = ({ const [isPlatformCancel, setIsPlatformCancel] = useState(false) const openSnackbar = useCustomSnackbar() - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() // Actions const fetchSelectedJobs = useCallback(async () => { @@ -67,7 +67,7 @@ export const CancelDialog = ({ setIsCancelling(true) const reason = isPlatformCancel ? PlatformCancelReason : "" - const accessToken = userManager && (await getAccessToken(userManager)) + const accessToken = await getAccessToken() const response = await updateJobsService.cancelJobs(cancellableJobs, reason, accessToken) if (response.failedJobIds.length === 0) { diff --git a/internal/lookout/ui/src/components/lookoutV2/ReprioritiseDialog.tsx b/internal/lookout/ui/src/components/lookoutV2/ReprioritiseDialog.tsx index ac2fd0db495..98d0464e1fd 100644 --- a/internal/lookout/ui/src/components/lookoutV2/ReprioritiseDialog.tsx +++ b/internal/lookout/ui/src/components/lookoutV2/ReprioritiseDialog.tsx @@ -18,7 +18,7 @@ import dialogStyles from "./DialogStyles.module.css" import { JobStatusTable } from "./JobStatusTable" import { useCustomSnackbar } from "../../hooks/useCustomSnackbar" import { isTerminatedJobState, Job, JobFilter, JobId } from "../../models/lookoutV2Models" -import { getAccessToken, useUserManager } from "../../oidc" +import { useGetAccessToken } from "../../oidcAuth" import { IGetJobsService } from "../../services/lookoutV2/GetJobsService" import { UpdateJobsService } from "../../services/lookoutV2/UpdateJobsService" import { pl, waitMillis } from "../../utils" @@ -51,7 +51,7 @@ export const ReprioritiseDialog = ({ const [hasAttemptedReprioritise, setHasAttemptedReprioritise] = useState(false) const openSnackbar = useCustomSnackbar() - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() // Actions const fetchSelectedJobs = useCallback(async () => { @@ -79,7 +79,7 @@ export const ReprioritiseDialog = ({ setIsReprioritising(true) - const accessToken = userManager && (await getAccessToken(userManager)) + const accessToken = await getAccessToken() const response = await updateJobsService.reprioritiseJobs(reprioritisableJobs, newPriority, accessToken) if (response.failedJobIds.length === 0) { diff --git a/internal/lookout/ui/src/components/lookoutV2/sidebar/SidebarTabJobResult.tsx b/internal/lookout/ui/src/components/lookoutV2/sidebar/SidebarTabJobResult.tsx index 86598881f21..c4c822b931d 100644 --- a/internal/lookout/ui/src/components/lookoutV2/sidebar/SidebarTabJobResult.tsx +++ b/internal/lookout/ui/src/components/lookoutV2/sidebar/SidebarTabJobResult.tsx @@ -21,7 +21,7 @@ import { NoRunsAlert } from "./NoRunsAlert" import { SidebarTabHeading, SidebarTabSubheading } from "./sidebarTabContentComponents" import { useCustomSnackbar } from "../../../hooks/useCustomSnackbar" import { Job, JobRun, JobState } from "../../../models/lookoutV2Models" -import { getAccessToken, useUserManager } from "../../../oidc" +import { useGetAccessToken } from "../../../oidcAuth" import { ICordonService } from "../../../services/lookoutV2/CordonService" import { IGetJobInfoService } from "../../../services/lookoutV2/GetJobInfoService" import { IGetRunInfoService } from "../../../services/lookoutV2/GetRunInfoService" @@ -216,11 +216,11 @@ export const SidebarTabJobResult = ({ setOpen(false) } - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() const cordon = async (cluster: string, node: string) => { try { - const accessToken = userManager && (await getAccessToken(userManager)) + const accessToken = await getAccessToken() await cordonService.cordonNode(cluster, node, accessToken) openSnackbar("Successfully cordoned node " + node, "success") } catch (e) { diff --git a/internal/lookout/ui/src/containers/CancelJobSetsDialog.tsx b/internal/lookout/ui/src/containers/CancelJobSetsDialog.tsx index 585348b8320..48e342c7f17 100644 --- a/internal/lookout/ui/src/containers/CancelJobSetsDialog.tsx +++ b/internal/lookout/ui/src/containers/CancelJobSetsDialog.tsx @@ -4,7 +4,7 @@ import { Dialog, DialogContent, DialogTitle } from "@mui/material" import CancelJobSets from "../components/job-sets/cancel-job-sets/CancelJobSets" import CancelJobSetsOutcome from "../components/job-sets/cancel-job-sets/CancelJobSetsOutcome" -import { getAccessToken, useUserManager } from "../oidc" +import { useGetAccessToken } from "../oidcAuth" import { ApiJobState } from "../openapi/armada" import { JobSet } from "../services/JobService" import { CancelJobSetsResponse, UpdateJobSetsService } from "../services/lookoutV2/UpdateJobSetsService" @@ -52,7 +52,7 @@ export default function CancelJobSetsDialog(props: CancelJobSetsDialogProps) { const statesToCancel = getStatesToCancel(includeQueued, includeRunning) - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() async function cancelJobSets() { if (requestStatus === "Loading") { @@ -61,7 +61,7 @@ export default function CancelJobSetsDialog(props: CancelJobSetsDialogProps) { setRequestStatus("Loading") const reason = isPlatformCancel ? PlatformCancelReason : "" - const accessToken = userManager && (await getAccessToken(userManager)) + const accessToken = await getAccessToken() const cancelJobSetsResponse = await props.updateJobSetsService.cancelJobSets( props.queue, jobSetsToCancel, diff --git a/internal/lookout/ui/src/containers/ReprioritizeJobSetsDialog.tsx b/internal/lookout/ui/src/containers/ReprioritizeJobSetsDialog.tsx index 2e16a157751..5ee1b5f821a 100644 --- a/internal/lookout/ui/src/containers/ReprioritizeJobSetsDialog.tsx +++ b/internal/lookout/ui/src/containers/ReprioritizeJobSetsDialog.tsx @@ -4,7 +4,7 @@ import { Dialog, DialogContent, DialogTitle } from "@mui/material" import ReprioritizeJobSets from "../components/job-sets/reprioritize-job-sets/ReprioritizeJobSets" import ReprioritizeJobSetsOutcome from "../components/job-sets/reprioritize-job-sets/ReprioritizeJobSetsOutcome" -import { getAccessToken, useUserManager } from "../oidc" +import { useGetAccessToken } from "../oidcAuth" import { JobSet } from "../services/JobService" import { ReprioritizeJobSetsResponse, UpdateJobSetsService } from "../services/lookoutV2/UpdateJobSetsService" import { ApiResult, priorityIsValid, RequestStatus } from "../utils" @@ -37,7 +37,7 @@ export default function ReprioritizeJobSetsDialog(props: ReprioritizeJobSetsDial const jobSetsToReprioritize = getReprioritizeableJobSets(props.selectedJobSets) - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() async function reprioritizeJobSets() { if (requestStatus == "Loading" || !priorityIsValid(priority)) { @@ -45,7 +45,7 @@ export default function ReprioritizeJobSetsDialog(props: ReprioritizeJobSetsDial } setRequestStatus("Loading") - const accessToken = userManager && (await getAccessToken(userManager)) + const accessToken = await getAccessToken() const reprioritizeJobSetsResponse = await props.updateJobSetsService.reprioritizeJobSets( props.queue, jobSetsToReprioritize, diff --git a/internal/lookout/ui/src/oidc.ts b/internal/lookout/ui/src/oidc.ts deleted file mode 100644 index b9d42c66500..00000000000 --- a/internal/lookout/ui/src/oidc.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createContext, useContext } from "react" - -import { UserManager } from "oidc-client-ts" - -export const UserManagerContext = createContext(undefined) - -export function useUserManager(): UserManager | undefined { - return useContext(UserManagerContext) -} - -export async function getAccessToken(userManager: UserManager): Promise { - const user = await userManager.getUser() - - if (user !== null && !user.expired) { - return user.access_token - } - - try { - await userManager.signinRedirect() - } catch (err) { - console.error("Error during sign-in redirect:", err) - throw err - } - - const redirectedUser = await userManager.getUser() - if (redirectedUser !== null) { - return redirectedUser.access_token - } - - throw new Error("Failed to obtain access token after sign-in redirect") -} - -export function getAuthorizationHeaders(accessToken: string): Headers { - const headers = new Headers() - headers.append("Authorization", `Bearer ${accessToken}`) - return headers -} diff --git a/internal/lookout/ui/src/oidcAuth/OidcAuthContext.ts b/internal/lookout/ui/src/oidcAuth/OidcAuthContext.ts new file mode 100644 index 00000000000..deb8e54072e --- /dev/null +++ b/internal/lookout/ui/src/oidcAuth/OidcAuthContext.ts @@ -0,0 +1,9 @@ +import { createContext } from "react" + +import { UserManager } from "oidc-client-ts" + +export interface OidcAuthContextProps { + userManager: UserManager | undefined +} + +export const OidcAuthContext = createContext(undefined) diff --git a/internal/lookout/ui/src/oidcAuth/OidcAuthProvider.tsx b/internal/lookout/ui/src/oidcAuth/OidcAuthProvider.tsx new file mode 100644 index 00000000000..b11a5c7ca4b --- /dev/null +++ b/internal/lookout/ui/src/oidcAuth/OidcAuthProvider.tsx @@ -0,0 +1,149 @@ +import { ReactNode, useCallback, useEffect, useMemo, useState } from "react" + +import { Alert, AlertTitle, Button, Container, LinearProgress, styled, Typography } from "@mui/material" +import { UserManager, WebStorageStateStore } from "oidc-client-ts" + +import { SPACING } from "../styling/spacing" +import { OidcConfig } from "../utils" +import { OidcAuthContext, OidcAuthContextProps } from "./OidcAuthContext" + +export const OIDC_REDIRECT_PATHNAME = "/oidc" + +const ELLIPSIS = "\u2026" + +const userManagerStore = new WebStorageStateStore({ store: window.localStorage }) + +const Wrapper = styled("main")(({ theme }) => ({ + minHeight: "100vh", + backgroundColor: theme.palette.background.default, + display: "flex", + justifyContent: "center", + alignItems: "center", +})) + +const ContentContainer = styled(Container)(({ theme }) => ({ + display: "flex", + flexDirection: "column", + alignItems: "center", + gap: theme.spacing(SPACING.xl), +})) + +const ProgressContainer = styled("div")({ + width: "100%", +}) + +const StyledAlert = styled(Alert)({ + width: "100%", +}) + +const IconImg = styled("img")({ + maxHeight: 200, +}) + +export interface OidcAuthProviderProps { + children: ReactNode + oidcConfig: OidcConfig | undefined +} + +export const OidcAuthProvider = ({ children, oidcConfig }: OidcAuthProviderProps) => { + const [isLoading, setIsLoading] = useState(true) + + const userManager = useMemo( + () => + oidcConfig + ? new UserManager({ + authority: oidcConfig.authority, + client_id: oidcConfig.clientId, + redirect_uri: `${window.location.origin}${OIDC_REDIRECT_PATHNAME}`, + scope: oidcConfig.scope, + userStore: userManagerStore, + loadUserInfo: true, + }) + : undefined, + [oidcConfig], + ) + + const [authError, setAuthError] = useState(undefined) + + const isOidcRedirectPath = window.location.pathname === OIDC_REDIRECT_PATHNAME + const authenticate = useCallback(async () => { + setAuthError(undefined) + setIsLoading(true) + if (!userManager) { + return + } + const user = await (isOidcRedirectPath ? userManager.signinRedirectCallback() : userManager.getUser()) + if (!user || user.expired) { + return await userManager.signinRedirect() + } + + setAuthError(undefined) + setIsLoading(false) + }, [userManager, isOidcRedirectPath]) + + const handlerAuthenticationError = useCallback((e: any) => { + console.error(e) + setAuthError(e) + setIsLoading(false) + }, []) + + useEffect(() => { + if (!oidcConfig) { + setIsLoading(false) + return + } + + authenticate().catch(handlerAuthenticationError) + }, [authenticate, oidcConfig]) + + const oidcAuthContextValue = useMemo(() => ({ userManager }), [userManager]) + + if (isLoading) { + return ( + + +
+ +
+ + + +
+ + Armada Lookout + + + Signing you in{ELLIPSIS} + +
+
+
+ ) + } + + if (authError) { + return ( + + +
+ +
+ authenticate().catch(handlerAuthenticationError)}> + Retry + + } + > + Sorry, there was an error signing you into Armada Lookout + {String(authError)} + Please check the console for more details. + +
+
+ ) + } + + return {children} +} diff --git a/internal/lookout/ui/src/oidcAuth/hooks.ts b/internal/lookout/ui/src/oidcAuth/hooks.ts new file mode 100644 index 00000000000..2b55922da7a --- /dev/null +++ b/internal/lookout/ui/src/oidcAuth/hooks.ts @@ -0,0 +1,46 @@ +import { useCallback, useContext, useEffect, useState } from "react" + +import { UserManager } from "oidc-client-ts" + +import { OidcAuthContext } from "./OidcAuthContext" + +export const useUserManager = (): UserManager | undefined => useContext(OidcAuthContext)?.userManager + +export const useUsername = (): string | null => { + const userManager = useUserManager() + const [username, setUsername] = useState(null) + useEffect(() => { + if (!userManager) { + return + } + + ;(async () => { + const user = await userManager.getUser() + if (!user) { + return + } + + setUsername(user.profile.sub) + })() + }, [userManager]) + + return username +} + +export const useGetAccessToken = () => { + const userManager = useUserManager() + + return useCallback(async () => { + if (!userManager) { + return undefined + } + + const user = await userManager.getUser() + if (!user || user.expired) { + await userManager.signinRedirect() + return undefined + } + + return user.access_token + }, [userManager]) +} diff --git a/internal/lookout/ui/src/oidcAuth/index.ts b/internal/lookout/ui/src/oidcAuth/index.ts new file mode 100644 index 00000000000..75974f49512 --- /dev/null +++ b/internal/lookout/ui/src/oidcAuth/index.ts @@ -0,0 +1,3 @@ +export * from "./OidcAuthProvider" +export * from "./hooks" +export * from "./utils" diff --git a/internal/lookout/ui/src/oidcAuth/utils.ts b/internal/lookout/ui/src/oidcAuth/utils.ts new file mode 100644 index 00000000000..a94255334ae --- /dev/null +++ b/internal/lookout/ui/src/oidcAuth/utils.ts @@ -0,0 +1,4 @@ +export const appendAuthorizationHeaders = (headers: Headers, accessToken: string) => { + headers.append("Authorization", `Bearer ${accessToken}`) + return headers +} diff --git a/internal/lookout/ui/src/services/lookoutV2/CordonService.ts b/internal/lookout/ui/src/services/lookoutV2/CordonService.ts index 2f3978be99a..2f414877a40 100644 --- a/internal/lookout/ui/src/services/lookoutV2/CordonService.ts +++ b/internal/lookout/ui/src/services/lookoutV2/CordonService.ts @@ -1,4 +1,4 @@ -import { getAuthorizationHeaders } from "../../oidc" +import { appendAuthorizationHeaders } from "../../oidcAuth" import { ConfigurationParameters } from "../../openapi/binoculars" import { getBinocularsApi } from "../../utils" @@ -16,6 +16,11 @@ export class CordonService implements ICordonService { } async cordonNode(cluster: string, node: string, accessToken?: string): Promise { + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } + const api = getBinocularsApi(cluster, this.baseUrlPattern, this.config) await api.cordon( { @@ -23,7 +28,7 @@ export class CordonService implements ICordonService { nodeName: node, }, }, - accessToken === undefined ? undefined : { headers: getAuthorizationHeaders(accessToken) }, + { headers }, ) } } diff --git a/internal/lookout/ui/src/services/lookoutV2/LogService.ts b/internal/lookout/ui/src/services/lookoutV2/LogService.ts index cc7a86d9247..5e1128d9088 100644 --- a/internal/lookout/ui/src/services/lookoutV2/LogService.ts +++ b/internal/lookout/ui/src/services/lookoutV2/LogService.ts @@ -1,4 +1,4 @@ -import { getAuthorizationHeaders } from "../../oidc" +import { appendAuthorizationHeaders } from "../../oidcAuth" import { BinocularsLogLine, ConfigurationParameters } from "../../openapi/binoculars" import { getBinocularsApi } from "../../utils" @@ -38,6 +38,11 @@ export class LogService implements ILogService { tailLines: number | undefined, accessToken?: string, ): Promise { + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } + const api = getBinocularsApi(cluster, this.baseUrlPattern, this.config) const logResult = await api.logs( { @@ -52,7 +57,7 @@ export class LogService implements ILogService { }, }, }, - accessToken === undefined ? undefined : { headers: getAuthorizationHeaders(accessToken) }, + { headers }, ) return parseLogLines(logResult.log ?? []) } diff --git a/internal/lookout/ui/src/services/lookoutV2/UpdateJobSetsService.ts b/internal/lookout/ui/src/services/lookoutV2/UpdateJobSetsService.ts index d10333235f2..7925af6dc28 100644 --- a/internal/lookout/ui/src/services/lookoutV2/UpdateJobSetsService.ts +++ b/internal/lookout/ui/src/services/lookoutV2/UpdateJobSetsService.ts @@ -1,5 +1,5 @@ import { JobSet } from "../../models/lookoutV2Models" -import { getAuthorizationHeaders } from "../../oidc" +import { appendAuthorizationHeaders } from "../../oidcAuth" import { ApiJobState, SubmitApi } from "../../openapi/armada" import { getErrorMessage } from "../../utils" @@ -32,6 +32,11 @@ export class UpdateJobSetsService { const response: CancelJobSetsResponse = { cancelledJobSets: [], failedJobSetCancellations: [] } for (const jobSet of jobSets) { try { + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } + await this.submitApi.cancelJobSet( { body: { @@ -43,7 +48,7 @@ export class UpdateJobSetsService { reason: reason, }, }, - accessToken === undefined ? undefined : { headers: getAuthorizationHeaders(accessToken) }, + { headers }, ) response.cancelledJobSets.push(jobSet) } catch (e) { @@ -65,6 +70,11 @@ export class UpdateJobSetsService { for (const jobSet of jobSets) { try { + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } + const apiResponse = await this.submitApi.reprioritizeJobs( { body: { @@ -73,7 +83,7 @@ export class UpdateJobSetsService { newPriority: newPriority, }, }, - accessToken === undefined ? undefined : { headers: getAuthorizationHeaders(accessToken) }, + { headers }, ) if (apiResponse == null || apiResponse.reprioritizationResults == null) { const errorMessage = "No reprioritizationResults found in response body" diff --git a/internal/lookout/ui/src/services/lookoutV2/UpdateJobsService.ts b/internal/lookout/ui/src/services/lookoutV2/UpdateJobsService.ts index dbd705973af..dface590767 100644 --- a/internal/lookout/ui/src/services/lookoutV2/UpdateJobsService.ts +++ b/internal/lookout/ui/src/services/lookoutV2/UpdateJobsService.ts @@ -1,7 +1,7 @@ import _ from "lodash" import { Job, JobId } from "../../models/lookoutV2Models" -import { getAuthorizationHeaders } from "../../oidc" +import { appendAuthorizationHeaders } from "../../oidcAuth" import { SubmitApi } from "../../openapi/armada" import { getErrorMessage } from "../../utils" @@ -27,6 +27,10 @@ export class UpdateJobsService { for (const [queue, jobSetMap] of chunks) { for (const [jobSet, batches] of jobSetMap) { for (const batch of batches) { + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } apiResponsePromises.push({ promise: this.submitApi.cancelJobs( { @@ -37,7 +41,7 @@ export class UpdateJobsService { reason: reason, }, }, - accessToken === undefined ? undefined : { headers: getAuthorizationHeaders(accessToken) }, + { headers }, ), jobIds: batch, }) @@ -83,6 +87,10 @@ export class UpdateJobsService { for (const [queue, jobSetMap] of chunks) { for (const [jobSet, batches] of jobSetMap) { for (const batch of batches) { + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } apiResponsePromises.push({ promise: this.submitApi.reprioritizeJobs( { @@ -93,7 +101,7 @@ export class UpdateJobsService { newPriority: newPriority, }, }, - accessToken === undefined ? undefined : { headers: getAuthorizationHeaders(accessToken) }, + { headers }, ), jobIds: batch, }) diff --git a/internal/lookout/ui/src/services/lookoutV2/useGetJobSchedulingReport.ts b/internal/lookout/ui/src/services/lookoutV2/useGetJobSchedulingReport.ts index 82f710d50a3..27da4dc2f3d 100644 --- a/internal/lookout/ui/src/services/lookoutV2/useGetJobSchedulingReport.ts +++ b/internal/lookout/ui/src/services/lookoutV2/useGetJobSchedulingReport.ts @@ -1,12 +1,12 @@ import { useQuery } from "@tanstack/react-query" import { useGetUiConfig } from "./useGetUiConfig" -import { getAccessToken, getAuthorizationHeaders, useUserManager } from "../../oidc" +import { appendAuthorizationHeaders, useGetAccessToken } from "../../oidcAuth" import { SchedulerReportingApi, Configuration, SchedulerobjectsJobReport } from "../../openapi/schedulerobjects" import { getErrorMessage } from "../../utils" export const useGetJobSchedulingReport = (jobId: string, enabled = true) => { - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() const { data: uiConfig } = useGetUiConfig(enabled) const armadaApiBaseUrl = uiConfig?.armadaApiBaseUrl @@ -21,12 +21,13 @@ export const useGetJobSchedulingReport = (jobId: string, enabled = true) => { queryKey: ["getJobSchedulingReport", jobId], queryFn: async ({ signal }) => { try { - const accessToken = userManager === undefined ? undefined : await getAccessToken(userManager) + const accessToken = await getAccessToken() + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } - return await schedulerReportingApi.getJobReport( - { jobId }, - { headers: accessToken ? getAuthorizationHeaders(accessToken) : undefined, signal }, - ) + return await schedulerReportingApi.getJobReport({ jobId }, { headers, signal }) } catch (e) { throw await getErrorMessage(e) } diff --git a/internal/lookout/ui/src/services/lookoutV2/useGetLogs.ts b/internal/lookout/ui/src/services/lookoutV2/useGetLogs.ts index 6f727bbaccb..4b44f2e4de7 100644 --- a/internal/lookout/ui/src/services/lookoutV2/useGetLogs.ts +++ b/internal/lookout/ui/src/services/lookoutV2/useGetLogs.ts @@ -1,7 +1,7 @@ import { InfiniteData, useInfiniteQuery } from "@tanstack/react-query" import { LogLine } from "./LogService" -import { getAccessToken, useUserManager } from "../../oidc" +import { useGetAccessToken } from "../../oidcAuth" import { getErrorMessage } from "../../utils" import { useServices } from "../context" @@ -16,7 +16,7 @@ export const useGetLogs = ( enabled = true, ) => { const { v2LogService } = useServices() - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() return useInfiniteQuery< LogLine[], @@ -28,7 +28,7 @@ export const useGetLogs = ( queryKey: ["getLogs", cluster, namespace, jobId, container, loadFromStart], queryFn: async ({ pageParam, signal }) => { try { - const accessToken = userManager && (await getAccessToken(userManager)) + const accessToken = await getAccessToken() const logLines = await v2LogService.getLogs( cluster, namespace, diff --git a/internal/lookout/ui/src/services/lookoutV2/useGetQueueSchedulingReport.ts b/internal/lookout/ui/src/services/lookoutV2/useGetQueueSchedulingReport.ts index 09299121b55..6b7be1a616d 100644 --- a/internal/lookout/ui/src/services/lookoutV2/useGetQueueSchedulingReport.ts +++ b/internal/lookout/ui/src/services/lookoutV2/useGetQueueSchedulingReport.ts @@ -1,12 +1,12 @@ import { useQuery } from "@tanstack/react-query" import { useGetUiConfig } from "./useGetUiConfig" -import { getAccessToken, getAuthorizationHeaders, useUserManager } from "../../oidc" +import { appendAuthorizationHeaders, useGetAccessToken } from "../../oidcAuth" import { SchedulerReportingApi, Configuration, SchedulerobjectsQueueReport } from "../../openapi/schedulerobjects" import { getErrorMessage } from "../../utils" export const useGetQueueSchedulingReport = (queueName: string, verbosity: number, enabled = true) => { - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() const { data: uiConfig } = useGetUiConfig(enabled) const armadaApiBaseUrl = uiConfig?.armadaApiBaseUrl @@ -21,12 +21,13 @@ export const useGetQueueSchedulingReport = (queueName: string, verbosity: number queryKey: ["getQueueSchedulingReport", queueName, verbosity], queryFn: async ({ signal }) => { try { - const accessToken = userManager === undefined ? undefined : await getAccessToken(userManager) + const accessToken = await getAccessToken() + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } - return await schedulerReportingApi.getQueueReport( - { queueName, verbosity }, - { headers: accessToken ? getAuthorizationHeaders(accessToken) : undefined, signal }, - ) + return await schedulerReportingApi.getQueueReport({ queueName, verbosity }, { headers, signal }) } catch (e) { throw await getErrorMessage(e) } diff --git a/internal/lookout/ui/src/services/lookoutV2/useGetSchedulingReport.ts b/internal/lookout/ui/src/services/lookoutV2/useGetSchedulingReport.ts index 2633596a532..07cd76bbed1 100644 --- a/internal/lookout/ui/src/services/lookoutV2/useGetSchedulingReport.ts +++ b/internal/lookout/ui/src/services/lookoutV2/useGetSchedulingReport.ts @@ -1,12 +1,12 @@ import { useQuery } from "@tanstack/react-query" import { useGetUiConfig } from "./useGetUiConfig" -import { getAccessToken, getAuthorizationHeaders, useUserManager } from "../../oidc" +import { appendAuthorizationHeaders, useGetAccessToken } from "../../oidcAuth" import { SchedulerReportingApi, Configuration, SchedulerobjectsSchedulingReport } from "../../openapi/schedulerobjects" import { getErrorMessage } from "../../utils" export const useGetSchedulingReport = (verbosity: number, enabled = true) => { - const userManager = useUserManager() + const getAccessToken = useGetAccessToken() const { data: uiConfig } = useGetUiConfig(enabled) const armadaApiBaseUrl = uiConfig?.armadaApiBaseUrl @@ -21,12 +21,13 @@ export const useGetSchedulingReport = (verbosity: number, enabled = true) => { queryKey: ["getSchedulingReport", verbosity], queryFn: async ({ signal }) => { try { - const accessToken = userManager === undefined ? undefined : await getAccessToken(userManager) + const accessToken = await getAccessToken() + const headers = new Headers() + if (accessToken) { + appendAuthorizationHeaders(headers, accessToken) + } - return await schedulerReportingApi.getSchedulingReport( - { verbosity }, - { headers: accessToken ? getAuthorizationHeaders(accessToken) : undefined, signal }, - ) + return await schedulerReportingApi.getSchedulingReport({ verbosity }, { headers, signal }) } catch (e) { throw await getErrorMessage(e) } diff --git a/internal/lookout/ui/src/utils.tsx b/internal/lookout/ui/src/utils.tsx index a458e946f89..42b48be57bb 100644 --- a/internal/lookout/ui/src/utils.tsx +++ b/internal/lookout/ui/src/utils.tsx @@ -2,6 +2,7 @@ import { Component, FC } from "react" import { Location, NavigateFunction, Params, useLocation, useNavigate, useParams } from "react-router-dom" +import { OIDC_REDIRECT_PATHNAME } from "./oidcAuth/OidcAuthProvider" import { BinocularsApi, Configuration, ConfigurationParameters } from "./openapi/binoculars" export interface OidcConfig { @@ -116,7 +117,7 @@ export async function getUIConfig(): Promise { break } - if (window.location.pathname === "/oidc") config.oidcEnabled = true + if (window.location.pathname === OIDC_REDIRECT_PATHNAME) config.oidcEnabled = true const backend = searchParams.get("backend") if (backend) config.backend = backend