From 2ee812efe059acbc327258e33e5c26cd22d6f26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Wed, 14 May 2025 10:34:28 +0200 Subject: [PATCH] Wizard: refactor Satellite validation The Satellite token navigation was a bit too overengineered, refactoring for better readability. --- .../utilities/useValidation.tsx | 86 +++++++++++-------- .../steps/Registration/Registration.test.tsx | 4 +- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx index b8cf10ed..68cfa8f5 100644 --- a/src/Components/CreateImageWizard/utilities/useValidation.tsx +++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx @@ -112,52 +112,62 @@ type ValidationState = { ruleCharacters: HelperTextVariant; }; +function tokenValidityRemaining(expireTimeInSeconds: number): number { + const currentTimeSeconds = Math.floor(Date.now() / 1000); + return expireTimeInSeconds - currentTimeSeconds; +} + +function getTokenExpirationTime(token: string): number | undefined { + try { + const decoded = jwtDecode(token) as { exp?: number }; + return decoded.exp; + } catch { + return undefined; + } +} + +function getExpirationString(totalSeconds: number): string | undefined { + const hours = Math.floor(totalSeconds / 3600); + + if (hours > 25) { + return undefined; + } + + if (hours > 0) { + return `${hours} hour${hours > 1 ? 's' : ''}`; + } + return 'less than an hour'; +} + export function validateSatelliteToken( registrationCommand: string | undefined ) { const errors: Record = {}; - if (registrationCommand === '' || !registrationCommand) { + if (registrationCommand === '') { errors.command = 'No registration command for Satellite registration'; return errors; } - try { - const match = registrationCommand?.match( - /Bearer\s+([\w-]+\.[\w-]+\.[\w-]+)/ - ); - if (!match) { - Object.assign(errors, { command: 'Invalid or missing token' }); - } else { - const token = match[1]; - const decoded = jwtDecode(token); - const currentTimeSeconds = Date.now() / 1000; - const dayInSeconds = 86400; - if (decoded.exp && decoded.exp < currentTimeSeconds + dayInSeconds) { - const expirationDate = new Date(decoded.exp * 1000); - let relativeTimeString; - const secondsRemaining = decoded.exp - currentTimeSeconds; - if (secondsRemaining < 1) { - relativeTimeString = `is expired`; - } else if (secondsRemaining < 60) { - relativeTimeString = `will expire in less than a minute`; - } else if (secondsRemaining < 3600) { - const minutesLeft = Math.floor(secondsRemaining / 60); - relativeTimeString = `will expire in approximately ${minutesLeft} minute${ - minutesLeft > 1 ? 's' : '' - }`; - } else { - const hoursLeftExact = secondsRemaining / 3600; - const numHours = Math.round(hoursLeftExact); - relativeTimeString = `will expire in approximately ${numHours} hour${ - numHours > 1 ? 's' : '' - }`; - } - errors.expired = `The token ${relativeTimeString}. Expiration date: ${expirationDate.toString()}. Check out the Satellite documentation to extend the Token lifetime.`; - } - } - } catch { + const match = registrationCommand?.match(/Bearer\s+([\w-]+\.[\w-]+\.[\w-]+)/); + if (!match || match.length < 2) { errors.command = 'Invalid or missing token'; + return errors; } + const expiresInSeconds = getTokenExpirationTime(match[1]); + if (expiresInSeconds === undefined) { + errors.command = 'Invalid or missing token'; + return errors; + } + const tokenRemainingS = tokenValidityRemaining(expiresInSeconds); + if (tokenRemainingS <= 0) { + errors.command = `The token is expired. Check out the Satellite documentation to extend the token lifetime.`; + return errors; + } + const expirationString = getExpirationString(tokenRemainingS); + if (expirationString !== undefined) { + errors.expired = `The token expires in ${expirationString}. Check out the Satellite documentation to extend the token lifetime.`; + } + return errors; } @@ -214,7 +224,9 @@ export function useRegistrationValidation(): StepValidation { return { errors: errors, disabledNext: - Object.keys(errors).filter((key) => key !== 'expired').length > 0, + Object.keys(errors).some((key) => key !== 'expired') || + !caCertificate || + !registrationCommand, }; } diff --git a/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx b/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx index 2e42b37a..9677db26 100644 --- a/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Registration/Registration.test.tsx @@ -101,7 +101,9 @@ const addSatelliteRegistrationCommandViaKeyDown = async (command: string) => { /registration command/i ); + await waitFor(() => user.clear(satelliteRegistrationCommand)); await waitFor(() => user.type(satelliteRegistrationCommand, command)); + satelliteRegistrationCommand.blur(); }; const uploadFile = async (scriptName: string): Promise => { @@ -435,7 +437,7 @@ describe('Registration request generated correctly', () => { ); const expiredTokenHelper = await screen.findByText( - /The token is expired. Expiration date/i + /The token is expired./i ); await waitFor(() => expect(expiredTokenHelper).toBeInTheDocument());