From 4f335005d932cab4826766578ceea0cb7dc08820 Mon Sep 17 00:00:00 2001 From: Marlon Keating <322346+marlonkeating@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:15:48 -0800 Subject: [PATCH] feat: Persist SSO Config 'authorized' checkbox state (#1121) --- .../SettingsSSOTab/SSOFormWorkflowConfig.tsx | 37 +++++++++++-------- .../steps/NewSSOConfigAuthorizeStep.tsx | 10 ++--- .../tests/NewSSOConfigForm.test.jsx | 6 ++- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx index ca7dd08e07..218811ac3e 100644 --- a/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx +++ b/src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx @@ -1,5 +1,6 @@ import omit from 'lodash/omit'; +import { AxiosError } from 'axios'; import type { FormWorkflowHandlerArgs, FormWorkflowStep } from '../../forms/FormWorkflow'; import SSOConfigConnectStep, { validations as SSOConfigConnectStepValidations } from './steps/NewSSOConfigConnectStep'; import SSOConfigConfigureStep, { validations as SSOConfigConfigureStepValidations } from './steps/NewSSOConfigConfigureStep'; @@ -8,7 +9,6 @@ import SSOConfigConfirmStep from './steps/NewSSOConfigConfirmStep'; import LmsApiService from '../../../data/services/LmsApiService'; import handleErrors from '../utils'; import { snakeCaseDict } from '../../../utils'; -import { AxiosError } from 'axios'; import { INVALID_IDP_METADATA_ERROR, RECORD_UNDER_CONFIGURATIONS_ERROR } from '../data/constants'; type SSOConfigSnakeCase = { @@ -40,6 +40,7 @@ type SSOConfigSnakeCase = { oauth_user_id: string, sp_metadata_url?: string, record?: object, + marked_authorized: boolean }; export type SSOConfigCamelCase = { @@ -70,7 +71,8 @@ export type SSOConfigCamelCase = { sapsfPrivateKey: string, odataClientId: string, oauthUserId: string, - spMetadataUrl?: string + spMetadataUrl?: string, + markedAuthorized: boolean }; type SSOConfigFormControlVariables = { @@ -81,13 +83,6 @@ type SSOConfigFormControlVariables = { type SSOConfigFormContextData = SSOConfigCamelCase & SSOConfigFormControlVariables; export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => { - const placeHolderButton = (buttonName?: string) => () => ({ - buttonText: buttonName || 'Next', - opensNewWindow: false, - onClick: () => { }, - preventDefaultErrorModal: false, - }); - const advanceConnectStep = async ({ formFields, errHandler, @@ -98,7 +93,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => { const sanitizeAndCopyFormFields = (formFields: SSOConfigSnakeCase) => { const copiedFormFields = { ...formFields }; - return omit(copiedFormFields, ['record', 'sp_metadata_url', 'submitted_at', 'configured_at','validated_at']); + return omit(copiedFormFields, ['record', 'sp_metadata_url', 'submitted_at', 'configured_at', 'validated_at']); }; const saveChanges = async ({ @@ -117,7 +112,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => { let updatedFormFields: SSOConfigCamelCase = omit(formFields, ['idpConnectOption', 'spMetadataUrl', 'isPendingConfiguration']); updatedFormFields.enterpriseCustomer = enterpriseId; const submittedFormFields: SSOConfigSnakeCase = snakeCaseDict(updatedFormFields) as SSOConfigSnakeCase; - let copiedFormFields = sanitizeAndCopyFormFields(submittedFormFields); + const copiedFormFields = sanitizeAndCopyFormFields(submittedFormFields); if (copiedFormFields?.uuid) { try { const updateResponse = await LmsApiService.updateEnterpriseSsoOrchestrationRecord( @@ -127,9 +122,9 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => { updatedFormFields = updateResponse.data; } catch (error: AxiosError | any) { err = handleErrors(error); - if (error.message?.includes("Must provide valid IDP metadata url")) { + if (error.message?.includes('Must provide valid IDP metadata url')) { errHandler?.(INVALID_IDP_METADATA_ERROR); - } else if (error.message?.includes("Record has already been submitted for configuration.")) { + } else if (error.message?.includes('Record has already been submitted for configuration.')) { errHandler?.(RECORD_UNDER_CONFIGURATIONS_ERROR); } else { setConfigureError(error); @@ -142,7 +137,7 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => { updatedFormFields.spMetadataUrl = createResponse.data.sp_metadata_url; } catch (error: AxiosError | any) { err = handleErrors(error); - if (error.message?.includes("Must provide valid IDP metadata url")) { + if (error.message?.includes('Must provide valid IDP metadata url')) { errHandler?.(INVALID_IDP_METADATA_ERROR); } else { setConfigureError(error); @@ -184,7 +179,12 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => { formComponent: SSOConfigAuthorizeStep, validations: SSOConfigAuthorizeStepValidations, stepName: 'Authorize', - nextButtonConfig: placeHolderButton(), + nextButtonConfig: () => ({ + buttonText: 'Next', + opensNewWindow: false, + onClick: saveChanges, + preventDefaultErrorModal: false, + }), showBackButton: true, showCancelButton: false, }, { @@ -192,7 +192,12 @@ export const SSOFormWorkflowConfig = ({ enterpriseId, setConfigureError }) => { formComponent: SSOConfigConfirmStep, validations: [], stepName: 'Confirm and Test', - nextButtonConfig: placeHolderButton('Finish'), + nextButtonConfig: () => ({ + buttonText: 'Finish', + opensNewWindow: false, + onClick: () => {}, + preventDefaultErrorModal: false, + }), showBackButton: true, showCancelButton: false, }, diff --git a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx index dd640f2450..88d280c983 100644 --- a/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx +++ b/src/components/settings/SettingsSSOTab/steps/NewSSOConfigAuthorizeStep.tsx @@ -1,21 +1,20 @@ import React, { useContext } from 'react'; import { useParams } from 'react-router-dom'; import { - Alert, Form, Hyperlink, Button, Row, + Alert, Hyperlink, Button, Row, } from '@edx/paragon'; import { Info, Download } from '@edx/paragon/icons'; import { getConfig } from '@edx/frontend-platform/config'; import { createSAMLURLs } from '../utils'; import { SSOConfigContext } from '../SSOConfigContext'; -import { setFormFieldAction } from '../../../forms/data/actions'; import { FormFieldValidation, useFormContext } from '../../../forms/FormContext'; import ValidatedFormCheckbox from '../../../forms/ValidatedFormCheckbox'; export const validations: FormFieldValidation[] = [ { - formFieldId: 'confirmAuthorizedEdxServiceProvider', + formFieldId: 'markedAuthorized', validator: (fields) => { - const ret = !fields.confirmAuthorizedEdxServiceProvider && 'Please verify authorization of edX as a Service Provider.'; + const ret = !fields.markedAuthorized && 'Please verify authorization of edX as a Service Provider.'; return ret; }, }, @@ -40,7 +39,6 @@ const SSOConfigAuthorizeStep = () => { ssoState, } = useContext(SSOConfigContext); const { - dispatch, formFields, } = useFormContext(); const { enterpriseSlug } = useParams(); @@ -80,7 +78,7 @@ const SSOConfigAuthorizeStep = () => {


Return to this window and check the box once complete

- + I have authorized edX as a Service Provider diff --git a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx index 084157a67b..3f08d7cbe5 100644 --- a/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx +++ b/src/components/settings/SettingsSSOTab/tests/NewSSOConfigForm.test.jsx @@ -421,7 +421,10 @@ describe('SAML Config Tab', () => { test('navigate through new non-SAP sso workflow', async () => { setupNewSSOStepper(); const mockCreateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'createEnterpriseSsoOrchestrationRecord'); - mockCreateEnterpriseSsoOrchestrationRecord.mockResolvedValue({ data: { record: 'fakeuuid', sp_metadata_url: 'https://fake.url' } }); + const mockUpdateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'updateEnterpriseSsoOrchestrationRecord'); + const mockReturnData = { data: { record: 'fakeuuid', sp_metadata_url: 'https://fake.url' } }; + mockCreateEnterpriseSsoOrchestrationRecord.mockResolvedValue(mockReturnData); + mockUpdateEnterpriseSsoOrchestrationRecord.mockResolvedValue(mockReturnData); jest.spyOn(Router, 'useParams').mockReturnValue({ enterpriseSlug: 'testslug' }); // Connect Step await waitFor(() => { @@ -504,7 +507,6 @@ describe('SAML Config Tab', () => { }, []); screen.queryByText(testMetadataUrl); }); - // TODO: Test case where we go SAP route test('navigate through new SAP sso workflow', async () => { setupNewSSOStepper(); const mockCreateEnterpriseSsoOrchestrationRecord = jest.spyOn(LmsApiService, 'createEnterpriseSsoOrchestrationRecord');