From a9cbf28cf5d0a4e9d4a3a29c763efed4469c5998 Mon Sep 17 00:00:00 2001 From: rushannanayakkara Date: Thu, 21 Mar 2024 11:45:48 +0530 Subject: [PATCH 01/23] Add config to enable SMS based password recovery --- .../src/extensions/i18n/models/extensions.ts | 3 + .../i18n/resources/en-US/extensions.ts | 32 ++- .../governance-connector-constants.ts | 10 +- .../server-configurations-constants.ts | 3 + .../forms/password-recovery-form.tsx | 203 +++++++++++++++++- .../pages/connector-edit-page.tsx | 35 ++- .../settings/edit-connector.tsx | 5 +- 7 files changed, 277 insertions(+), 14 deletions(-) diff --git a/apps/console/src/extensions/i18n/models/extensions.ts b/apps/console/src/extensions/i18n/models/extensions.ts index 628856a345d..f54248dca1b 100755 --- a/apps/console/src/extensions/i18n/models/extensions.ts +++ b/apps/console/src/extensions/i18n/models/extensions.ts @@ -2993,8 +2993,11 @@ export interface Extensions { form: { fields: { enable: FormAttributes; + enableSMSBasedRecovery: FormAttributes; expiryTime: FormAttributes; notifySuccess: FormAttributes; + smsOtpExpiryTime: FormAttributes; + smsOtpRegex: FormAttributes; }; }; connectorDescription: string; diff --git a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts index dab1e513189..5c458d1cf33 100755 --- a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts +++ b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts @@ -3502,6 +3502,10 @@ export const extensions: Extensions = { hint: "Enabling this will let the users reset their password using an email.", label: "Enable" }, + enableSMSBasedRecovery: { + hint: "This specifies whether to send an SMS OTP to the mobile.", + label: "Enable SMS based recovery" + }, expiryTime: { hint: "Password recovery link expiry time in minutes.", label: "Recovery link expiry time", @@ -3521,6 +3525,31 @@ export const extensions: Extensions = { "This specifies whether to notify the user via an email when password " + "recovery is successful.", label: "Notify on successful recovery" + }, + smsOtpExpiryTime: { + hint: "SMS OTP expiry time in minutes.", + label: "SMS OTP expiry time", + placeholder: "Enter expiry time", + validations: { + invalid: "SMS OTP expiry time should be an integer.", + empty: "SMS OTP expiry time cannot be empty.", + range: + "SMS OTP expiry time should be between 1 minute & 1440 minutes " + + "(1 day).", + maxLengthReached: + "SMS OTP expiry time should be a number with 4 or less digits." + } + }, + smsOtpRegex: { + hint: "Regex for SMS OTP in format [allowed characters]{length}. " + + "Supported character ranges are a-z, A-Z, 0-9. Minimum OTP length is 4.", + label: "SMS OTP Regex", + placeholder: "Enter Regex", + validations: { + empty: "SMS OTP regex cannot be empty.", + maxLengthReached: + "SMS OTP expiry time should be a string with 20 or less characters." + } } } }, @@ -3537,8 +3566,7 @@ export const extensions: Extensions = { } }, subHeading: - "Enable self-service password recovery for users " + - "on the login page.\nThe user will receive a password reset link via email upon request." + "Enable self-service password recovery for users on the login page." }, subHeading: "Account Recovery related settings." }, diff --git a/apps/console/src/features/server-configurations/constants/governance-connector-constants.ts b/apps/console/src/features/server-configurations/constants/governance-connector-constants.ts index d86c1133278..34f17953961 100644 --- a/apps/console/src/features/server-configurations/constants/governance-connector-constants.ts +++ b/apps/console/src/features/server-configurations/constants/governance-connector-constants.ts @@ -60,12 +60,20 @@ export class GovernanceConnectorConstants { EXPIRY_TIME_MAX_VALUE: number; EXPIRY_TIME_MIN_LENGTH: number; EXPIRY_TIME_MIN_VALUE: number; + SMS_OTP_EXPIRY_TIME_MAX_LENGTH: number; + SMS_OTP_EXPIRY_TIME_MAX_VALUE: number; + SMS_OTP_REGEX_MAX_LENGTH: number; + SMS_OTP_REGEX_MIN_LENGTH: number; } = { EXPIRY_TIME_MAX_LENGTH: 5, EXPIRY_TIME_MAX_VALUE: 10080, EXPIRY_TIME_MIN_LENGTH: 1, - EXPIRY_TIME_MIN_VALUE: 1 + EXPIRY_TIME_MIN_VALUE: 1, + SMS_OTP_EXPIRY_TIME_MAX_LENGTH: 4, + SMS_OTP_EXPIRY_TIME_MAX_VALUE: 1440, + SMS_OTP_REGEX_MAX_LENGTH: 20, + SMS_OTP_REGEX_MIN_LENGTH: 8 }; /** diff --git a/apps/console/src/features/server-configurations/constants/server-configurations-constants.ts b/apps/console/src/features/server-configurations/constants/server-configurations-constants.ts index 36aeef4f822..fd387c0d888 100644 --- a/apps/console/src/features/server-configurations/constants/server-configurations-constants.ts +++ b/apps/console/src/features/server-configurations/constants/server-configurations-constants.ts @@ -237,6 +237,9 @@ export class ServerConfigurationsConstants { public static readonly RECOVERY_CALLBACK_REGEX: string = "Recovery.CallbackRegex"; public static readonly PASSWORD_RECOVERY_QUESTION_FORCED_ENABLE: string = "Recovery.Question.Password.Forced.Enable"; + public static readonly RECOVERY_EMAIL_LINK_ENABLE: string = "Recovery.Notification.Password.emailLink.Enable"; + public static readonly RECOVERY_SMS_OTP_ENABLE: string = "Recovery.Notification.Password.smsOtp.Enable"; + public static readonly RECOVERY_SMS_OTP_REGEX: string = "Recovery.Notification.Password.smsOtp.Regex"; /** * Login policies - account locking API Keyword constants. diff --git a/apps/console/src/features/server-configurations/forms/password-recovery-form.tsx b/apps/console/src/features/server-configurations/forms/password-recovery-form.tsx index e1477ffa869..f6fcfe82892 100644 --- a/apps/console/src/features/server-configurations/forms/password-recovery-form.tsx +++ b/apps/console/src/features/server-configurations/forms/password-recovery-form.tsx @@ -71,6 +71,22 @@ interface PasswordRecoveryFormInitialValuesInterface { * Notify user on successful password recovery. */ notifySuccess: boolean; + /** + * Whether email based recovery is enabled. + */ + enableEmailBasedRecovery: boolean; + /** + * Whether SMS based recovery is enabled. + */ + enableSMSBasedRecovery: boolean; + /** + * SMS OTP expiry time. + */ + smsOtpExpiryTime: string; + /** + * SMS OTP regex. + */ + smsOtpRegex: string; } /** @@ -81,11 +97,23 @@ export interface PasswordRecoveryFormErrorValidationsInterface { * Recovery link expiry time field. */ expiryTime: string; + /** + * Sms otp expiry time field + */ + smsOtpExpiryTime: string; + /** + * SMS OTP regex field. + */ + smsOtpRegex: string; } const allowedConnectorFields: string[] = [ ServerConfigurationsConstants.NOTIFY_SUCCESS, - ServerConfigurationsConstants.RECOVERY_LINK_EXPIRY_TIME + ServerConfigurationsConstants.RECOVERY_LINK_EXPIRY_TIME, + ServerConfigurationsConstants.RECOVERY_EMAIL_LINK_ENABLE, + ServerConfigurationsConstants.RECOVERY_SMS_OTP_ENABLE, + ServerConfigurationsConstants.RECOVERY_SMS_EXPIRY_TIME, + ServerConfigurationsConstants.RECOVERY_SMS_OTP_REGEX ]; const FORM_ID: string = "governance-connectors-password-recovery-form"; @@ -112,6 +140,8 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent(undefined); + const [ isEmailRecoveryEnabled, setIsEmailRecoveryEnabled ] = useState(false); + const [ isSMSRecoveryEnabled, setIsSMSRecoveryEnabled ] = useState(false); /** * Flattens and resolved form initial values and field metadata. @@ -136,10 +166,32 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent { const errors: PasswordRecoveryFormErrorValidationsInterface = { - expiryTime: undefined + expiryTime: undefined, + smsOtpExpiryTime: undefined, + smsOtpRegex: undefined }; if (!values.expiryTime) { @@ -170,12 +224,49 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent GovernanceConnectorConstants + .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.SMS_OTP_EXPIRY_TIME_MAX_VALUE)) { + // Check for invalid range. + errors.smsOtpExpiryTime = t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpExpiryTime.validations.range"); + } else if (values.smsOtpExpiryTime && + !FormValidation.isLengthValid(values.smsOtpExpiryTime as string, GovernanceConnectorConstants + .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.SMS_OTP_EXPIRY_TIME_MAX_LENGTH)) { + // Check for invalid input length. + errors.smsOtpExpiryTime = t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpExpiryTime.validations.maxLengthReached"); + } else if (!values.smsOtpRegex) { + // Check for required error. + errors.smsOtpRegex = t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpRegex.validations.empty"); + } else if (values.smsOtpRegex && + !FormValidation.isLengthValid(values.smsOtpRegex as string, GovernanceConnectorConstants + .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.SMS_OTP_REGEX_MAX_LENGTH)) { + // Check for invalid input length. + errors.smsOtpRegex = t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpRegex.validations.maxLengthReached"); } return errors; @@ -190,14 +281,30 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent) => { const data: { "Recovery.ExpiryTime": any; + "Recovery.Notification.Password.emailLink.Enable": boolean; + "Recovery.Notification.Password.ExpiryTime.smsOtp": number; + "Recovery.Notification.Password.smsOtp.Enable": boolean; + "Recovery.Notification.Password.smsOtp.Regex": any; "Recovery.NotifySuccess": boolean; } = { "Recovery.ExpiryTime": values.expiryTime !== undefined ? values.expiryTime : initialConnectorValues?.expiryTime, + "Recovery.Notification.Password.ExpiryTime.smsOtp": values.smsOtpExpiryTime !== undefined + ? values.smsOtpExpiryTime + : initialConnectorValues?.smsOtpExpiryTime, + "Recovery.Notification.Password.emailLink.Enable": values.enableEmailBasedRecovery !== undefined + ? !!values.enableEmailBasedRecovery + : initialConnectorValues?.enableEmailBasedRecovery, + "Recovery.Notification.Password.smsOtp.Enable": values.enableSMSBasedRecovery !== undefined + ? !!values.enableSMSBasedRecovery + : initialConnectorValues?.enableSMSBasedRecovery, + "Recovery.Notification.Password.smsOtp.Regex": values.smsOtpRegex !== undefined + ? values.smsOtpRegex + : initialConnectorValues?.smsOtpRegex, "Recovery.NotifySuccess": values.notifySuccess !== undefined ? !!values.notifySuccess - : initialConnectorValues?.notifySuccess + : initialConnectorValues?.notifySuccess, }; return data; @@ -216,6 +323,96 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent + setIsSMSRecoveryEnabled(value) } + data-testid={ `${testId}-sms-based-recovery` } + /> + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.enableSMSBasedRecovery.hint") + } + + + + + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpExpiryTime.hint") + } + + + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpRegex.hint") + } + = serverConfigurationConfig.connectorToggleName[ connector?.name ] && serverConfigurationConfig.autoEnableConnectorToggleProperty ) { - data.properties.push({ - name: GovernanceConnectorUtils.decodeConnectorPropertyName( - serverConfigurationConfig.connectorToggleName[ connector?.name ] - ), - value: "true" - }); + if (connectorId === ServerConfigurationsConstants.ACCOUNT_RECOVERY_CONNECTOR_ID) { + if (ServerConfigurationsConstants.RECOVERY_EMAIL_LINK_ENABLE in values + || ServerConfigurationsConstants.RECOVERY_SMS_OTP_ENABLE in values) { + const emailLinkEnabled: boolean = values[ ServerConfigurationsConstants + .RECOVERY_EMAIL_LINK_ENABLE ] === true; + const smsOtpEnabled: boolean = values[ ServerConfigurationsConstants + .RECOVERY_SMS_OTP_ENABLE ] === true; + const value: string = emailLinkEnabled || smsOtpEnabled + ? "true" + : "false"; + + data.properties.push({ + name: GovernanceConnectorUtils.decodeConnectorPropertyName( + serverConfigurationConfig.connectorToggleName[ connector?.name ] + ), + value: value + }); + } + } + else { + data.properties.push({ + name: GovernanceConnectorUtils.decodeConnectorPropertyName( + serverConfigurationConfig.connectorToggleName[ connector?.name ] + ), + value: "true" + }); + } } setIsSubmitting(true); @@ -565,7 +586,7 @@ export const ConnectorEditPage: FunctionComponent = case ServerConfigurationsConstants.ACCOUNT_RECOVERY_CONNECTOR_ID: return type === "username" ? undefined - : ServerConfigurationsConstants.PASSWORD_RECOVERY_NOTIFICATION_BASED_ENABLE; + : null; case ServerConfigurationsConstants.ORGANIZATION_SELF_SERVICE_CONNECTOR_ID: return ServerConfigurationsConstants.ORGANIZATION_SELF_SERVICE_ENABLE; case ServerConfigurationsConstants.MULTI_ATTRIBUTE_LOGIN_CONNECTOR_ID: diff --git a/apps/console/src/features/server-configurations/settings/edit-connector.tsx b/apps/console/src/features/server-configurations/settings/edit-connector.tsx index 6a68a6be84a..c887110dca6 100644 --- a/apps/console/src/features/server-configurations/settings/edit-connector.tsx +++ b/apps/console/src/features/server-configurations/settings/edit-connector.tsx @@ -172,7 +172,10 @@ export const EditConnector: FunctionComponent = ( header={ resolveConnectorTitle(connector) } onPrimaryActionClick={ handleSelection } primaryAction={ "Configure" } - connectorEnabled={ enableOption } + connectorEnabled={ connector?.id === ServerConfigurationsConstants.ACCOUNT_RECOVERY_CONNECTOR_ID + ? undefined + : enableOption + } > ); From 68a08c954465a8adf88214c6138cc028886db555 Mon Sep 17 00:00:00 2001 From: rushannanayakkara Date: Fri, 5 Apr 2024 17:36:00 +0530 Subject: [PATCH 02/23] Add email link password recovery option to PasswordRecoveryConfigurationForm --- .../src/extensions/i18n/models/extensions.ts | 1 + .../i18n/resources/en-US/extensions.ts | 6 ++++- .../forms/password-recovery-form.tsx | 24 +++++++++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/console/src/extensions/i18n/models/extensions.ts b/apps/console/src/extensions/i18n/models/extensions.ts index 999c29de4e3..88f9c3f0f22 100755 --- a/apps/console/src/extensions/i18n/models/extensions.ts +++ b/apps/console/src/extensions/i18n/models/extensions.ts @@ -3020,6 +3020,7 @@ export interface Extensions { fields: { enable: FormAttributes; enableSMSBasedRecovery: FormAttributes; + enableEmailBasedRecovery: FormAttributes; expiryTime: FormAttributes; notifySuccess: FormAttributes; smsOtpExpiryTime: FormAttributes; diff --git a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts index 8243189554f..ce96b13fb7a 100755 --- a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts +++ b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts @@ -3586,7 +3586,11 @@ export const extensions: Extensions = { maxLengthReached: "SMS OTP expiry time should be a string with 20 or less characters." } - } + }, + enableEmailBasedRecovery: { + hint: "This specifies whether to send an recovery link to the email address.", + label: "Enable email link based recovery" + }, } }, connectorDescription: "Enable self-service password recovery for users " + "on the login page.", diff --git a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx index f6fcfe82892..84b6d49c96c 100644 --- a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx +++ b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx @@ -414,6 +414,25 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent setIsEmailRecoveryEnabled(value) } + data-testid={ `${testId}-email-link-based-recovery` } + /> + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.enableEmailBasedRecovery.hint") + } + + - + { t("extensions:manage.serverConfigurations.accountRecovery." + "passwordRecovery.form.fields.notifySuccess.hint") } - + { t("extensions:manage.serverConfigurations.accountRecovery." + "passwordRecovery.form.fields.expiryTime.hint") From 67339e35bdbda05223562c3824d4a97cd85cc888 Mon Sep 17 00:00:00 2001 From: rushannanayakkara Date: Wed, 17 Apr 2024 10:10:45 +0530 Subject: [PATCH 03/23] Replace sms otp regex with individual configs --- .../src/extensions/i18n/models/extensions.ts | 5 +- .../i18n/resources/en-US/extensions.ts | 25 ++- .../governance-connector-constants.ts | 12 +- .../server-configurations-constants.ts | 5 +- .../forms/password-recovery-form.tsx | 187 ++++++++++++++---- 5 files changed, 183 insertions(+), 51 deletions(-) diff --git a/apps/console/src/extensions/i18n/models/extensions.ts b/apps/console/src/extensions/i18n/models/extensions.ts index c7331e6e241..56f725bd40d 100755 --- a/apps/console/src/extensions/i18n/models/extensions.ts +++ b/apps/console/src/extensions/i18n/models/extensions.ts @@ -3040,7 +3040,10 @@ export interface Extensions { expiryTime: FormAttributes; notifySuccess: FormAttributes; smsOtpExpiryTime: FormAttributes; - smsOtpRegex: FormAttributes; + smsOtpUseUppercase: FormAttributes; + smsOtpUseLowercase: FormAttributes; + smsOtpUseNumeric: FormAttributes; + smsOtpLength: FormAttributes; }; }; connectorDescription: string; diff --git a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts index 0661229d043..cb1135a2936 100755 --- a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts +++ b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts @@ -3592,15 +3592,26 @@ export const extensions: Extensions = { "SMS OTP expiry time should be a number with 4 or less digits." } }, - smsOtpRegex: { - hint: "Regex for SMS OTP in format [allowed characters]{length}. " + - "Supported character ranges are a-z, A-Z, 0-9. Minimum OTP length is 4.", - label: "SMS OTP Regex", - placeholder: "Enter Regex", + smsOtpUseUppercase: { + hint: "This specifies whether to use upper case characters in the sms otp code.", + label: "Use upper case letters", + }, + smsOtpUseLowercase: { + hint: "This specifies whether to use lower case characters in the sms otp code.", + label: "Use lower case letters", + }, + smsOtpUseNumeric: { + hint: "This specifies whether to use numeric characters in the sms otp code.", + label: "Use numeric characters", + }, + smsOtpLength: { + hint: "SMS OTP length should be between 6 and 10", + label: "SMS OTP code length", + placeholder: "Enter length", validations: { - empty: "SMS OTP regex cannot be empty.", + empty: "SMS OTP length cannot be empty.", maxLengthReached: - "SMS OTP expiry time should be a string with 20 or less characters." + "SMS OTP length should be between 6 and 10 characters." } }, enableEmailBasedRecovery: { diff --git a/features/admin.server-configurations.v1/constants/governance-connector-constants.ts b/features/admin.server-configurations.v1/constants/governance-connector-constants.ts index 34f17953961..d2bf89907d7 100644 --- a/features/admin.server-configurations.v1/constants/governance-connector-constants.ts +++ b/features/admin.server-configurations.v1/constants/governance-connector-constants.ts @@ -62,8 +62,10 @@ export class GovernanceConnectorConstants { EXPIRY_TIME_MIN_VALUE: number; SMS_OTP_EXPIRY_TIME_MAX_LENGTH: number; SMS_OTP_EXPIRY_TIME_MAX_VALUE: number; - SMS_OTP_REGEX_MAX_LENGTH: number; - SMS_OTP_REGEX_MIN_LENGTH: number; + SMS_OTP_CODE_LENGTH_MAX_LENGTH: number; + SMS_OTP_CODE_LENGTH_MAX_VALUE: number; + SMS_OTP_CODE_LENGTH_MIN_LENGTH: number; + SMS_OTP_CODE_LENGTH_MIN_VALUE: number; } = { EXPIRY_TIME_MAX_LENGTH: 5, @@ -72,8 +74,10 @@ export class GovernanceConnectorConstants { EXPIRY_TIME_MIN_VALUE: 1, SMS_OTP_EXPIRY_TIME_MAX_LENGTH: 4, SMS_OTP_EXPIRY_TIME_MAX_VALUE: 1440, - SMS_OTP_REGEX_MAX_LENGTH: 20, - SMS_OTP_REGEX_MIN_LENGTH: 8 + SMS_OTP_CODE_LENGTH_MAX_LENGTH: 2, + SMS_OTP_CODE_LENGTH_MAX_VALUE: 10, + SMS_OTP_CODE_LENGTH_MIN_LENGTH: 1, + SMS_OTP_CODE_LENGTH_MIN_VALUE: 6 }; /** diff --git a/features/admin.server-configurations.v1/constants/server-configurations-constants.ts b/features/admin.server-configurations.v1/constants/server-configurations-constants.ts index fd387c0d888..8096f2aedc1 100644 --- a/features/admin.server-configurations.v1/constants/server-configurations-constants.ts +++ b/features/admin.server-configurations.v1/constants/server-configurations-constants.ts @@ -239,7 +239,10 @@ export class ServerConfigurationsConstants { "Recovery.Question.Password.Forced.Enable"; public static readonly RECOVERY_EMAIL_LINK_ENABLE: string = "Recovery.Notification.Password.emailLink.Enable"; public static readonly RECOVERY_SMS_OTP_ENABLE: string = "Recovery.Notification.Password.smsOtp.Enable"; - public static readonly RECOVERY_SMS_OTP_REGEX: string = "Recovery.Notification.Password.smsOtp.Regex"; + public static readonly RECOVERY_OTP_USE_UPPERCASE: string = "Recovery.Notification.Password.OTP.UseUppercaseCharactersInOTP"; + public static readonly RECOVERY_OTP_USE_LOWERCASE: string = "Recovery.Notification.Password.OTP.UseLowercaseCharactersInOTP"; + public static readonly RECOVERY_OTP_USE_NUMERIC: string = "Recovery.Notification.Password.OTP.UseNumbersInOTP"; + public static readonly RECOVERY_OTP_LENGTH: string = "Recovery.Notification.Password.OTP.OTPLength"; /** * Login policies - account locking API Keyword constants. diff --git a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx index 84b6d49c96c..5f4a6f29b07 100644 --- a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx +++ b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx @@ -84,9 +84,21 @@ interface PasswordRecoveryFormInitialValuesInterface { */ smsOtpExpiryTime: string; /** - * SMS OTP regex. + * Whether to use upper case letters in SMS OTP code. */ - smsOtpRegex: string; + smsOtpUseUppercase: boolean; + /** + * Whether to use lower case letters in SMS OTP code. + */ + smsOtpUseLowercase: boolean; + /** + * Whether to use numeric characters in SMS OTP code. + */ + smsOtpUseNumeric: boolean; + /** + * The length of the SMS OTP code. + */ + smsOtpLength: string; } /** @@ -102,9 +114,9 @@ export interface PasswordRecoveryFormErrorValidationsInterface { */ smsOtpExpiryTime: string; /** - * SMS OTP regex field. + * SMS OTP code length field. */ - smsOtpRegex: string; + smsOtpLength: string; } const allowedConnectorFields: string[] = [ @@ -113,7 +125,10 @@ const allowedConnectorFields: string[] = [ ServerConfigurationsConstants.RECOVERY_EMAIL_LINK_ENABLE, ServerConfigurationsConstants.RECOVERY_SMS_OTP_ENABLE, ServerConfigurationsConstants.RECOVERY_SMS_EXPIRY_TIME, - ServerConfigurationsConstants.RECOVERY_SMS_OTP_REGEX + ServerConfigurationsConstants.RECOVERY_OTP_USE_UPPERCASE, + ServerConfigurationsConstants.RECOVERY_OTP_USE_LOWERCASE, + ServerConfigurationsConstants.RECOVERY_OTP_USE_NUMERIC, + ServerConfigurationsConstants.RECOVERY_OTP_LENGTH ]; const FORM_ID: string = "governance-connectors-password-recovery-form"; @@ -181,11 +196,27 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent { - const errors: PasswordRecoveryFormErrorValidationsInterface = { expiryTime: undefined, smsOtpExpiryTime: undefined, - smsOtpRegex: undefined + smsOtpLength: undefined }; if (!values.expiryTime) { @@ -257,16 +287,18 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent GovernanceConnectorConstants + .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.SMS_OTP_CODE_LENGTH_MAX_VALUE) { // Check for invalid input length. - errors.smsOtpRegex = t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.smsOtpRegex.validations.maxLengthReached"); + console.log("INVALID OTP LENGTH"); + errors.smsOtpLength = t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpLength.validations.maxLengthReached"); } return errors; @@ -284,7 +316,10 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent +
SMS OTP Code Configuration
+ + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpUseUppercase.hint") + } + + {/* USE LOWERCASE */} + + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpUseLowercase.hint") + } + + + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.smsOtpUseNumeric.hint") + } + - + data-testid={ `${testId}-otp-length` } + > + + + { - t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.smsOtpRegex.hint") + "SMS OTP code length" } @@ -478,7 +589,7 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent From fd44065f7c1c9a7368d1959296e5f0f96973ccf4 Mon Sep 17 00:00:00 2001 From: rushannanayakkara Date: Wed, 17 Apr 2024 12:45:34 +0530 Subject: [PATCH 04/23] Restructure Password Recovery Form --- .../src/extensions/i18n/models/extensions.ts | 12 +- .../i18n/resources/en-US/extensions.ts | 40 +-- .../forms/password-recovery-form.tsx | 253 ++++++++++-------- 3 files changed, 164 insertions(+), 141 deletions(-) diff --git a/apps/console/src/extensions/i18n/models/extensions.ts b/apps/console/src/extensions/i18n/models/extensions.ts index 56f725bd40d..4bb42678734 100755 --- a/apps/console/src/extensions/i18n/models/extensions.ts +++ b/apps/console/src/extensions/i18n/models/extensions.ts @@ -3040,12 +3040,16 @@ export interface Extensions { expiryTime: FormAttributes; notifySuccess: FormAttributes; smsOtpExpiryTime: FormAttributes; - smsOtpUseUppercase: FormAttributes; - smsOtpUseLowercase: FormAttributes; - smsOtpUseNumeric: FormAttributes; - smsOtpLength: FormAttributes; + passwordRecoveryOtpUseUppercase: FormAttributes; + passwordRecoveryOtpUseLowercase: FormAttributes; + passwordRecoveryOtpUseNumeric: FormAttributes; + passwordRecoveryOtpLength: FormAttributes; }; }; + recoveryOptionSubHeadingEmailLink: string; + recoveryOptionSubHeadingSMS: string; + recoveryOptionHeading: string; + otpConfigHeading: string; connectorDescription: string; heading: string; notification: { diff --git a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts index cb1135a2936..c4636e563f1 100755 --- a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts +++ b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts @@ -3579,39 +3579,39 @@ export const extensions: Extensions = { label: "Notify on successful recovery" }, smsOtpExpiryTime: { - hint: "SMS OTP expiry time in minutes.", - label: "SMS OTP expiry time", + hint: "Password recovery OTP expiry time in minutes.", + label: "Password recovery OTP expiry time", placeholder: "Enter expiry time", validations: { - invalid: "SMS OTP expiry time should be an integer.", - empty: "SMS OTP expiry time cannot be empty.", + invalid: "Password recovery OTP expiry time should be an integer.", + empty: "Password recovery OTP expiry time cannot be empty.", range: - "SMS OTP expiry time should be between 1 minute & 1440 minutes " + + "Password recovery OTP expiry time should be between 1 minute & 1440 minutes " + "(1 day).", maxLengthReached: - "SMS OTP expiry time should be a number with 4 or less digits." + "Password recovery OTP expiry time should be a number with 4 or less digits." } }, - smsOtpUseUppercase: { - hint: "This specifies whether to use upper case characters in the sms otp code.", + passwordRecoveryOtpUseUppercase: { + hint: "This specifies whether to use upper case characters in the password recovery otp code.", label: "Use upper case letters", }, - smsOtpUseLowercase: { - hint: "This specifies whether to use lower case characters in the sms otp code.", + passwordRecoveryOtpUseLowercase: { + hint: "This specifies whether to use lower case characters in the password recovery otp code.", label: "Use lower case letters", }, - smsOtpUseNumeric: { - hint: "This specifies whether to use numeric characters in the sms otp code.", + passwordRecoveryOtpUseNumeric: { + hint: "This specifies whether to use numeric characters in the password recovery otp code.", label: "Use numeric characters", }, - smsOtpLength: { - hint: "SMS OTP length should be between 6 and 10", - label: "SMS OTP code length", - placeholder: "Enter length", + passwordRecoveryOtpLength: { + hint: "Password recovery OTP length in characters", + label: "Password recovery OTP code length", + placeholder: "Enter OTP code length", validations: { - empty: "SMS OTP length cannot be empty.", + empty: "Password recovery OTP length cannot be empty.", maxLengthReached: - "SMS OTP length should be between 6 and 10 characters." + "Password recovery OTP length should be between 6 and 10 characters." } }, enableEmailBasedRecovery: { @@ -3620,6 +3620,10 @@ export const extensions: Extensions = { }, } }, + recoveryOptionSubHeadingEmailLink: "Email Link", + recoveryOptionSubHeadingSMS: "SMS OTP", + recoveryOptionHeading: "Recovery Option Selection", + otpConfigHeading: "OTP Code Configuration", connectorDescription: "Enable self-service password recovery for users " + "on the login page.", heading: "Password Recovery", notification: { diff --git a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx index 5f4a6f29b07..e22bc827ea4 100644 --- a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx +++ b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx @@ -19,12 +19,12 @@ import { TestableComponentInterface } from "@wso2is/core/models"; import { CommonUtils } from "@wso2is/core/utils"; import { Field, Form } from "@wso2is/form"; -import { Hint } from "@wso2is/react-components"; +import { Hint, Heading } from "@wso2is/react-components"; import { FormValidation } from "@wso2is/validation"; import isEmpty from "lodash-es/isEmpty"; import React, { FunctionComponent, ReactElement, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Label } from "semantic-ui-react"; +import { Label, Divider } from "semantic-ui-react"; import { GovernanceConnectorConstants } from "../constants/governance-connector-constants"; import { ServerConfigurationsConstants } from "../constants/server-configurations-constants"; import { @@ -86,15 +86,15 @@ interface PasswordRecoveryFormInitialValuesInterface { /** * Whether to use upper case letters in SMS OTP code. */ - smsOtpUseUppercase: boolean; + passwordRecoveryOtpUseUppercase: boolean; /** * Whether to use lower case letters in SMS OTP code. */ - smsOtpUseLowercase: boolean; + passwordRecoveryOtpUseLowercase: boolean; /** * Whether to use numeric characters in SMS OTP code. */ - smsOtpUseNumeric: boolean; + passwordRecoveryOtpUseNumeric: boolean; /** * The length of the SMS OTP code. */ @@ -199,24 +199,23 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent + + {t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.recoveryOptionHeading")} + + + {t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.recoveryOptionSubHeadingEmailLink")} + + setIsEmailRecoveryEnabled(value) } + data-testid={ `${testId}-email-link-based-recovery` } + /> + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.enableEmailBasedRecovery.hint") + } + + + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.notifySuccess.hint") + } + + + + + + { + t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.expiryTime.hint") + } + + + + {t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.recoveryOptionSubHeadingSMS")} + -
SMS OTP Code Configuration
+ + + {t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.otpConfigHeading")} + - + { t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.smsOtpUseUppercase.hint") + "passwordRecovery.form.fields.passwordRecoveryOtpUseUppercase.hint") } - {/* USE LOWERCASE */} - + { t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.smsOtpUseLowercase.hint") + "passwordRecovery.form.fields.passwordRecoveryOtpUseLowercase.hint") } - + { t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.smsOtpUseNumeric.hint") + "passwordRecovery.form.fields.passwordRecoveryOtpUseNumeric.hint") } - - { - "SMS OTP code length" - } - - setIsEmailRecoveryEnabled(value) } - data-testid={ `${testId}-email-link-based-recovery` } - /> { t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.enableEmailBasedRecovery.hint") - } - - - - { - t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.notifySuccess.hint") - } - - - - - - { - t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.form.fields.expiryTime.hint") + "passwordRecovery.form.fields.passwordRecoveryOtpLength.hint") } Date: Wed, 17 Apr 2024 13:11:48 +0530 Subject: [PATCH 05/23] Block disabling the last enabled otp character set --- .../forms/password-recovery-form.tsx | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx index e22bc827ea4..e2fce1d06d1 100644 --- a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx +++ b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx @@ -155,8 +155,11 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent(undefined); - const [ isEmailRecoveryEnabled, setIsEmailRecoveryEnabled ] = useState(false); + const [ isEmailRecoveryEnabled, setIsEmailRecoveryEnabled ] = useState(false); const [ isSMSRecoveryEnabled, setIsSMSRecoveryEnabled ] = useState(false); + const [ isUpperCaseEnabled, setIsUpperCaseEnabled ] = useState(false); + const [ isLowerCaseEnabled, setIsLowerCaseEnabled ] = useState(false); + const [ isNumericEnabled, setIsNumericEnabled ] = useState(false); /** * Flattens and resolved form initial values and field metadata. @@ -222,6 +225,10 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent setIsUpperCaseEnabled(value) } data-testid={ `${testId}-sms-otp-uppercase` } /> @@ -550,7 +560,10 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent setIsLowerCaseEnabled(value) } data-testid={ `${testId}-sms-otp-lowercase` } /> @@ -567,7 +580,10 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent setIsNumericEnabled(value) } data-testid={ `${testId}-sms-otp-numeric` } /> @@ -603,7 +619,7 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent From 1ad0172c36d92ac74b73a75695b0f860fb1503b7 Mon Sep 17 00:00:00 2001 From: rushannanayakkara Date: Fri, 19 Apr 2024 08:21:52 +0530 Subject: [PATCH 06/23] Code clean up --- .../constants/server-configurations-constants.ts | 6 ++++-- .../forms/password-recovery-form.tsx | 11 ++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/features/admin.server-configurations.v1/constants/server-configurations-constants.ts b/features/admin.server-configurations.v1/constants/server-configurations-constants.ts index 8096f2aedc1..46e6e8bcf63 100644 --- a/features/admin.server-configurations.v1/constants/server-configurations-constants.ts +++ b/features/admin.server-configurations.v1/constants/server-configurations-constants.ts @@ -239,8 +239,10 @@ export class ServerConfigurationsConstants { "Recovery.Question.Password.Forced.Enable"; public static readonly RECOVERY_EMAIL_LINK_ENABLE: string = "Recovery.Notification.Password.emailLink.Enable"; public static readonly RECOVERY_SMS_OTP_ENABLE: string = "Recovery.Notification.Password.smsOtp.Enable"; - public static readonly RECOVERY_OTP_USE_UPPERCASE: string = "Recovery.Notification.Password.OTP.UseUppercaseCharactersInOTP"; - public static readonly RECOVERY_OTP_USE_LOWERCASE: string = "Recovery.Notification.Password.OTP.UseLowercaseCharactersInOTP"; + public static readonly RECOVERY_OTP_USE_UPPERCASE: string = + "Recovery.Notification.Password.OTP.UseUppercaseCharactersInOTP"; + public static readonly RECOVERY_OTP_USE_LOWERCASE: string = + "Recovery.Notification.Password.OTP.UseLowercaseCharactersInOTP"; public static readonly RECOVERY_OTP_USE_NUMERIC: string = "Recovery.Notification.Password.OTP.UseNumbersInOTP"; public static readonly RECOVERY_OTP_LENGTH: string = "Recovery.Notification.Password.OTP.OTPLength"; diff --git a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx index e2fce1d06d1..45836085f8c 100644 --- a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx +++ b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx @@ -19,12 +19,12 @@ import { TestableComponentInterface } from "@wso2is/core/models"; import { CommonUtils } from "@wso2is/core/utils"; import { Field, Form } from "@wso2is/form"; -import { Hint, Heading } from "@wso2is/react-components"; +import { Heading, Hint } from "@wso2is/react-components"; import { FormValidation } from "@wso2is/validation"; import isEmpty from "lodash-es/isEmpty"; import React, { FunctionComponent, ReactElement, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Label, Divider } from "semantic-ui-react"; +import { Divider, Label } from "semantic-ui-react"; import { GovernanceConnectorConstants } from "../constants/governance-connector-constants"; import { ServerConfigurationsConstants } from "../constants/server-configurations-constants"; import { @@ -287,12 +287,6 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent GovernanceConnectorConstants .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.SMS_OTP_CODE_LENGTH_MAX_VALUE) { // Check for invalid input length. - console.log("INVALID OTP LENGTH"); errors.smsOtpLength = t("extensions:manage.serverConfigurations.accountRecovery." + "passwordRecovery.form.fields.smsOtpLength.validations.maxLengthReached"); } From 6d4b565d414f3c0e50a50115677ab7d51d57e453 Mon Sep 17 00:00:00 2001 From: rushannanayakkara Date: Mon, 22 Apr 2024 14:02:38 +0530 Subject: [PATCH 07/23] Add otp max resend config --- .../src/extensions/i18n/models/extensions.ts | 1 + .../i18n/resources/en-US/extensions.ts | 13 ++ .../governance-connector-constants.ts | 14 +- .../server-configurations-constants.ts | 8 +- .../forms/password-recovery-form.tsx | 155 +++++++++++++----- 5 files changed, 146 insertions(+), 45 deletions(-) diff --git a/apps/console/src/extensions/i18n/models/extensions.ts b/apps/console/src/extensions/i18n/models/extensions.ts index 4bb42678734..ae7e16fa018 100755 --- a/apps/console/src/extensions/i18n/models/extensions.ts +++ b/apps/console/src/extensions/i18n/models/extensions.ts @@ -3039,6 +3039,7 @@ export interface Extensions { enableEmailBasedRecovery: FormAttributes; expiryTime: FormAttributes; notifySuccess: FormAttributes; + otpMaxResendCount: FormAttributes; smsOtpExpiryTime: FormAttributes; passwordRecoveryOtpUseUppercase: FormAttributes; passwordRecoveryOtpUseLowercase: FormAttributes; diff --git a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts index c4636e563f1..6465fbbab2f 100755 --- a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts +++ b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts @@ -3578,6 +3578,19 @@ export const extensions: Extensions = { "recovery is successful.", label: "Notify on successful recovery" }, + otpMaxResendCount: { + hint: "Password recovery OTP maximum resend count.", + label: "Max resend count", + placeholder: "Enter max resend count", + validations: { + invalid: "Password recovery OTP resend count should be an integer.", + empty: "Password recovery OTP resend count cannot be empty.", + range: + "Password recovery OTP resend count should be between 1 & 5.", + maxLengthReached: + "Password recovery OTP resend count should be a number with 1 digits." + } + }, smsOtpExpiryTime: { hint: "Password recovery OTP expiry time in minutes.", label: "Password recovery OTP expiry time", diff --git a/features/admin.server-configurations.v1/constants/governance-connector-constants.ts b/features/admin.server-configurations.v1/constants/governance-connector-constants.ts index d2bf89907d7..7c01eb23f2b 100644 --- a/features/admin.server-configurations.v1/constants/governance-connector-constants.ts +++ b/features/admin.server-configurations.v1/constants/governance-connector-constants.ts @@ -60,6 +60,10 @@ export class GovernanceConnectorConstants { EXPIRY_TIME_MAX_VALUE: number; EXPIRY_TIME_MIN_LENGTH: number; EXPIRY_TIME_MIN_VALUE: number; + MAX_RESEND_COUNT_MIN_LENGTH: number; + MAX_RESEND_COUNT_MAX_LENGTH: number; + MAX_RESEND_COUNT_MIN_VALUE: number; + MAX_RESEND_COUNT_MAX_VALUE: number; SMS_OTP_EXPIRY_TIME_MAX_LENGTH: number; SMS_OTP_EXPIRY_TIME_MAX_VALUE: number; SMS_OTP_CODE_LENGTH_MAX_LENGTH: number; @@ -72,12 +76,16 @@ export class GovernanceConnectorConstants { EXPIRY_TIME_MAX_VALUE: 10080, EXPIRY_TIME_MIN_LENGTH: 1, EXPIRY_TIME_MIN_VALUE: 1, - SMS_OTP_EXPIRY_TIME_MAX_LENGTH: 4, - SMS_OTP_EXPIRY_TIME_MAX_VALUE: 1440, + MAX_RESEND_COUNT_MAX_LENGTH: 1, + MAX_RESEND_COUNT_MAX_VALUE: 5, + MAX_RESEND_COUNT_MIN_LENGTH: 1, + MAX_RESEND_COUNT_MIN_VALUE: 1, SMS_OTP_CODE_LENGTH_MAX_LENGTH: 2, SMS_OTP_CODE_LENGTH_MAX_VALUE: 10, SMS_OTP_CODE_LENGTH_MIN_LENGTH: 1, - SMS_OTP_CODE_LENGTH_MIN_VALUE: 6 + SMS_OTP_CODE_LENGTH_MIN_VALUE: 6, + SMS_OTP_EXPIRY_TIME_MAX_LENGTH: 4, + SMS_OTP_EXPIRY_TIME_MAX_VALUE: 1440, }; /** diff --git a/features/admin.server-configurations.v1/constants/server-configurations-constants.ts b/features/admin.server-configurations.v1/constants/server-configurations-constants.ts index 46e6e8bcf63..65d5b30507a 100644 --- a/features/admin.server-configurations.v1/constants/server-configurations-constants.ts +++ b/features/admin.server-configurations.v1/constants/server-configurations-constants.ts @@ -237,14 +237,18 @@ export class ServerConfigurationsConstants { public static readonly RECOVERY_CALLBACK_REGEX: string = "Recovery.CallbackRegex"; public static readonly PASSWORD_RECOVERY_QUESTION_FORCED_ENABLE: string = "Recovery.Question.Password.Forced.Enable"; + public static readonly RECOVERY_EMAIL_LINK_ENABLE: string = "Recovery.Notification.Password.emailLink.Enable"; public static readonly RECOVERY_SMS_OTP_ENABLE: string = "Recovery.Notification.Password.smsOtp.Enable"; - public static readonly RECOVERY_OTP_USE_UPPERCASE: string = + public static readonly RECOVERY_OTP_USE_UPPERCASE: string = "Recovery.Notification.Password.OTP.UseUppercaseCharactersInOTP"; - public static readonly RECOVERY_OTP_USE_LOWERCASE: string = + + public static readonly RECOVERY_OTP_USE_LOWERCASE: string = "Recovery.Notification.Password.OTP.UseLowercaseCharactersInOTP"; + public static readonly RECOVERY_OTP_USE_NUMERIC: string = "Recovery.Notification.Password.OTP.UseNumbersInOTP"; public static readonly RECOVERY_OTP_LENGTH: string = "Recovery.Notification.Password.OTP.OTPLength"; + public static readonly RECOVERY_OTP_MAX_RESEND_COUNT: string = "Recovery.Notification.Password.MaxResendAttempts"; /** * Login policies - account locking API Keyword constants. diff --git a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx index 45836085f8c..3ec6bb3e62f 100644 --- a/features/admin.server-configurations.v1/forms/password-recovery-form.tsx +++ b/features/admin.server-configurations.v1/forms/password-recovery-form.tsx @@ -99,6 +99,10 @@ interface PasswordRecoveryFormInitialValuesInterface { * The length of the SMS OTP code. */ smsOtpLength: string; + /** + * The maximum amount of times otp is resent. + */ + otpMaxResendCount: string; } /** @@ -117,6 +121,10 @@ export interface PasswordRecoveryFormErrorValidationsInterface { * SMS OTP code length field. */ smsOtpLength: string; + /** + * OTP max resend count field. + */ + otpMaxResendCount: string; } const allowedConnectorFields: string[] = [ @@ -128,7 +136,8 @@ const allowedConnectorFields: string[] = [ ServerConfigurationsConstants.RECOVERY_OTP_USE_UPPERCASE, ServerConfigurationsConstants.RECOVERY_OTP_USE_LOWERCASE, ServerConfigurationsConstants.RECOVERY_OTP_USE_NUMERIC, - ServerConfigurationsConstants.RECOVERY_OTP_LENGTH + ServerConfigurationsConstants.RECOVERY_OTP_LENGTH, + ServerConfigurationsConstants.RECOVERY_OTP_MAX_RESEND_COUNT ]; const FORM_ID: string = "governance-connectors-password-recovery-form"; @@ -155,7 +164,7 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent(undefined); - const [ isEmailRecoveryEnabled, setIsEmailRecoveryEnabled ] = useState(false); + const [ isEmailRecoveryEnabled, setIsEmailRecoveryEnabled ] = useState(false); const [ isSMSRecoveryEnabled, setIsSMSRecoveryEnabled ] = useState(false); const [ isUpperCaseEnabled, setIsUpperCaseEnabled ] = useState(false); const [ isLowerCaseEnabled, setIsLowerCaseEnabled ] = useState(false); @@ -219,6 +228,11 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent { const errors: PasswordRecoveryFormErrorValidationsInterface = { expiryTime: undefined, + otpMaxResendCount: undefined, smsOtpExpiryTime: undefined, smsOtpLength: undefined }; @@ -294,10 +308,21 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent GovernanceConnectorConstants - .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.SMS_OTP_CODE_LENGTH_MAX_VALUE) { + .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.SMS_OTP_CODE_LENGTH_MAX_VALUE) { // Check for invalid input length. errors.smsOtpLength = t("extensions:manage.serverConfigurations.accountRecovery." + "passwordRecovery.form.fields.smsOtpLength.validations.maxLengthReached"); + } else if (!values.otpMaxResendCount) { + // Check for required error + errors.smsOtpLength = t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.otpMaxResendCount.validations.empty"); + } else if (parseInt(values.otpMaxResendCount, 10) < GovernanceConnectorConstants + .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.MAX_RESEND_COUNT_MIN_VALUE || + parseInt(values.otpMaxResendCount, 10) > GovernanceConnectorConstants + .PASSWORD_RECOVERY_FORM_FIELD_CONSTRAINTS.MAX_RESEND_COUNT_MAX_VALUE) { + // Check for invalid input length. + errors.otpMaxResendCount = t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.form.fields.otpMaxResendCount.validations.maxLengthReached"); } return errors; @@ -314,6 +339,7 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent - {t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.recoveryOptionHeading")} + { t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.recoveryOptionHeading") } - {t("extensions:manage.serverConfigurations.accountRecovery." + - "passwordRecovery.recoveryOptionSubHeadingEmailLink")} + { t("extensions:manage.serverConfigurations.accountRecovery." + + "passwordRecovery.recoveryOptionSubHeadingEmailLink") } setIsEmailRecoveryEnabled(value) } - data-testid={ `${testId}-email-link-based-recovery` } + data-testid={ `${ testId }-email-link-based-recovery` } /> { @@ -403,7 +434,7 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent { @@ -441,7 +472,7 @@ export const PasswordRecoveryConfigurationForm: FunctionComponent