diff --git a/.changeset/lazy-paws-greet.md b/.changeset/lazy-paws-greet.md new file mode 100644 index 00000000000..347aa8e43d0 --- /dev/null +++ b/.changeset/lazy-paws-greet.md @@ -0,0 +1,8 @@ +--- +"@wso2is/admin.identity-providers.v1": minor +"@wso2is/admin.connections.v1": minor +"@wso2is/console": minor +"@wso2is/i18n": patch +--- + +Introduce a org level config to govern TOTP diff --git a/.eslintrc.js b/.eslintrc.js index 21f596035ec..ae4acc63c00 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -72,7 +72,7 @@ const getLicenseHeaderPattern = () => { const LICENSE_HEADER_DEFAULT_PATTERN = [ "*", { - pattern: " Copyright \\(c\\) \\b(2019|202[0-5])(?:-(202[0-6]))?, WSO2 LLC. \\(https://www.wso2.com\\).$", + pattern: " Copyright \\(c\\) \\b(2019|202[0-6])(?:-(202[0-6]))?, WSO2 LLC. \\(https://www.wso2.com\\).$", template: " * Copyright (c) {{year}}, WSO2 LLC. (https://www.wso2.com)." }, " *", diff --git a/features/admin.connections.v1/components/edit/edit-multi-factor-authenticator.tsx b/features/admin.connections.v1/components/edit/edit-multi-factor-authenticator.tsx index 0b593f3ebd1..f3706a6db77 100644 --- a/features/admin.connections.v1/components/edit/edit-multi-factor-authenticator.tsx +++ b/features/admin.connections.v1/components/edit/edit-multi-factor-authenticator.tsx @@ -251,10 +251,9 @@ export const EditMultiFactorAuthenticator: FunctionComponent { if (![ - LocalAuthenticatorConstants.AUTHENTICATOR_IDS.TOTP_AUTHENTICATOR_ID, LocalAuthenticatorConstants.AUTHENTICATOR_IDS.MAGIC_LINK_AUTHENTICATOR_ID ].includes(authenticator.id)) { return activeIndex; diff --git a/features/admin.connections.v1/components/edit/forms/authenticators/index.ts b/features/admin.connections.v1/components/edit/forms/authenticators/index.ts index 1ac1e89489b..a9666f3aecf 100644 --- a/features/admin.connections.v1/components/edit/forms/authenticators/index.ts +++ b/features/admin.connections.v1/components/edit/forms/authenticators/index.ts @@ -25,3 +25,4 @@ export * from "./google-authenticator-form"; export * from "./microsoft-authenticator-form"; export * from "./sms-otp-authenticator-form"; export * from "./push-authenticator-form"; +export * from "./totp-authenticator-form"; diff --git a/features/admin.connections.v1/components/edit/forms/authenticators/totp-authenticator-form.tsx b/features/admin.connections.v1/components/edit/forms/authenticators/totp-authenticator-form.tsx new file mode 100644 index 00000000000..34792578d4e --- /dev/null +++ b/features/admin.connections.v1/components/edit/forms/authenticators/totp-authenticator-form.tsx @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TestableComponentInterface } from "@wso2is/core/models"; +import { Field, Form } from "@wso2is/form"; +import isEmpty from "lodash-es/isEmpty"; +import React, { FunctionComponent, ReactElement, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + CommonAuthenticatorFormInitialValuesInterface, + CommonAuthenticatorFormMetaInterface, + CommonAuthenticatorFormPropertyInterface +} from "../../../../models/authenticators"; +import { + CommonPluggableComponentPropertyInterface +} from "../../../../models/connection"; + +interface TOTPAuthenticatorFormPropsInterface extends TestableComponentInterface { + /** + * TOTP Authenticator Metadata. + */ + metadata: CommonAuthenticatorFormMetaInterface; + /** + * TOTP Authenticator configured initial values. + */ + initialValues: CommonAuthenticatorFormInitialValuesInterface; + /** + * Callback for form submit. + * @param values - Resolved Form Values. + */ + onSubmit: (values: CommonAuthenticatorFormInitialValuesInterface) => void; + /** + * Is readonly. + */ + readOnly?: boolean; + /** + * Specifies if the form is submitting. + */ + isSubmitting?: boolean; +} + +/** + * Form initial values interface. + */ +interface TOTPAuthenticatorFormInitialValuesInterface { + /** + * Enable progressive enrollment. + */ + TOTP_EnrolUserInAuthenticationFlow: boolean; +} + +/** + * Proptypes for the TOTP Authenticator Form error messages. + */ +export interface TOTPAuthenticatorFormErrorValidationsInterface { + /** + * Enable progressive enrollment. + */ + TOTP_EnrolUserInAuthenticationFlow: string; +} + +const FORM_ID: string = "totp-authenticator-form"; + +/** + * TOTP Authenticator Form. + * + * @param props - Props injected to the component. + * @returns Functional component. + */ +export const TOTPAuthenticatorForm: FunctionComponent = ( + props: TOTPAuthenticatorFormPropsInterface +): ReactElement => { + + const { + initialValues: originalInitialValues, + onSubmit, + readOnly, + isSubmitting, + ["data-testid"]: testId + } = props; + + const { t } = useTranslation(); + + const [ initialValues, setInitialValues ] = useState(undefined); + + /** + * Flattens and resolved form initial values and field metadata. + */ + useEffect(() => { + + if (isEmpty(originalInitialValues?.properties)) { + return; + } + + let resolvedInitialValues: TOTPAuthenticatorFormInitialValuesInterface = null; + + originalInitialValues.properties.forEach((value: CommonAuthenticatorFormPropertyInterface) => { + const moderatedName: string = value.name.replace(/\./g, "_"); + + resolvedInitialValues = { + ...resolvedInitialValues, + [moderatedName]: (value.value === "true" || value.value === "false") + ? JSON.parse(value.value) + : value.value + }; + }); + + setInitialValues(resolvedInitialValues); + }, [ originalInitialValues ]); + + /** + * Prepare form values for submitting. + * + * @param values - Form values. + * @returns Sanitized form values. + */ + const getUpdatedConfigurations = (values: TOTPAuthenticatorFormInitialValuesInterface) + : CommonAuthenticatorFormInitialValuesInterface => { + + const properties: CommonPluggableComponentPropertyInterface[] = []; + + for (const [ name, value ] of Object.entries(values)) { + if (name !== undefined) { + const moderatedName: string = name.replace(/_/g, "."); + + properties.push({ + name: moderatedName, + value: value.toString() + }); + } + } + + const result: CommonAuthenticatorFormInitialValuesInterface = { + ...originalInitialValues, + properties + }; + + return result; + }; + + return ( +
) => { + onSubmit(getUpdatedConfigurations(values as TOTPAuthenticatorFormInitialValuesInterface)); + } } + initialValues={ initialValues } + > + +