diff --git a/src/components/common/verify-code/tests/verify-code-controller.test.ts b/src/components/common/verify-code/tests/verify-code-controller.test.ts index 135b02ed7..fb8088c86 100644 --- a/src/components/common/verify-code/tests/verify-code-controller.test.ts +++ b/src/components/common/verify-code/tests/verify-code-controller.test.ts @@ -130,6 +130,26 @@ describe("Verify code controller tests", () => { expect(res.redirect).to.have.calledWith("/password-reset-required"); }); + it("if account has reprove identity and suspended status, redirects to /get-security-codes", async () => { + const accountInterventionService = accountInterventionsFakeHelper({ + passwordResetRequired: false, + temporarilySuspended: true, + blocked: false, + reproveIdentity: true, + }); + await verifyCodePost(verifyCodeService, accountInterventionService, { + notificationType: + NOTIFICATION_TYPE.VERIFY_CHANGE_HOW_GET_SECURITY_CODES, + template: "check-your-email/index.njk", + validationKey: "pages.checkYourEmail.code.validationError.invalidCode", + validationErrorCode: ERROR_CODES.INVALID_VERIFY_EMAIL_CODE, + })(req as Request, res as Response); + + expect(accountInterventionService.accountInterventionStatus).to.have.been + .called; + expect(res.redirect).to.have.calledWith(PATH_NAMES.GET_SECURITY_CODES); + }); + it("if account has no AIS status, redirects to /get-security-codes", async () => { const accountInterventionService = accountInterventionsFakeHelper(noInterventions); diff --git a/src/components/common/verify-code/verify-code-controller.ts b/src/components/common/verify-code/verify-code-controller.ts index c55668785..ccfb9976a 100644 --- a/src/components/common/verify-code/verify-code-controller.ts +++ b/src/components/common/verify-code/verify-code-controller.ts @@ -18,6 +18,7 @@ import { supportAccountInterventions, } from "../../../config"; import { AccountInterventionsInterface } from "../../account-intervention/types"; +import { isSuspendedWithoutUserActions } from "../../../utils/interventions"; interface Config { notificationType: NOTIFICATION_TYPE; @@ -114,7 +115,9 @@ export function verifyCodePost( if (options.journeyType !== JOURNEY_TYPE.PASSWORD_RESET_MFA) { nextEvent = USER_JOURNEY_EVENTS.PASSWORD_RESET_INTERVENTION; } - } else if (accountInterventionsResponse.data.temporarilySuspended) { + } else if ( + isSuspendedWithoutUserActions(accountInterventionsResponse.data) + ) { nextEvent = USER_JOURNEY_EVENTS.TEMPORARILY_BLOCKED_INTERVENTION; } } diff --git a/src/middleware/account-interventions-middleware.ts b/src/middleware/account-interventions-middleware.ts index 7ac82bfc1..51e80cd08 100644 --- a/src/middleware/account-interventions-middleware.ts +++ b/src/middleware/account-interventions-middleware.ts @@ -4,8 +4,11 @@ import { USER_JOURNEY_EVENTS } from "../components/common/state-machine/state-ma import { accountInterventionService } from "../components/account-intervention/account-intervention-service"; import { ExpressRouteFunc } from "../types"; import { supportAccountInterventions } from "../config"; -import { AccountInterventionStatus } from "../components/account-intervention/types"; import { logger } from "../utils/logger"; +import { + isSuspendedWithoutUserActions, + passwordHasBeenResetMoreRecentlyThanInterventionApplied, +} from "../utils/interventions"; export function accountInterventionsMiddleware( handleSuspendedStatus: boolean, @@ -57,8 +60,7 @@ export function accountInterventionsMiddleware( ); } } else if ( - accountInterventionsResponse.data.temporarilySuspended && - !accountInterventionsResponse.data.passwordResetRequired && + isSuspendedWithoutUserActions(accountInterventionsResponse.data) && handleSuspendedStatus ) { return res.redirect( @@ -74,13 +76,3 @@ export function accountInterventionsMiddleware( return next(); }; } - -function passwordHasBeenResetMoreRecentlyThanInterventionApplied( - req: Request, - status: AccountInterventionStatus -) { - return ( - req.session.user.passwordResetTime !== undefined && - req.session.user.passwordResetTime > parseInt(status.appliedAt) - ); -} diff --git a/src/utils/interventions.ts b/src/utils/interventions.ts new file mode 100644 index 000000000..2990f2d5e --- /dev/null +++ b/src/utils/interventions.ts @@ -0,0 +1,22 @@ +import { Request } from "express"; +import { AccountInterventionStatus } from "../components/account-intervention/types"; + +export function isSuspendedWithoutUserActions( + status: AccountInterventionStatus +): boolean { + return ( + status.temporarilySuspended && + !status.reproveIdentity && + !status.passwordResetRequired + ); +} + +export function passwordHasBeenResetMoreRecentlyThanInterventionApplied( + req: Request, + status: AccountInterventionStatus +) { + return ( + req.session.user.passwordResetTime !== undefined && + req.session.user.passwordResetTime > parseInt(status.appliedAt) + ); +} diff --git a/test/unit/middleware/account-interventions-middleware.test.ts b/test/unit/middleware/account-interventions-middleware.test.ts index 9e5682d74..d552e2325 100644 --- a/test/unit/middleware/account-interventions-middleware.test.ts +++ b/test/unit/middleware/account-interventions-middleware.test.ts @@ -200,6 +200,45 @@ describe("accountInterventionsMiddleware", () => { ); expect(next).to.have.been.calledOnce; }); + + it("should not redirect to UNAVAILABLE_TEMPORARY when handleSuspended status is true and handlePasswordResetStatus is false", async () => { + await callMiddleware( + true, + false, + accountInterventionsWithPasswordResetTrue + ); + expect(res.redirect).to.not.have.been.calledWith( + PATH_NAMES.UNAVAILABLE_TEMPORARY + ); + expect(next).to.be.calledOnce; + }); + }); + + describe("when reproveIdentity and temporarilySuspended is true", () => { + let accountIntervetionsWithReproveIdentity: AccountInterventionsInterface; + + before(() => { + accountIntervetionsWithReproveIdentity = accountInterventionsFakeHelper( + { + passwordResetRequired: false, + blocked: false, + temporarilySuspended: true, + reproveIdentity: true, + } + ); + }); + + it("should not redirect to UNAVAILABLE_TEMPORARY when handleSuspended status is true and handlePasswordResetStatus is false", async () => { + await callMiddleware( + true, + false, + accountIntervetionsWithReproveIdentity + ); + expect(res.redirect).to.not.have.been.calledWith( + PATH_NAMES.UNAVAILABLE_TEMPORARY + ); + expect(next).to.be.calledOnce; + }); }); describe("when temporarilySuspended is true", function () {