Skip to content

Commit

Permalink
feat: Persist SSO Config 'authorized' checkbox state (#1121)
Browse files Browse the repository at this point in the history
  • Loading branch information
marlonkeating authored Dec 6, 2023
1 parent b990fec commit 4f33500
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 24 deletions.
37 changes: 21 additions & 16 deletions src/components/settings/SettingsSSOTab/SSOFormWorkflowConfig.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 = {
Expand Down Expand Up @@ -40,6 +40,7 @@ type SSOConfigSnakeCase = {
oauth_user_id: string,
sp_metadata_url?: string,
record?: object,
marked_authorized: boolean
};

export type SSOConfigCamelCase = {
Expand Down Expand Up @@ -70,7 +71,8 @@ export type SSOConfigCamelCase = {
sapsfPrivateKey: string,
odataClientId: string,
oauthUserId: string,
spMetadataUrl?: string
spMetadataUrl?: string,
markedAuthorized: boolean
};

type SSOConfigFormControlVariables = {
Expand All @@ -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,
Expand All @@ -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 ({
Expand All @@ -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(
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -184,15 +179,25 @@ 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,
}, {
index: 3,
formComponent: SSOConfigConfirmStep,
validations: [],
stepName: 'Confirm and Test',
nextButtonConfig: placeHolderButton('Finish'),
nextButtonConfig: () => ({
buttonText: 'Finish',
opensNewWindow: false,
onClick: () => {},
preventDefaultErrorModal: false,
}),
showBackButton: true,
showCancelButton: false,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
},
},
Expand All @@ -40,7 +39,6 @@ const SSOConfigAuthorizeStep = () => {
ssoState,
} = useContext<SSOConfigContextValue>(SSOConfigContext);
const {
dispatch,
formFields,
} = useFormContext();
const { enterpriseSlug } = useParams();
Expand Down Expand Up @@ -80,7 +78,7 @@ const SSOConfigAuthorizeStep = () => {
</p>
<hr />
<p>Return to this window and check the box once complete</p>
<ValidatedFormCheckbox className="mt-4" formId="confirmAuthorizedEdxServiceProvider">
<ValidatedFormCheckbox className="mt-4" formId="markedAuthorized">
I have authorized edX as a Service Provider
</ValidatedFormCheckbox>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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');
Expand Down

0 comments on commit 4f33500

Please sign in to comment.