diff --git a/src/Components/CreateImageWizard/CreateImageWizard.tsx b/src/Components/CreateImageWizard/CreateImageWizard.tsx index f81211e3a..69ea205ee 100644 --- a/src/Components/CreateImageWizard/CreateImageWizard.tsx +++ b/src/Components/CreateImageWizard/CreateImageWizard.tsx @@ -39,6 +39,7 @@ import { useDetailsValidation, useRegistrationValidation, useHostnameValidation, + useUsersValidation, } from './utilities/useValidation'; import { isAwsAccountIdValid, @@ -225,6 +226,8 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { const firstBootValidation = useFirstBootValidation(); // Details const detailsValidation = useDetailsValidation(); + // Users + const usersValidation = useUsersValidation(); let startIndex = 1; // default index if (isEdit) { @@ -457,7 +460,9 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { key="wizard-users" isHidden={!isUsersEnabled} footer={ - + } > diff --git a/src/Components/CreateImageWizard/steps/Users/component/UserInfo.tsx b/src/Components/CreateImageWizard/steps/Users/component/UserInfo.tsx index 48b433cdc..ad633f724 100644 --- a/src/Components/CreateImageWizard/steps/Users/component/UserInfo.tsx +++ b/src/Components/CreateImageWizard/steps/Users/component/UserInfo.tsx @@ -13,6 +13,7 @@ import { setUserPasswordByIndex, setUserSshKeyByIndex, } from '../../../../../store/wizardSlice'; +import { useUsersValidation } from '../../../utilities/useValidation'; import { HookValidatedInput } from '../../../ValidatedTextInput'; const UserInfo = () => { const dispatch = useAppDispatch(); @@ -45,10 +46,7 @@ const UserInfo = () => { dispatch(setUserSshKeyByIndex({ index: index, sshKey: value })); }; - const stepValidation = { - errors: {}, - disabledNext: false, - }; + const stepValidation = useUsersValidation(); return ( <> diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx index 2307c6d2d..91b2152ce 100644 --- a/src/Components/CreateImageWizard/utilities/useValidation.tsx +++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx @@ -19,6 +19,8 @@ import { selectActivationKey, selectRegistrationType, selectHostname, + selectUserNameByIndex, + selectUsers, } from '../../../store/wizardSlice'; import { getDuplicateMountPoints, @@ -27,6 +29,7 @@ import { isMountpointMinSizeValid, isSnapshotValid, isHostnameValid, + isUserNameValid, } from '../validators'; export type StepValidation = { @@ -155,6 +158,26 @@ export function useHostnameValidation(): StepValidation { return { errors: {}, disabledNext: false }; } +export function useUsersValidation(): StepValidation { + const index = 0; + const userNameSelector = selectUserNameByIndex(index); + const userName = useAppSelector(userNameSelector); + const userNameValid = isUserNameValid(userName); + const users = useAppSelector(selectUsers); + const canProceed = + // Case 1: there is no users + users.length === 0 || + // Case 2: All fields are empty + userNameValid; + + return { + errors: { + userName: !userNameValid ? 'Invalid user name' : '', + }, + disabledNext: !canProceed, + }; +} + export function useDetailsValidation(): StepValidation { const name = useAppSelector(selectBlueprintName); const description = useAppSelector(selectBlueprintDescription); diff --git a/src/Components/CreateImageWizard/validators.ts b/src/Components/CreateImageWizard/validators.ts index 9704b36e2..74950058d 100644 --- a/src/Components/CreateImageWizard/validators.ts +++ b/src/Components/CreateImageWizard/validators.ts @@ -67,6 +67,19 @@ export const isFileSystemConfigValid = (partitions: Partition[]) => { return duplicates.length === 0; }; +export const isUserNameValid = (userName: string) => { + const isLengthValid = + userName !== undefined && userName.length >= 1 && userName.length <= 32; + + // Check if the username follows the pattern: + // Starts and ends with a valid character (not a dot). + // Can contain alphanumeric characters, underscores, hyphens, and periods in the middle. + const isPatternValid = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9_]$/.test( + userName + ); + return isLengthValid && isPatternValid; +}; + export const getDuplicateMountPoints = (partitions: Partition[]): string[] => { const mountPointSet: Set = new Set(); const duplicates: string[] = [];