From 7fa34f70f839714a93692be50ffa0e977c86092a Mon Sep 17 00:00:00 2001 From: NipuniBhagya Date: Fri, 24 Oct 2025 15:33:26 +0530 Subject: [PATCH 1/7] Improve Sift connector configuration page --- .../admin.core.v1/store/reducers/config.ts | 1 + .../api/event-publishing-configurations.ts | 77 +++ .../api/use-fraud-detection-configurations.ts | 53 ++ .../configs/endpoints.ts | 1 + .../configs/ui.ts | 13 + .../sift-connector-form.scss | 2 +- .../sift-connector-form.tsx | 456 ++++++++++++++---- .../models/endpoints.ts | 1 + .../models/fraud-detection.ts | 37 ++ .../namespaces/governance-connectors-ns.ts | 42 ++ .../en-US/portals/governance-connectors.ts | 46 ++ .../wso2is/assets/images/connectors/sift.svg | 26 + 12 files changed, 662 insertions(+), 93 deletions(-) create mode 100644 features/admin.server-configurations.v1/api/event-publishing-configurations.ts create mode 100644 features/admin.server-configurations.v1/api/use-fraud-detection-configurations.ts create mode 100644 features/admin.server-configurations.v1/models/fraud-detection.ts create mode 100644 modules/theme/src/themes/wso2is/assets/images/connectors/sift.svg diff --git a/features/admin.core.v1/store/reducers/config.ts b/features/admin.core.v1/store/reducers/config.ts index edb933aeffe..d99c258c10c 100644 --- a/features/admin.core.v1/store/reducers/config.ts +++ b/features/admin.core.v1/store/reducers/config.ts @@ -123,6 +123,7 @@ export const commonConfigReducerInitialState: CommonConfigReducerStateInterface< fidoConfigs: "", flow: "", flowMeta: "", + fraudDetectionConfigurations: "", getSecret: "", getSecretList: "", getSecretType: "", diff --git a/features/admin.server-configurations.v1/api/event-publishing-configurations.ts b/features/admin.server-configurations.v1/api/event-publishing-configurations.ts new file mode 100644 index 00000000000..457827addd4 --- /dev/null +++ b/features/admin.server-configurations.v1/api/event-publishing-configurations.ts @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2025, 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 { AsgardeoSPAClient, HttpClientInstance } from "@asgardeo/auth-react"; +import { RequestConfigInterface } from "@wso2is/admin.core.v1/hooks/use-request"; +import { store } from "@wso2is/admin.core.v1/store"; +import { IdentityAppsApiException } from "@wso2is/core/exceptions"; +import { HttpMethods } from "@wso2is/core/models"; +import { AxiosError, AxiosResponse } from "axios"; +import { ServerConfigurationsConstants } from "../constants/server-configurations-constants"; +import { FraudDetectionConfigurationsInterface } from "../models/fraud-detection"; + +/** + * Initialize an axios Http client. + * + */ +const httpClient: HttpClientInstance = AsgardeoSPAClient.getInstance().httpRequest.bind( + AsgardeoSPAClient.getInstance()); + +/** + * Function to update the Event Publishing Configurations. + */ +const updateEventPublishingConfigurations = ( + payload: FraudDetectionConfigurationsInterface +): Promise => { + const requestConfig: RequestConfigInterface = { + data: payload, + headers: { + Accept: "application/json", + "Content-Type": "application/json" + }, + method: HttpMethods.PUT, + url: store.getState().config.endpoints.fraudDetectionConfigurations + }; + + return httpClient(requestConfig) + .then((response: AxiosResponse) => { + if (response.status !== 200) { + throw new IdentityAppsApiException( + ServerConfigurationsConstants.CONFIGS_UPDATE_REQUEST_INVALID_STATUS_CODE_ERROR, + null, + response.status, + response.request, + response, + response.config); + } + + return Promise.resolve(response.data); + }) + .catch((error: AxiosError) => { + throw new IdentityAppsApiException( + ServerConfigurationsConstants.CONFIGS_UPDATE_REQUEST_ERROR, + error.stack, + error.code, + error.request, + error.response, + error.config); + }); +}; + +export default updateEventPublishingConfigurations; diff --git a/features/admin.server-configurations.v1/api/use-fraud-detection-configurations.ts b/features/admin.server-configurations.v1/api/use-fraud-detection-configurations.ts new file mode 100644 index 00000000000..5b3108ca301 --- /dev/null +++ b/features/admin.server-configurations.v1/api/use-fraud-detection-configurations.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2025, 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 useRequest, { + RequestConfigInterface, + RequestErrorInterface, + RequestResultInterface +} from "@wso2is/admin.core.v1/hooks/use-request"; +import { store } from "@wso2is/admin.core.v1/store"; +import { HttpMethods } from "@wso2is/core/models"; +import { FraudDetectionConfigurationsInterface } from "../models/fraud-detection"; + +const useFraudDetectionConfigurations = ( + shouldFetch: boolean = true +): RequestResultInterface => { + const requestConfig: RequestConfigInterface = { + headers: { + Accept: "application/json", + "Content-Type": "application/json" + }, + method: HttpMethods.GET, + url: store.getState().config.endpoints.fraudDetectionConfigurations + }; + + const { data, error, isLoading, isValidating, mutate } = useRequest( + shouldFetch ? requestConfig : null + ); + + return { + data: data as Data, + error, + isLoading, + isValidating, + mutate + }; +}; + +export default useFraudDetectionConfigurations; diff --git a/features/admin.server-configurations.v1/configs/endpoints.ts b/features/admin.server-configurations.v1/configs/endpoints.ts index 244e7115dfb..2861b5b20dc 100644 --- a/features/admin.server-configurations.v1/configs/endpoints.ts +++ b/features/admin.server-configurations.v1/configs/endpoints.ts @@ -43,6 +43,7 @@ export const getServerConfigurationsResourceEndpoints = ( captchaForSSOLogin: `${ serverHost }/api/server/v1/identity-governance/${ ServerConfigurationsConstants.IDENTITY_GOVERNANCE_LOGIN_POLICIES_ID }/connectors/${ServerConfigurationsConstants.CAPTCHA_FOR_SSO_LOGIN_CONNECTOR_ID}`, + fraudDetectionConfigurations: `${ serverHost }/api/server/v1/configs/fraud-detection`, governanceConnectorCategories: `${ serverHost }/api/server/v1/identity-governance`, impersonationConfigurations: `${ serverHost }/api/server/v1/configs/impersonation`, loginPolicies: `${ serverHost }/api/server/v1/identity-governance/${ diff --git a/features/admin.server-configurations.v1/configs/ui.ts b/features/admin.server-configurations.v1/configs/ui.ts index 4cbffa655bc..cace8d4962a 100644 --- a/features/admin.server-configurations.v1/configs/ui.ts +++ b/features/admin.server-configurations.v1/configs/ui.ts @@ -16,6 +16,7 @@ * under the License. */ +import { FunctionComponent, SVGProps } from "react"; import { CircleUserIcon, GearIcon, @@ -94,6 +95,7 @@ import { default as JWTKeyIcon } from "../../themes/default/assets/images/illustrations/jwt-key-icon.svg"; import { ServerConfigurationsConstants } from "../constants/server-configurations-constants"; +import { ReactComponent as SiftLogo } from "../../themes/wso2is/assets/images/connectors/sift.svg"; interface GetGovernanceConnectorIllustrationsInterface { [key: string]: string; @@ -157,3 +159,14 @@ export const getConnectorCategoryIcon = (): ConnectorCategoryIconsInterface => { "default": GearIcon }; }; + +/** + * Get Sift connector icon. + */ +export const getSiftConnectorIcon = (): { + sift: FunctionComponent>; +} => { + return { + sift: SiftLogo + }; +}; diff --git a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.scss b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.scss index fca07fccddf..5ad5baddd53 100644 --- a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.scss +++ b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.scss @@ -16,7 +16,7 @@ * under the License. */ -.sift-connector-form{ +.sift-connector-form { .oxygen-text-field { min-width: 350px; } diff --git a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx index cfea6a6b094..4aab6ed6ad4 100644 --- a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx +++ b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx @@ -16,17 +16,33 @@ * under the License. */ +import { Grid } from "@mui/material"; +import Alert from "@oxygen-ui/react/Alert"; +import Box from "@oxygen-ui/react/Box"; +import FormGroup from "@oxygen-ui/react/FormGroup"; import IconButton from "@oxygen-ui/react/IconButton"; import InputAdornment from "@oxygen-ui/react/InputAdornment"; import Stack from "@oxygen-ui/react/Stack"; +import Typography from "@oxygen-ui/react/Typography"; import { TrashIcon } from "@oxygen-ui/react-icons"; -import { IdentifiableComponentInterface } from "@wso2is/core/models"; +import { AlertLevels, IdentifiableComponentInterface } from "@wso2is/core/models"; +import { addAlert } from "@wso2is/core/store"; import { FinalForm, FinalFormField, FormRenderProps, TextFieldAdapter } from "@wso2is/form"; -import { PrimaryButton } from "@wso2is/react-components"; -import React, { FunctionComponent, ReactElement, useState } from "react"; +import CheckboxAdapter from "@wso2is/form/src/components/adapters/checkbox-field-adapter"; +import { ContentLoader, GenericIcon, Message, PrimaryButton } from "@wso2is/react-components"; +import React, { FunctionComponent, ReactElement, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Icon } from "semantic-ui-react"; +import { useDispatch } from "react-redux"; +import { Dispatch } from "redux"; +import { Divider, Icon } from "semantic-ui-react"; +import updateEventPublishingConfigurations from "../../api/event-publishing-configurations"; +import useFraudDetectionConfigurations from "../../api/use-fraud-detection-configurations"; +import { getSiftConnectorIcon } from "../../configs/ui"; import { ServerConfigurationsConstants } from "../../constants/server-configurations-constants"; +import { + FraudAnalyticEventPropertyInterface, + FraudDetectionConfigurationsInterface +} from "../../models/fraud-detection"; import { GovernanceConnectorInterface } from "../../models/governance-connectors"; import { GovernanceConnectorUtils } from "../../utils/governance-connector-utils"; import "./sift-connector-form.scss"; @@ -69,9 +85,97 @@ const SiftConnectorForm: FunctionComponent = ({ }: SiftConnectorFormPropsInterface): ReactElement => { const { t } = useTranslation(); + const dispatch: Dispatch = useDispatch(); - const [ apiKey, setApiKey ] = useState(initialValues?.properties[0]?.value ?? ""); - const [ isShow, setIsShow ] = useState(false); + const [ isShow, setIsShow ] = useState(false); + + const { + data: fraudDetectionConfigurations, + error: fraudDetectionConfigurationsError, + isLoading: isFraudDetectionConfigurationsLoading, + mutate: mutateEventPublishingConfigurations + } = useFraudDetectionConfigurations(true); + + const eventMetadata: Record = { + "Event.Notification_Based_Verification": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userVerifications.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userVerifications.label", + displayOrder: 6 + }, + "Event.User_Credential_Update": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.credentialUpdates.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.credentialUpdates.label", + displayOrder: 4 + }, + "Event.User_Login": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logins.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logins.label", + displayOrder: 2 + }, + "Event.User_Logout": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logouts.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logouts.label", + displayOrder: 3 + }, + "Event.User_Profile_Update": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userProfileUpdates.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userProfileUpdates.label", + displayOrder: 5 + }, + "Event.User_Registration": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.registrations.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.registrations.label", + displayOrder: 1 + } + }; + + // Memoize enriched fraud detection configurations + const enrichedFraudDetectionConfigurations: FraudDetectionConfigurationsInterface | undefined = useMemo(() => { + if (!fraudDetectionConfigurations) return undefined; + + return { + ...fraudDetectionConfigurations, + events: fraudDetectionConfigurations.events.map((event: FraudAnalyticEventPropertyInterface) => ({ + ...event, + description: eventMetadata[event.eventName]?.description || "", + displayName: eventMetadata[event.eventName]?.displayName || event.eventName + })) + }; + }, [ fraudDetectionConfigurations, eventMetadata ]); + + // Split events into two columns + const { leftColumnEvents, rightColumnEvents } = useMemo(() => { + if (!enrichedFraudDetectionConfigurations?.events) { + return { leftColumnEvents: [], rightColumnEvents: [] }; + } + + enrichedFraudDetectionConfigurations.events.sort( + (a: FraudAnalyticEventPropertyInterface, b: FraudAnalyticEventPropertyInterface) => { + const orderA: number = eventMetadata[a.eventName]?.displayOrder || 0; + const orderB: number = eventMetadata[b.eventName]?.displayOrder || 0; + + return orderA - orderB; + } + ); + const midPoint: number = Math.ceil(enrichedFraudDetectionConfigurations.events.length / 2); + + return { + leftColumnEvents: enrichedFraudDetectionConfigurations.events.slice(0, midPoint), + rightColumnEvents: enrichedFraudDetectionConfigurations.events.slice(midPoint) + }; + }, [ enrichedFraudDetectionConfigurations ]); /** * Get updated API Key. @@ -81,20 +185,13 @@ const SiftConnectorForm: FunctionComponent = ({ */ const getUpdatedAPIKey = (values: Record) => { - let data: { [ key: string]: unknown } = { - [ ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY ]: "" - }; - - if (Object.keys(values).length === 0) { - return data; - } + const encodedApiKeyProperty: string = GovernanceConnectorUtils + .encodeConnectorPropertyName(ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY); - for (const key in values) { - data = { - ...data, - [ GovernanceConnectorUtils.decodeConnectorPropertyName(key) ]: values[ key ] - }; - } + const data: { [ key: string]: unknown } = { + [ ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY ]: + values[encodedApiKeyProperty] || "" + }; return data; }; @@ -118,93 +215,268 @@ const SiftConnectorForm: FunctionComponent = ({ ); + /** + * Render event selection checkbox. + * + * @param event - Fraud analytic event property. + * @returns ReactElement + */ + const renderEventSelectionCheckbox = (event: FraudAnalyticEventPropertyInterface): ReactElement => { + return ( + + + + + { t(event.displayName) } + + + + { t(event.description) } + + ) + } + /> + + ); + }; + + /** + * Render event publishing section. + * + * @returns ReactElement + */ + const renderEventPublishingSection = (form): ReactElement => { + return ( + <> + + + + { + form.getState().values.shouldIncludePII && ( + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties" + + ".publishUserInfo.warning") + } + + ) + } + + + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.title") } + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.subtitle") } + + + + + + { leftColumnEvents?.map((event: FraudAnalyticEventPropertyInterface) => + renderEventSelectionCheckbox(event)) } + + + + + { rightColumnEvents?.map( + (event: FraudAnalyticEventPropertyInterface) => + renderEventSelectionCheckbox(event) + ) } + + + + + ); + }; + + /** + * Handle form submission for event publishing configurations. + * + * @param values - Form values. + */ + const handleEventPublishingSubmit = (values: Record) => { + const eventPublishingPayload: FraudDetectionConfigurationsInterface = { + events: enrichedFraudDetectionConfigurations?.events.map((event: FraudAnalyticEventPropertyInterface) => ({ + enabled: values.events?.[ GovernanceConnectorUtils + .encodeConnectorPropertyName(event.eventName)] as boolean, + eventName: event.eventName, + properties: [] + })) || [], + publishUserInfo: values.shouldIncludePII as boolean || false + }; + + updateEventPublishingConfigurations(eventPublishingPayload).then(() => { + if (values[GovernanceConnectorUtils.encodeConnectorPropertyName( + ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY + )]) { + onSubmit(getUpdatedAPIKey(values)); + } + + mutateEventPublishingConfigurations(); + }).catch(() => { + dispatch( + addAlert({ + description: t("adminServerConfigurations:components.siftConnectorForm.messages." + + "eventPublishingUpdateError"), + level: AlertLevels.ERROR, + message: t("adminServerConfigurations:components.siftConnectorForm.messages." + + "eventPublishingUpdateError") + }) + ); + }); + }; + + /** * Resolve initial form values. + * + * @returns Initial form values. + */ + const resolveInitialFormValues = () => { + return { + [ GovernanceConnectorUtils + .encodeConnectorPropertyName(ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY) + ]: initialValues?.properties[0]?.value ?? "", + events: enrichedFraudDetectionConfigurations?.events.reduce( + (formValue: Record, event: FraudAnalyticEventPropertyInterface) => { + + formValue[GovernanceConnectorUtils.encodeConnectorPropertyName(event.eventName)] = event.enabled; + + return formValue; + }, {} as Record) ?? {}, + shouldIncludePII: enrichedFraudDetectionConfigurations?.publishUserInfo ?? false + }; + }; + return (
) => - onSubmit(getUpdatedAPIKey(values)) + handleEventPublishingSubmit(values) } data-componentid={ `${ componentId }-configuration-form` } - initialValues={ - { - [ GovernanceConnectorUtils - .encodeConnectorPropertyName(ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY) - ]: initialValues?.properties[0]?.value ?? "" - } - } + initialValues={ resolveInitialFormValues() } render={ ({ handleSubmit, form }: FormRenderProps) => ( -
- - + + + + { t("governanceConnectors:connectorCategories" + + ".loginAttemptsSecurity.connectors.siftConnector.title") } + + + + + ) => { - const value: string = e.target.value; - - setApiKey(value); - form.change(GovernanceConnectorUtils + } + component={ TextFieldAdapter } + autoComplete="new-password" + InputProps={ { + endAdornment: renderInputAdornment() + } } + /> + { + form.getState().values[GovernanceConnectorUtils .encodeConnectorPropertyName(ServerConfigurationsConstants - .SIFT_CONNECTOR_API_KEY_PROPERTY), value - ); - } } - value={ apiKey } - /> + .SIFT_CONNECTOR_API_KEY_PROPERTY)] && ( + { + form.change( + GovernanceConnectorUtils + .encodeConnectorPropertyName(ServerConfigurationsConstants + .SIFT_CONNECTOR_API_KEY_PROPERTY), "" + ); + } } + data-componentid={ + `${ componentId }-configuration-form-api-key-delete-button` + } + > + + + ) + } + + { - (apiKey && apiKey !== "") && ( - { - setApiKey(""); - form.change( - GovernanceConnectorUtils - .encodeConnectorPropertyName(ServerConfigurationsConstants - .SIFT_CONNECTOR_API_KEY_PROPERTY), "" - ); - } } - data-componentid={ `${ componentId }-configuration-form-api-key-delete-button` } - > - - - ) - } - - + : ( + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.title") } + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.subtitle") } + + { renderEventPublishingSection(form) } + + ) + } - > - { t("common:update") } - -
+ + { t("common:update") } + + + ) } />
diff --git a/features/admin.server-configurations.v1/models/endpoints.ts b/features/admin.server-configurations.v1/models/endpoints.ts index 784a086e008..fcbf4b9538e 100644 --- a/features/admin.server-configurations.v1/models/endpoints.ts +++ b/features/admin.server-configurations.v1/models/endpoints.ts @@ -40,4 +40,5 @@ export interface ServerConfigurationsResourceEndpointsInterface { selfSignUp: string; serverConfigurations: string; serverSupportedSchemas: string; + fraudDetectionConfigurations: string; } diff --git a/features/admin.server-configurations.v1/models/fraud-detection.ts b/features/admin.server-configurations.v1/models/fraud-detection.ts new file mode 100644 index 00000000000..77b3941708a --- /dev/null +++ b/features/admin.server-configurations.v1/models/fraud-detection.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2025, 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. + */ + +/** + * Interface for the Fraud Detection Configurations. + */ +export interface FraudDetectionConfigurationsInterface { + publishUserInfo: boolean; + events: FraudAnalyticEventPropertyInterface[]; +} + +/** + * Interface for the Fraud Analytic Event Property. + */ +export interface FraudAnalyticEventPropertyInterface { + eventName: string; + enabled: boolean; + properties: string[]; + displayName?: string; + description?: string; + displayOrder?: number; +} diff --git a/modules/i18n/src/models/namespaces/governance-connectors-ns.ts b/modules/i18n/src/models/namespaces/governance-connectors-ns.ts index 2c79b588c3a..e976021456c 100644 --- a/modules/i18n/src/models/namespaces/governance-connectors-ns.ts +++ b/modules/i18n/src/models/namespaces/governance-connectors-ns.ts @@ -338,6 +338,7 @@ export interface governanceConnectorsNS { }; }; siftConnector: { + title: string; properties: { name: string; description: string; @@ -346,11 +347,52 @@ export interface governanceConnectorsNS { placeholder: string; }; }; + eventPublishing: { + title: string; + subtitle: string; + eventProperties: { + title: string; + subtitle: string; + publishUserInfo: { + label: string; + warning: string; + }; + events: { + logins: { + label: string; + hint: string; + }; + logouts: { + label: string; + hint: string; + }; + registrations: { + label: string; + hint: string; + }; + credentialUpdates: { + label: string; + hint: string; + }; + userProfileUpdates: { + label: string; + hint: string; + }; + userVerifications: { + label: string; + hint: string; + }; + }; + }; + }; notifications: { configurationUpdate: { success: NotificationItem; error: NotificationItem; }; + eventPropertiesUpdate: { + error: NotificationItem; + }; }; }; }; diff --git a/modules/i18n/src/translations/en-US/portals/governance-connectors.ts b/modules/i18n/src/translations/en-US/portals/governance-connectors.ts index 9c69ca4189e..c4469797112 100644 --- a/modules/i18n/src/translations/en-US/portals/governance-connectors.ts +++ b/modules/i18n/src/translations/en-US/portals/governance-connectors.ts @@ -276,6 +276,46 @@ export const governanceConnectors: governanceConnectorsNS = { } }, siftConnector: { + title: "Sift Integration", + eventPublishing: { + title: "Configure Event Publishing", + subtitle: "Choose which user activities to publish to Sift and what data to include in the payload.", + eventProperties: { + title: "Events to Publish", + subtitle: "Select the events you want to publish to Sift for analysis.", + publishUserInfo: { + label: "Include Personally Identifiable Information (PII) in event payload", + warning: "This will send sensitive user data (like email, IP address, and name) to Sift. " + + "Ensure this complies with your organization's privacy policy." + }, + events: { + logins: { + label: "Logins", + hint: "Publish user login events." + }, + logouts: { + label: "Logouts", + hint: "Publish user logout events." + }, + registrations: { + label: "Registrations", + hint: "Publish user registration events." + }, + credentialUpdates: { + label: "Credential Updates", + hint: "Publish user credential update events." + }, + userProfileUpdates: { + label: "User Profile Updates", + hint: "Publish user profile update events." + }, + userVerifications: { + label: "User Verifications", + hint: "Publish notification based user verification events." + } + } + } + }, properties: { name: "Fraud Detection", description: "Integrate Sift to detect and prevent fraudulent account logins.", @@ -294,6 +334,12 @@ export const governanceConnectors: governanceConnectorsNS = { description: "Successfully updated the Sift configuration.", message: "Update Successful" } + }, + eventPropertiesUpdate: { + error: { + description: "An error occurred while updating the event publishing configurations.", + message: "Event Configurations Update Error" + } } } } diff --git a/modules/theme/src/themes/wso2is/assets/images/connectors/sift.svg b/modules/theme/src/themes/wso2is/assets/images/connectors/sift.svg new file mode 100644 index 00000000000..d7b46add63c --- /dev/null +++ b/modules/theme/src/themes/wso2is/assets/images/connectors/sift.svg @@ -0,0 +1,26 @@ + + + + + From f4a6f99ea0fd97d4a62979433e8ce1754f234254 Mon Sep 17 00:00:00 2001 From: NipuniBhagya Date: Tue, 16 Dec 2025 16:07:59 +0530 Subject: [PATCH 2/7] Add new Sift connector configurations --- .../configs/ui.ts | 4 +- .../sift-connector-form.tsx | 214 +++++++++++------- .../models/fraud-detection.ts | 11 + .../utils/governance-connector-utils.ts | 54 +++++ .../namespaces/governance-connectors-ns.ts | 18 +- .../en-US/portals/governance-connectors.ts | 32 +-- 6 files changed, 238 insertions(+), 95 deletions(-) diff --git a/features/admin.server-configurations.v1/configs/ui.ts b/features/admin.server-configurations.v1/configs/ui.ts index cace8d4962a..5887c7c1831 100644 --- a/features/admin.server-configurations.v1/configs/ui.ts +++ b/features/admin.server-configurations.v1/configs/ui.ts @@ -16,7 +16,6 @@ * under the License. */ -import { FunctionComponent, SVGProps } from "react"; import { CircleUserIcon, GearIcon, @@ -31,6 +30,7 @@ import { VerticleFilterBarsIcon } from "@oxygen-ui/react-icons"; import UsernameValidationIcon from "@wso2is/admin.extensions.v1/assets/images/icons/username-validation-icon.svg"; +import { FunctionComponent, SVGProps } from "react"; import { default as LockRecoverIcon } from "../../themes/default/assets/images/icons/lock-recover-icon.svg"; @@ -94,8 +94,8 @@ import { import { default as JWTKeyIcon } from "../../themes/default/assets/images/illustrations/jwt-key-icon.svg"; -import { ServerConfigurationsConstants } from "../constants/server-configurations-constants"; import { ReactComponent as SiftLogo } from "../../themes/wso2is/assets/images/connectors/sift.svg"; +import { ServerConfigurationsConstants } from "../constants/server-configurations-constants"; interface GetGovernanceConnectorIllustrationsInterface { [key: string]: string; diff --git a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx index 4aab6ed6ad4..9687162ca1b 100644 --- a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx +++ b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx @@ -16,20 +16,19 @@ * under the License. */ -import { Grid } from "@mui/material"; +import { Grid, Stack } from "@mui/material"; import Alert from "@oxygen-ui/react/Alert"; import Box from "@oxygen-ui/react/Box"; import FormGroup from "@oxygen-ui/react/FormGroup"; import IconButton from "@oxygen-ui/react/IconButton"; import InputAdornment from "@oxygen-ui/react/InputAdornment"; -import Stack from "@oxygen-ui/react/Stack"; import Typography from "@oxygen-ui/react/Typography"; import { TrashIcon } from "@oxygen-ui/react-icons"; import { AlertLevels, IdentifiableComponentInterface } from "@wso2is/core/models"; import { addAlert } from "@wso2is/core/store"; import { FinalForm, FinalFormField, FormRenderProps, TextFieldAdapter } from "@wso2is/form"; import CheckboxAdapter from "@wso2is/form/src/components/adapters/checkbox-field-adapter"; -import { ContentLoader, GenericIcon, Message, PrimaryButton } from "@wso2is/react-components"; +import { ContentLoader, GenericIcon, PrimaryButton } from "@wso2is/react-components"; import React, { FunctionComponent, ReactElement, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useDispatch } from "react-redux"; @@ -40,6 +39,7 @@ import useFraudDetectionConfigurations from "../../api/use-fraud-detection-confi import { getSiftConnectorIcon } from "../../configs/ui"; import { ServerConfigurationsConstants } from "../../constants/server-configurations-constants"; import { + FraudAnalyticEventMetadataInterface, FraudAnalyticEventPropertyInterface, FraudDetectionConfigurationsInterface } from "../../models/fraud-detection"; @@ -96,50 +96,8 @@ const SiftConnectorForm: FunctionComponent = ({ mutate: mutateEventPublishingConfigurations } = useFraudDetectionConfigurations(true); - const eventMetadata: Record = { - "Event.Notification_Based_Verification": { - description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.userVerifications.hint", - displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.userVerifications.label", - displayOrder: 6 - }, - "Event.User_Credential_Update": { - description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.credentialUpdates.hint", - displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.credentialUpdates.label", - displayOrder: 4 - }, - "Event.User_Login": { - description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.logins.hint", - displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.logins.label", - displayOrder: 2 - }, - "Event.User_Logout": { - description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.logouts.hint", - displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.logouts.label", - displayOrder: 3 - }, - "Event.User_Profile_Update": { - description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.userProfileUpdates.hint", - displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.userProfileUpdates.label", - displayOrder: 5 - }, - "Event.User_Registration": { - description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.registrations.hint", - displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.events.registrations.label", - displayOrder: 1 - } - }; + const eventMetadata: Record = GovernanceConnectorUtils + .resolveEventPropertyMappings(); // Memoize enriched fraud detection configurations const enrichedFraudDetectionConfigurations: FraudDetectionConfigurationsInterface | undefined = useMemo(() => { @@ -255,24 +213,95 @@ const SiftConnectorForm: FunctionComponent = ({ const renderEventPublishingSection = (form): ReactElement => { return ( <> - + + { + (form.getState().values.publishDeviceMetadata && + form.getState().values.publishUserInfo) && ( + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity.connectors" + + ".siftConnector.eventPublishing.eventProperties.piiPublishingWarning") } + + ) + } + + + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties" + + ".publishUserInfo.label") } + + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties" + + ".publishUserInfo.description") } + + ) } /> { - form.getState().values.shouldIncludePII && ( - + (form.getState().values.publishUserInfo && + !form.getState().values.publishDeviceMetadata) && ( + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + ".connectors.siftConnector.eventPublishing.eventProperties" + - ".publishUserInfo.warning") - } + ".publishUserInfo.warning") } + + ) + } + + + + + + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties" + + ".publishDeviceMetadata.label") } + + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties" + + ".publishDeviceMetadata.description") } + + ) + } + /> + { + (form.getState().values.publishDeviceMetadata && + !form.getState().values.publishUserInfo) && ( + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties" + + ".publishDeviceMetadata.warning") } ) } @@ -280,15 +309,15 @@ const SiftConnectorForm: FunctionComponent = ({ - { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.title") } + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity.connectors." + + "siftConnector.eventPublishing.eventProperties.title") } - { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties.subtitle") } + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity.connectors." + + "siftConnector.eventPublishing.eventProperties.subtitle") } - + { leftColumnEvents?.map((event: FraudAnalyticEventPropertyInterface) => @@ -304,6 +333,38 @@ const SiftConnectorForm: FunctionComponent = ({ + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity.connectors." + + "siftConnector.eventPublishing.eventDiagnostics.title") } + + + + + + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventDiagnostics." + + "logRequestPayload.label") } + + + + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventDiagnostics." + + "logRequestPayload.description") } + + ) + } + /> + + ); }; @@ -321,13 +382,15 @@ const SiftConnectorForm: FunctionComponent = ({ eventName: event.eventName, properties: [] })) || [], - publishUserInfo: values.shouldIncludePII as boolean || false + logRequestPayload: values.logRequestPayload as boolean || false, + publishDeviceMetadata: values.publishDeviceMetadata as boolean || false, + publishUserInfo: values.publishUserInfo as boolean || false }; updateEventPublishingConfigurations(eventPublishingPayload).then(() => { if (values[GovernanceConnectorUtils.encodeConnectorPropertyName( ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY - )]) { + )] !== undefined) { onSubmit(getUpdatedAPIKey(values)); } @@ -335,11 +398,11 @@ const SiftConnectorForm: FunctionComponent = ({ }).catch(() => { dispatch( addAlert({ - description: t("adminServerConfigurations:components.siftConnectorForm.messages." + - "eventPublishingUpdateError"), + description: t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.notifications.eventPropertiesUpdate.error.description"), level: AlertLevels.ERROR, - message: t("adminServerConfigurations:components.siftConnectorForm.messages." + - "eventPublishingUpdateError") + message: t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.notifications.eventPropertiesUpdate.error.message") }) ); }); @@ -361,7 +424,9 @@ const SiftConnectorForm: FunctionComponent = ({ return formValue; }, {} as Record) ?? {}, - shouldIncludePII: enrichedFraudDetectionConfigurations?.publishUserInfo ?? false + logRequestPayload: enrichedFraudDetectionConfigurations?.logRequestPayload ?? false, + publishDeviceMetadata: enrichedFraudDetectionConfigurations?.publishDeviceMetadata ?? false, + publishUserInfo: enrichedFraudDetectionConfigurations?.publishUserInfo ?? false }; }; @@ -385,8 +450,8 @@ const SiftConnectorForm: FunctionComponent = ({ icon={ getSiftConnectorIcon().sift } /> - { t("governanceConnectors:connectorCategories" + - ".loginAttemptsSecurity.connectors.siftConnector.title") } + { t("governanceConnectors:connectorCategories.loginAttemptsSecurity.connectors" + + ".siftConnector.title") }
@@ -403,14 +468,11 @@ const SiftConnectorForm: FunctionComponent = ({ ServerConfigurationsConstants.SIFT_CONNECTOR_API_KEY_PROPERTY) } inputType="password" type={ isShow ? "text" : "password" } - label={ t("governanceConnectors:connectorCategories" + - ".loginAttemptsSecurity.connectors.siftConnector" + - ".properties.siftConnectorApiKey.label") - } + label={ t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.properties.siftConnectorApiKey.label") } placeholder={ t("governanceConnectors:connectorCategories" + - ".loginAttemptsSecurity.connectors.siftConnector" + - ".properties.siftConnectorApiKey.placeholder") - } + ".loginAttemptsSecurity.connectors.siftConnector." + + "properties.siftConnectorApiKey.placeholder") } component={ TextFieldAdapter } autoComplete="new-password" InputProps={ { diff --git a/features/admin.server-configurations.v1/models/fraud-detection.ts b/features/admin.server-configurations.v1/models/fraud-detection.ts index 77b3941708a..607519d9277 100644 --- a/features/admin.server-configurations.v1/models/fraud-detection.ts +++ b/features/admin.server-configurations.v1/models/fraud-detection.ts @@ -20,7 +20,9 @@ * Interface for the Fraud Detection Configurations. */ export interface FraudDetectionConfigurationsInterface { + logRequestPayload: boolean; publishUserInfo: boolean; + publishDeviceMetadata: boolean; events: FraudAnalyticEventPropertyInterface[]; } @@ -35,3 +37,12 @@ export interface FraudAnalyticEventPropertyInterface { description?: string; displayOrder?: number; } + +/** + * Interface for the Fraud Analytic Event Metadata. + */ +export interface FraudAnalyticEventMetadataInterface { + displayName: string; + description: string; + displayOrder: number; +} diff --git a/features/admin.server-configurations.v1/utils/governance-connector-utils.ts b/features/admin.server-configurations.v1/utils/governance-connector-utils.ts index 7a276d015d7..3257b4f19a5 100644 --- a/features/admin.server-configurations.v1/utils/governance-connector-utils.ts +++ b/features/admin.server-configurations.v1/utils/governance-connector-utils.ts @@ -37,6 +37,7 @@ import { GovernanceConnectorsInterface } from "../models/governance-connectors"; import { SetGovernanceConnectorCategory } from "../store/actions/governance-connector"; +import { FraudAnalyticEventMetadataInterface } from "../models/fraud-detection"; /** * Utility class for governance connectors. @@ -588,4 +589,57 @@ export class GovernanceConnectorUtils { .properties; } + + /** + * Resolve event property mappings. + * + * @returns Event property mappings. + */ + public static resolveEventPropertyMappings(): Record { + + return { + "Event.Notification_Based_Verification": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userVerifications.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userVerifications.label", + displayOrder: 6 + }, + "Event.User_Credential_Update": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.credentialUpdates.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.credentialUpdates.label", + displayOrder: 4 + }, + "Event.User_Login": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logins.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logins.label", + displayOrder: 2 + }, + "Event.User_Logout": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logouts.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.logouts.label", + displayOrder: 3 + }, + "Event.User_Profile_Update": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userProfileUpdates.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.userProfileUpdates.label", + displayOrder: 5 + }, + "Event.User_Registration": { + description: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.registrations.hint", + displayName: "governanceConnectors:connectorCategories.loginAttemptsSecurity" + + ".connectors.siftConnector.eventPublishing.eventProperties.events.registrations.label", + displayOrder: 1 + } + }; + } } diff --git a/modules/i18n/src/models/namespaces/governance-connectors-ns.ts b/modules/i18n/src/models/namespaces/governance-connectors-ns.ts index e976021456c..36ebfa84f17 100644 --- a/modules/i18n/src/models/namespaces/governance-connectors-ns.ts +++ b/modules/i18n/src/models/namespaces/governance-connectors-ns.ts @@ -353,8 +353,15 @@ export interface governanceConnectorsNS { eventProperties: { title: string; subtitle: string; + piiPublishingWarning: string; + publishDeviceMetadata: { + label: string; + description: string; + warning: string; + }; publishUserInfo: { label: string; + description: string; warning: string; }; events: { @@ -384,12 +391,15 @@ export interface governanceConnectorsNS { }; }; }; + eventDiagnostics: { + title: string; + logRequestPayload: { + label: string; + description: string; + }; + }; }; notifications: { - configurationUpdate: { - success: NotificationItem; - error: NotificationItem; - }; eventPropertiesUpdate: { error: NotificationItem; }; diff --git a/modules/i18n/src/translations/en-US/portals/governance-connectors.ts b/modules/i18n/src/translations/en-US/portals/governance-connectors.ts index c4469797112..46cf4b44f4d 100644 --- a/modules/i18n/src/translations/en-US/portals/governance-connectors.ts +++ b/modules/i18n/src/translations/en-US/portals/governance-connectors.ts @@ -283,10 +283,19 @@ export const governanceConnectors: governanceConnectorsNS = { eventProperties: { title: "Events to Publish", subtitle: "Select the events you want to publish to Sift for analysis.", + piiPublishingWarning: "You’ve enabled sending both user profile and device data (e.g., email, name, IP address) to Sift. " + + "Ensure this complies with your organization's privacy policy.", + publishDeviceMetadata: { + label: "Include user device metadata in the event payload", + description: "Publish user device metadata like IP address and user agent.", + warning: "This will send IP addresses and user agents, which may be considered personal data. " + + "Ensure this complies with your organization's privacy policy and data-sharing agreements." + }, publishUserInfo: { - label: "Include Personally Identifiable Information (PII) in event payload", - warning: "This will send sensitive user data (like email, IP address, and name) to Sift. " + - "Ensure this complies with your organization's privacy policy." + label: "Include user profile information in the event payload", + description: "Publish user attributes like username, email, mobile, etc.", + warning: "This will send sensitive, direct user identifiers to Sift. " + + "Ensure this complies with your organization's privacy policy and data-sharing agreements." }, events: { logins: { @@ -314,6 +323,13 @@ export const governanceConnectors: governanceConnectorsNS = { hint: "Publish notification based user verification events." } } + }, + eventDiagnostics: { + title: "Diagnostic Logging", + logRequestPayload: { + label: "Log event payloads locally", + description: "Save a copy of each event payload to local diagnostic logs for debugging purposes." + } } }, properties: { @@ -325,16 +341,6 @@ export const governanceConnectors: governanceConnectorsNS = { } }, notifications: { - configurationUpdate: { - error: { - description: "An error occurred while updating the Sift configuration.", - message: "Update Error" - }, - success: { - description: "Successfully updated the Sift configuration.", - message: "Update Successful" - } - }, eventPropertiesUpdate: { error: { description: "An error occurred while updating the event publishing configurations.", From 8ca88e8d42ecbe404c6aa1690d63c28a7e7ace7b Mon Sep 17 00:00:00 2001 From: NipuniBhagya Date: Fri, 23 Jan 2026 21:47:25 +0530 Subject: [PATCH 3/7] Add support to hide event publishing configurations --- .../governance-connector-constants.ts | 7 +- .../sift-connector-form.tsx | 81 ++++++++++++------- 2 files changed, 59 insertions(+), 29 deletions(-) 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 14a2d7bcfaa..d708eb7d5fb 100644 --- a/features/admin.server-configurations.v1/constants/governance-connector-constants.ts +++ b/features/admin.server-configurations.v1/constants/governance-connector-constants.ts @@ -20,7 +20,8 @@ * Keys used in feature dictionary. */ export enum GovernanceConnectorFeatureDictionaryKeys { - HIDE_INVITED_USER_REGISTRATION_TOGGLE = "hideInvitedUserRegistrationToggle" + HIDE_INVITED_USER_REGISTRATION_TOGGLE = "hideInvitedUserRegistrationToggle", + HIDE_FRAUD_DETECTION_EVENT_PUBLISHING_CONFIGURATION = "hideFraudDetectionEventPublishingConfiguration" } /** @@ -35,7 +36,9 @@ export class GovernanceConnectorConstants { */ public static readonly featureDictionary: Record = { [GovernanceConnectorFeatureDictionaryKeys.HIDE_INVITED_USER_REGISTRATION_TOGGLE]: - "governanceConnectors.invitedUserRegistration.enableDisableControl" + "governanceConnectors.invitedUserRegistration.enableDisableControl", + [GovernanceConnectorFeatureDictionaryKeys.HIDE_FRAUD_DETECTION_EVENT_PUBLISHING_CONFIGURATION]: + "governanceConnectors.fraudDetection.eventPublishingConfigurations" }; /** diff --git a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx index 9687162ca1b..c752207c635 100644 --- a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx +++ b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx @@ -24,19 +24,25 @@ import IconButton from "@oxygen-ui/react/IconButton"; import InputAdornment from "@oxygen-ui/react/InputAdornment"; import Typography from "@oxygen-ui/react/Typography"; import { TrashIcon } from "@oxygen-ui/react-icons"; -import { AlertLevels, IdentifiableComponentInterface } from "@wso2is/core/models"; +import { AppState } from "@wso2is/admin.core.v1/store"; +import { isFeatureEnabled } from "@wso2is/core/helpers"; +import { AlertLevels, FeatureAccessConfigInterface, IdentifiableComponentInterface } from "@wso2is/core/models"; import { addAlert } from "@wso2is/core/store"; import { FinalForm, FinalFormField, FormRenderProps, TextFieldAdapter } from "@wso2is/form"; import CheckboxAdapter from "@wso2is/form/src/components/adapters/checkbox-field-adapter"; import { ContentLoader, GenericIcon, PrimaryButton } from "@wso2is/react-components"; import React, { FunctionComponent, ReactElement, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { Dispatch } from "redux"; import { Divider, Icon } from "semantic-ui-react"; import updateEventPublishingConfigurations from "../../api/event-publishing-configurations"; import useFraudDetectionConfigurations from "../../api/use-fraud-detection-configurations"; import { getSiftConnectorIcon } from "../../configs/ui"; +import { + GovernanceConnectorConstants, + GovernanceConnectorFeatureDictionaryKeys +} from "../../constants/governance-connector-constants"; import { ServerConfigurationsConstants } from "../../constants/server-configurations-constants"; import { FraudAnalyticEventMetadataInterface, @@ -87,6 +93,16 @@ const SiftConnectorForm: FunctionComponent = ({ const { t } = useTranslation(); const dispatch: Dispatch = useDispatch(); + const governanceConnectorsFeatureConfig: FeatureAccessConfigInterface = useSelector( + (state: AppState) => state?.config?.ui?.features?.governanceConnectors + ); + const showFraudDetectionEventPublishingConfiguration: boolean = isFeatureEnabled( + governanceConnectorsFeatureConfig, + GovernanceConnectorConstants.featureDictionary[ + GovernanceConnectorFeatureDictionaryKeys.HIDE_FRAUD_DETECTION_EVENT_PUBLISHING_CONFIGURATION + ] + ); + const [ isShow, setIsShow ] = useState(false); const { @@ -94,7 +110,7 @@ const SiftConnectorForm: FunctionComponent = ({ error: fraudDetectionConfigurationsError, isLoading: isFraudDetectionConfigurationsLoading, mutate: mutateEventPublishingConfigurations - } = useFraudDetectionConfigurations(true); + } = useFraudDetectionConfigurations(showFraudDetectionEventPublishingConfiguration); const eventMetadata: Record = GovernanceConnectorUtils .resolveEventPropertyMappings(); @@ -433,9 +449,15 @@ const SiftConnectorForm: FunctionComponent = ({ return (
) => - handleEventPublishingSubmit(values) - } + onSubmit={ (values: Record) => { + if (!showFraudDetectionEventPublishingConfiguration) { + // Only submit API key + onSubmit(getUpdatedAPIKey(values)); + } else { + // Submit event publishing configurations + handleEventPublishingSubmit(values); + } + } } data-componentid={ `${ componentId }-configuration-form` } initialValues={ resolveInitialFormValues() } render={ ({ handleSubmit, form }: FormRenderProps) => ( @@ -501,30 +523,35 @@ const SiftConnectorForm: FunctionComponent = ({ ) } - { (fraudDetectionConfigurationsError && isFraudDetectionConfigurationsLoading) ? - : ( - - - { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.title") } - - - { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.subtitle") } - - { renderEventPublishingSection(form) } - - ) + : showFraudDetectionEventPublishingConfiguration + && ( + <> + + + + { t("governanceConnectors:connectorCategories" + + ".loginAttemptsSecurity.connectors.siftConnector" + + ".eventPublishing.title") } + + + { t("governanceConnectors:connectorCategories" + + ".loginAttemptsSecurity.connectors.siftConnector" + + ".eventPublishing.subtitle") } + + { renderEventPublishingSection(form) } + + + ) } Date: Fri, 23 Jan 2026 21:53:26 +0530 Subject: [PATCH 4/7] Add changeset file --- .changeset/wet-drinks-visit.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/wet-drinks-visit.md diff --git a/.changeset/wet-drinks-visit.md b/.changeset/wet-drinks-visit.md new file mode 100644 index 00000000000..618fbec3590 --- /dev/null +++ b/.changeset/wet-drinks-visit.md @@ -0,0 +1,9 @@ +--- +"@wso2is/admin.server-configurations.v1": minor +"@wso2is/admin.core.v1": minor +"@wso2is/theme": minor +"@wso2is/console": minor +"@wso2is/i18n": minor +--- + +Improve Sift configuration page From fa65e5b523c39a881062501198c53dc2ec4f17ef Mon Sep 17 00:00:00 2001 From: NipuniBhagya Date: Tue, 27 Jan 2026 10:15:53 +0530 Subject: [PATCH 5/7] Fix eslint issues --- .../forms/sift-connector-form/sift-connector-form.tsx | 10 +++++----- .../utils/governance-connector-utils.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx index c752207c635..9c9726f5df4 100644 --- a/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx +++ b/features/admin.server-configurations.v1/forms/sift-connector-form/sift-connector-form.tsx @@ -28,7 +28,7 @@ import { AppState } from "@wso2is/admin.core.v1/store"; import { isFeatureEnabled } from "@wso2is/core/helpers"; import { AlertLevels, FeatureAccessConfigInterface, IdentifiableComponentInterface } from "@wso2is/core/models"; import { addAlert } from "@wso2is/core/store"; -import { FinalForm, FinalFormField, FormRenderProps, TextFieldAdapter } from "@wso2is/form"; +import { FinalForm, FinalFormField, FormApi, FormRenderProps, TextFieldAdapter } from "@wso2is/form"; import CheckboxAdapter from "@wso2is/form/src/components/adapters/checkbox-field-adapter"; import { ContentLoader, GenericIcon, PrimaryButton } from "@wso2is/react-components"; import React, { FunctionComponent, ReactElement, useMemo, useState } from "react"; @@ -226,7 +226,7 @@ const SiftConnectorForm: FunctionComponent = ({ * * @returns ReactElement */ - const renderEventPublishingSection = (form): ReactElement => { + const renderEventPublishingSection = (form: FormApi>): ReactElement => { return ( <> @@ -271,7 +271,7 @@ const SiftConnectorForm: FunctionComponent = ({ (form.getState().values.publishUserInfo && !form.getState().values.publishDeviceMetadata) && ( @@ -311,12 +311,12 @@ const SiftConnectorForm: FunctionComponent = ({ (form.getState().values.publishDeviceMetadata && !form.getState().values.publishUserInfo) && ( { t("governanceConnectors:connectorCategories.loginAttemptsSecurity" + - ".connectors.siftConnector.eventPublishing.eventProperties" + + ".connectors.siftConnector.eventPublishing.eventProperties" + ".publishDeviceMetadata.warning") } ) diff --git a/features/admin.server-configurations.v1/utils/governance-connector-utils.ts b/features/admin.server-configurations.v1/utils/governance-connector-utils.ts index 3257b4f19a5..7be5982c528 100644 --- a/features/admin.server-configurations.v1/utils/governance-connector-utils.ts +++ b/features/admin.server-configurations.v1/utils/governance-connector-utils.ts @@ -28,6 +28,7 @@ import { I18n } from "@wso2is/i18n"; import camelCase from "lodash-es/camelCase"; import { getConnectorCategories } from "../api/governance-connectors"; import { ServerConfigurationsConstants } from "../constants/server-configurations-constants"; +import { FraudAnalyticEventMetadataInterface } from "../models/fraud-detection"; import { ConnectorOverrideConfig, ConnectorPropertyInterface, @@ -37,7 +38,6 @@ import { GovernanceConnectorsInterface } from "../models/governance-connectors"; import { SetGovernanceConnectorCategory } from "../store/actions/governance-connector"; -import { FraudAnalyticEventMetadataInterface } from "../models/fraud-detection"; /** * Utility class for governance connectors. From e5f376102ecc5333d3e247bf06d87f54a6503686 Mon Sep 17 00:00:00 2001 From: NipuniBhagya Date: Tue, 27 Jan 2026 14:15:20 +0530 Subject: [PATCH 6/7] Resolve PR comments --- .../api/event-publishing-configurations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/admin.server-configurations.v1/api/event-publishing-configurations.ts b/features/admin.server-configurations.v1/api/event-publishing-configurations.ts index 457827addd4..8912bf0ff74 100644 --- a/features/admin.server-configurations.v1/api/event-publishing-configurations.ts +++ b/features/admin.server-configurations.v1/api/event-publishing-configurations.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * 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 From 8b42af7611b577aeb81494eda677f8c8a8d0a523 Mon Sep 17 00:00:00 2001 From: NipuniBhagya Date: Tue, 27 Jan 2026 20:58:23 +0530 Subject: [PATCH 7/7] Fix eslint rule pattern --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 97ab5ce026e..9bf7f2808ce 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-5]))?, WSO2 LLC. \\(https://www.wso2.com\\).$", + pattern: " Copyright \\(c\\) \\b(2019|202[0-6])(?:-(202[0-7]))?, WSO2 LLC. \\(https://www.wso2.com\\).$", template: " * Copyright (c) {{year}}, WSO2 LLC. (https://www.wso2.com)." }, " *",