Wizard: add details for Satellite token expiration

This commit adds a more detailed information about when does the token
used in Satellite command expire.
This commit is contained in:
Anna Vítová 2025-05-12 11:51:23 +02:00 committed by Klara Simickova
parent daa6e59bc0
commit 4192ada532
3 changed files with 61 additions and 37 deletions

View file

@ -50,7 +50,7 @@ const SatelliteRegistration = () => {
return ( return (
<> <>
<SatelliteRegistrationCommand /> <SatelliteRegistrationCommand />
<FormGroup label="Certificate authority (CA)" isRequired> <FormGroup label="Certificate authority (CA) for Satellite" isRequired>
<FileUpload <FileUpload
id="text-file-with-restrictions-example" id="text-file-with-restrictions-example"
type="text" type="text"
@ -93,6 +93,12 @@ const SatelliteRegistration = () => {
? 'Certificate was uploaded' ? 'Certificate was uploaded'
: 'Drag and drop a valid certificate file or upload one'} : 'Drag and drop a valid certificate file or upload one'}
</HelperTextItem> </HelperTextItem>
{(isRejected || validated !== 'success') && (
<HelperTextItem>
You can find this certificate at{' '}
<i>http://satellite.example.com</i>/pub/katello-server-ca.crt
</HelperTextItem>
)}
</HelperText> </HelperText>
</FormHelperText> </FormHelperText>
</FormGroup> </FormGroup>

View file

@ -112,6 +112,55 @@ type ValidationState = {
ruleCharacters: HelperTextVariant; ruleCharacters: HelperTextVariant;
}; };
export function validateSatelliteToken(
registrationCommand: string | undefined
) {
const errors: Record<string, string> = {};
if (registrationCommand === '' || !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 {
errors.command = 'Invalid or missing token';
}
return errors;
}
export function useRegistrationValidation(): StepValidation { export function useRegistrationValidation(): StepValidation {
const registrationType = useAppSelector(selectRegistrationType); const registrationType = useAppSelector(selectRegistrationType);
const activationKey = useAppSelector(selectActivationKey); const activationKey = useAppSelector(selectActivationKey);
@ -154,44 +203,13 @@ export function useRegistrationValidation(): StepValidation {
'Valid certificate must be present if you are registering Satellite.', 'Valid certificate must be present if you are registering Satellite.',
}); });
} }
if (registrationCommand === '' || !registrationCommand) { const tokenErrors = validateSatelliteToken(registrationCommand);
Object.assign(errors, { Object.assign(errors, tokenErrors);
command: 'No registration command for Satellite registration',
});
}
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);
if (decoded.exp) {
const currentTimeSeconds = Date.now() / 1000;
const dayInSeconds = 86400;
if (decoded.exp < currentTimeSeconds + dayInSeconds) {
const expirationDate = new Date(decoded.exp * 1000);
Object.assign(errors, {
expired:
'The token is already expired or will expire by next day. Expiration date: ' +
expirationDate,
});
return {
errors: errors,
disabledNext: caCertificate === undefined,
};
}
}
}
} catch {
Object.assign(errors, { command: 'Invalid or missing token' });
}
return { return {
errors: errors, errors: errors,
disabledNext: disabledNext:
Object.keys(errors).length > 0 || caCertificate === undefined, Object.keys(errors).filter((key) => key !== 'expired').length > 0,
}; };
} }

View file

@ -435,7 +435,7 @@ describe('Registration request generated correctly', () => {
); );
const expiredTokenHelper = await screen.findByText( const expiredTokenHelper = await screen.findByText(
/The token is already expired or will expire by next day. Expiration date/i /The token is expired. Expiration date/i
); );
await waitFor(() => expect(expiredTokenHelper).toBeInTheDocument()); await waitFor(() => expect(expiredTokenHelper).toBeInTheDocument());