diff --git a/cypress/e2e/donor/confirm-your-identity.cy.js b/cypress/e2e/donor/confirm-your-identity.cy.js index 468d79e261..7e276a6e5c 100644 --- a/cypress/e2e/donor/confirm-your-identity.cy.js +++ b/cypress/e2e/donor/confirm-your-identity.cy.js @@ -17,7 +17,7 @@ describe('Confirm your identity', () => { cy.contains('label', 'Sam Smith (donor)').click(); cy.contains('button', 'Continue').click(); - cy.url().should('contain', '/one-login-identity-details'); + cy.url().should('contain', '/identity-details'); cy.checkA11yApp(); cy.contains('Sam'); @@ -106,91 +106,122 @@ describe('Confirm your identity', () => { }) describe('when identity details do not match LPA', () => { - it('can update LPA details', () => { - cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); - cy.contains('li', "Confirm your identity") - .should('contain', 'Not started') - .find('a') - .click(); + describe('before signing', () => { + it('can update LPA details', () => { + cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); + cy.contains('li', "Confirm your identity") + .should('contain', 'Not started') + .find('a') + .click(); - cy.url().should('contain', '/confirm-your-identity'); - cy.checkA11yApp(); - cy.contains('button', 'Continue').click(); + cy.url().should('contain', '/confirm-your-identity'); + cy.checkA11yApp(); + cy.contains('button', 'Continue').click(); - cy.contains('label', 'Charlie Cooper (certificate provider)').click(); - cy.contains('button', 'Continue').click(); + cy.contains('label', 'Charlie Cooper (certificate provider)').click(); + cy.contains('button', 'Continue').click(); - cy.url().should('contain', '/one-login-identity-details'); - cy.checkA11yApp(); + cy.url().should('contain', '/identity-details'); + cy.checkA11yApp(); - cy.contains('dd', 'Sam').parent().contains('span', 'Does not match'); - cy.contains('dd', 'Smith').parent().contains('span', 'Does not match'); - cy.contains('dd', '2 January 2000').parent().contains('span', 'Does not match'); + cy.contains('dd', 'Sam').parent().contains('span', 'Does not match'); + cy.contains('dd', 'Smith').parent().contains('span', 'Does not match'); + cy.contains('dd', '2 January 2000').parent().contains('span', 'Does not match'); - cy.contains('label', 'Yes').click(); - cy.contains('button', 'Continue').click(); + cy.contains('label', 'Yes').click(); + cy.contains('button', 'Continue').click(); - cy.url().should('contain', '/one-login-identity-details'); - cy.checkA11yApp(); + cy.url().should('contain', '/identity-details'); + cy.checkA11yApp(); - cy.contains('Your LPA details have been updated to match your confirmed identity') - cy.get('main').should('not.contain', 'Sam'); - cy.get('main').should('not.contain', 'Smith'); - cy.get('main').should('not.contain', '2 January 2000'); - }) + cy.contains('Your LPA details have been updated to match your confirmed identity') + cy.get('main').should('not.contain', 'Sam'); + cy.get('main').should('not.contain', 'Smith'); + cy.get('main').should('not.contain', '2 January 2000'); + }) - it('can withdraw LPA', () => { - cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); - cy.contains('li', "Confirm your identity") - .should('contain', 'Not started') - .find('a') - .click(); - cy.url().should('contain', '/confirm-your-identity'); - cy.checkA11yApp(); - cy.contains('button', 'Continue').click(); + it('can withdraw LPA', () => { + cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); + cy.contains('li', "Confirm your identity") + .should('contain', 'Not started') + .find('a') + .click(); - cy.contains('label', 'Charlie Cooper (certificate provider)').click(); - cy.contains('button', 'Continue').click(); + cy.url().should('contain', '/confirm-your-identity'); + cy.checkA11yApp(); + cy.contains('button', 'Continue').click(); - cy.url().should('contain', '/one-login-identity-details'); - cy.checkA11yApp(); + cy.contains('label', 'Charlie Cooper (certificate provider)').click(); + cy.contains('button', 'Continue').click(); - cy.contains('dd', 'Sam').parent().contains('span', 'Does not match'); - cy.contains('dd', 'Smith').parent().contains('span', 'Does not match'); - cy.contains('dd', '2 January 2000').parent().contains('span', 'Does not match'); + cy.url().should('contain', '/identity-details'); + cy.checkA11yApp(); - cy.contains('label', 'No').click(); - cy.contains('button', 'Continue').click(); + cy.contains('dd', 'Sam').parent().contains('span', 'Does not match'); + cy.contains('dd', 'Smith').parent().contains('span', 'Does not match'); + cy.contains('dd', '2 January 2000').parent().contains('span', 'Does not match'); - cy.url().should('contain', '/withdraw-this-lpa'); - cy.checkA11yApp(); - }) + cy.contains('label', 'No').click(); + cy.contains('button', 'Continue').click(); - it('errors when option not selected', () => { - cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); - cy.contains('li', "Confirm your identity") - .should('contain', 'Not started') - .find('a') - .click(); + cy.url().should('contain', '/withdraw-this-lpa'); + cy.checkA11yApp(); + }) - cy.url().should('contain', '/confirm-your-identity'); - cy.checkA11yApp(); - cy.contains('button', 'Continue').click(); + it('errors when option not selected', () => { + cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); + cy.contains('li', "Confirm your identity") + .should('contain', 'Not started') + .find('a') + .click(); - cy.contains('label', 'Charlie Cooper (certificate provider)').click(); - cy.contains('button', 'Continue').click(); + cy.url().should('contain', '/confirm-your-identity'); + cy.checkA11yApp(); + cy.contains('button', 'Continue').click(); - cy.url().should('contain', '/one-login-identity-details'); - cy.checkA11yApp(); + cy.contains('label', 'Charlie Cooper (certificate provider)').click(); + cy.contains('button', 'Continue').click(); - cy.contains('button', 'Continue').click(); + cy.url().should('contain', '/identity-details'); + cy.checkA11yApp(); + + cy.contains('button', 'Continue').click(); + + cy.get('.govuk-error-summary').within(() => { + cy.contains('Select yes if you would like to update your details'); + }); - cy.get('.govuk-error-summary').within(() => { - cy.contains('Select yes if you would like to update your details'); + cy.contains('.govuk-error-message', 'Select yes if you would like to update your details'); }); + }); - cy.contains('.govuk-error-message', 'Select yes if you would like to update your details'); + describe('after signing', () => { + it('cannot update details', () => { + cy.visit('/fixtures?redirect=/task-list&progress=signTheLpa&idStatus=donor:post-office'); + cy.contains('li', "Sign the LPA") + .should('contain', 'Completed'); + cy.contains('li', "Confirm your identity") + .should('contain', 'Pending') + .find('a') + .click(); + + cy.contains('label', 'confirm my identity another way').click(); + cy.contains('button', 'Continue').click(); + + cy.contains('label', 'Charlie Cooper (certificate provider)').click(); + cy.contains('button', 'Continue').click(); + + cy.url().should('contain', '/identity-details'); + cy.checkA11yApp(); + cy.contains('Does not match'); + cy.contains('cannot be updated'); + cy.contains('button', 'Continue').should('not.exist'); + cy.contains('a', 'Return to task list').click(); + + cy.contains('li', "Confirm your identity") + .should('contain', 'Pending'); + }); }); }); diff --git a/internal/donor/donorpage/one_login_identity_details.go b/internal/donor/donorpage/identity_details.go similarity index 90% rename from internal/donor/donorpage/one_login_identity_details.go rename to internal/donor/donorpage/identity_details.go index ec57316069..15d641885f 100644 --- a/internal/donor/donorpage/one_login_identity_details.go +++ b/internal/donor/donorpage/identity_details.go @@ -14,7 +14,7 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" ) -type oneLoginIdentityDetailsData struct { +type identityDetailsData struct { App appcontext.Data Errors validation.List Provided *donordata.Provided @@ -26,13 +26,13 @@ type oneLoginIdentityDetailsData struct { Form *form.YesNoForm } -func (d oneLoginIdentityDetailsData) DetailsMatch() bool { +func (d identityDetailsData) DetailsMatch() bool { return d.FirstNamesMatch && d.LastNameMatch && d.DateOfBirthMatch && d.AddressMatch } -func OneLoginIdentityDetails(tmpl template.Template, donorStore DonorStore) Handler { +func IdentityDetails(tmpl template.Template, donorStore DonorStore) Handler { return func(appData appcontext.Data, w http.ResponseWriter, r *http.Request, provided *donordata.Provided) error { - data := &oneLoginIdentityDetailsData{ + data := &identityDetailsData{ App: appData, Form: form.NewYesNoForm(form.YesNoUnknown), Provided: provided, @@ -43,7 +43,7 @@ func OneLoginIdentityDetails(tmpl template.Template, donorStore DonorStore) Hand AddressMatch: provided.Donor.Address.Postcode == provided.IdentityUserData.CurrentAddress.Postcode, } - if r.Method == http.MethodPost { + if r.Method == http.MethodPost && provided.CanChange() { f := form.ReadYesNoForm(r, "yesIfWouldLikeToUpdateDetails") data.Errors = f.Validate() diff --git a/internal/donor/donorpage/one_login_identity_details_test.go b/internal/donor/donorpage/identity_details_test.go similarity index 87% rename from internal/donor/donorpage/one_login_identity_details_test.go rename to internal/donor/donorpage/identity_details_test.go index f47674dad7..5405323b16 100644 --- a/internal/donor/donorpage/one_login_identity_details_test.go +++ b/internal/donor/donorpage/identity_details_test.go @@ -20,14 +20,14 @@ import ( ) func TestOneLoginIdentityDetailsDataDetailsMatch(t *testing.T) { - assert.True(t, oneLoginIdentityDetailsData{ + assert.True(t, identityDetailsData{ FirstNamesMatch: true, LastNameMatch: true, DateOfBirthMatch: true, AddressMatch: true, }.DetailsMatch()) - assert.False(t, oneLoginIdentityDetailsData{LastNameMatch: true, DateOfBirthMatch: true, AddressMatch: true}.DetailsMatch()) - assert.False(t, oneLoginIdentityDetailsData{}.DetailsMatch()) + assert.False(t, identityDetailsData{LastNameMatch: true, DateOfBirthMatch: true, AddressMatch: true}.DetailsMatch()) + assert.False(t, identityDetailsData{}.DetailsMatch()) } func TestGetOneLoginIdentityDetails(t *testing.T) { @@ -77,7 +77,7 @@ func TestGetOneLoginIdentityDetails(t *testing.T) { template := newMockTemplate(t) template.EXPECT(). - Execute(w, &oneLoginIdentityDetailsData{ + Execute(w, &identityDetailsData{ App: testAppData, Form: form.NewYesNoForm(form.YesNoUnknown), Provided: tc.donorProvided, @@ -89,7 +89,7 @@ func TestGetOneLoginIdentityDetails(t *testing.T) { }). Return(nil) - err := OneLoginIdentityDetails(template.Execute, nil)(testAppData, w, r, tc.donorProvided) + err := IdentityDetails(template.Execute, nil)(testAppData, w, r, tc.donorProvided) resp := w.Result() assert.Nil(t, err) @@ -121,7 +121,7 @@ func TestPostOneLoginIdentityDetailsWhenYes(t *testing.T) { Put(r.Context(), updated). Return(nil) - err := OneLoginIdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{ + err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{ LpaID: "lpa-id", Donor: donordata.Donor{FirstNames: "b", LastName: "b", DateOfBirth: existingDob, Address: testAddress}, IdentityUserData: identity.UserData{FirstNames: "B", LastName: "B", DateOfBirth: identityDob, CurrentAddress: place.Address{Line1: "a"}}, @@ -140,7 +140,7 @@ func TestPostOneLoginIdentityDetailsWhenNo(t *testing.T) { r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) r.Header.Set("Content-Type", "application/x-www-form-urlencoded") - err := OneLoginIdentityDetails(nil, nil)(testAppData, w, r, &donordata.Provided{LpaID: "lpa-id"}) + err := IdentityDetails(nil, nil)(testAppData, w, r, &donordata.Provided{LpaID: "lpa-id"}) resp := w.Result() assert.Nil(t, err) @@ -160,7 +160,7 @@ func TestPostOneLoginIdentityDetailsWhenDonorStoreError(t *testing.T) { Put(r.Context(), mock.Anything). Return(expectedError) - err := OneLoginIdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{}) + err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{}) resp := w.Result() assert.Equal(t, expectedError, err) @@ -178,12 +178,12 @@ func TestPostOneLoginIdentityDetailsWhenValidationError(t *testing.T) { template := newMockTemplate(t) template.EXPECT(). - Execute(w, mock.MatchedBy(func(data *oneLoginIdentityDetailsData) bool { + Execute(w, mock.MatchedBy(func(data *identityDetailsData) bool { return assert.Equal(t, validationError, data.Errors) })). Return(nil) - err := OneLoginIdentityDetails(template.Execute, nil)(testAppData, w, r, &donordata.Provided{Donor: donordata.Donor{FirstNames: "a"}}) + err := IdentityDetails(template.Execute, nil)(testAppData, w, r, &donordata.Provided{Donor: donordata.Donor{FirstNames: "a"}}) resp := w.Result() assert.Nil(t, err) diff --git a/internal/donor/donorpage/register.go b/internal/donor/donorpage/register.go index 0b82faa0f8..ec85cec63c 100644 --- a/internal/donor/donorpage/register.go +++ b/internal/donor/donorpage/register.go @@ -404,7 +404,7 @@ func Register( handleWithDonor(donor.PathIdentityWithOneLoginCallback, page.CanGoBack, IdentityWithOneLoginCallback(oneLoginClient, sessionStore, donorStore, scheduledStore, eventClient)) handleWithDonor(donor.PathIdentityDetails, page.CanGoBack, - OneLoginIdentityDetails(tmpls.Get("identity_details.gohtml"), donorStore)) + IdentityDetails(tmpls.Get("identity_details.gohtml"), donorStore)) handleWithDonor(donor.PathRegisterWithCourtOfProtection, page.None, RegisterWithCourtOfProtection(tmpls.Get("register_with_court_of_protection.gohtml"), donorStore)) diff --git a/internal/donor/path.go b/internal/donor/path.go index 3982fc91cc..e448af8633 100644 --- a/internal/donor/path.go +++ b/internal/donor/path.go @@ -68,7 +68,7 @@ const ( PathHowWillYouConfirmYourIdentity = Path("/how-will-you-confirm-your-identity") PathHowWouldCertificateProviderPreferToCarryOutTheirRole = Path("/how-would-certificate-provider-prefer-to-carry-out-their-role") PathHowWouldYouLikeToSendEvidence = Path("/how-would-you-like-to-send-evidence") - PathIdentityDetails = Path("/one-login-identity-details") + PathIdentityDetails = Path("/identity-details") PathIdentityWithOneLogin = Path("/id/one-login") PathIdentityWithOneLoginCallback = Path("/id/one-login/callback") PathLifeSustainingTreatment = Path("/life-sustaining-treatment") diff --git a/internal/page/fixtures/donor.go b/internal/page/fixtures/donor.go index 292346f36e..4652b389b3 100644 --- a/internal/page/fixtures/donor.go +++ b/internal/page/fixtures/donor.go @@ -435,6 +435,9 @@ func updateLPAProgress( userData = identity.UserData{ Status: identity.StatusExpired, } + case "post-office": + userData = identity.UserData{} + donorDetails.Tasks.ConfirmYourIdentity = task.IdentityStatePending default: userData = identity.UserData{ Status: identity.StatusConfirmed, @@ -461,7 +464,9 @@ func updateLPAProgress( donorDetails.FailedVouchAttempts = attempts donorDetails.IdentityUserData = userData - donorDetails.Tasks.ConfirmYourIdentity = task.IdentityStateCompleted + if donorDetails.Tasks.ConfirmYourIdentity.IsNotStarted() { + donorDetails.Tasks.ConfirmYourIdentity = task.IdentityStateCompleted + } } if data.Progress >= slices.Index(progressValues, "signTheLpa") { diff --git a/lang/cy.json b/lang/cy.json index 1ee167659b..c66f7cf888 100644 --- a/lang/cy.json +++ b/lang/cy.json @@ -1532,5 +1532,8 @@ "costsIfYouAreEligibleContent": "

Welsh

", "whichFeeYouAreEligibleToPay": "Welsh", "repeatApplicationNoFeeRequestSubmitted": "

Welsh

Welsh

", - "whatHappensNextRepeatApplicationNoFeeContent": "

Welsh

" + "whatHappensNextRepeatApplicationNoFeeContent": "

Welsh

", + "theDetailsOnYourLpaDoNotMatch": "Welsh", + "theDetailsOnYourLpaCannotBeUpdatedAsSigned": "

Welsh

", + "ifYouDoNotWantRegisteredWithConfirmedDetailsWarning": "Welsh" } diff --git a/lang/en.json b/lang/en.json index 793cc3e007..50f5e27897 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1424,5 +1424,8 @@ "costsIfYouAreEligibleContent": "

For your repeat LPA application, you will pay:

You will not pay a fee if you were originally given an exemption or hardship application

", "whichFeeYouAreEligibleToPay": "which fee you are eligible to pay", "repeatApplicationNoFeeRequestSubmitted": "

Repeat application no fee request submitted.

We will review your LPA application.

", - "whatHappensNextRepeatApplicationNoFeeContent": "

Sign your LPA

You can still sign your LPA while we’re reviewing your application. Your certificate provider must be there as a witness when you sign it. However, we will not contact your certificate provider to provide their certificate until your repeat application has been approved.

If your application is successful

Once we have approved your repeat application, we will contact your certificate provider to provide their certificate.

If your application is not successful

We will contact you if we need more information or if your application is unsuccessful.

" + "whatHappensNextRepeatApplicationNoFeeContent": "

Sign your LPA

You can still sign your LPA while we’re reviewing your application. Your certificate provider must be there as a witness when you sign it. However, we will not contact your certificate provider to provide their certificate until your repeat application has been approved.

If your application is successful

Once we have approved your repeat application, we will contact your certificate provider to provide their certificate.

If your application is not successful

We will contact you if we need more information or if your application is unsuccessful.

", + "theDetailsOnYourLpaDoNotMatch": "The details on your LPA do not match the identity details you have confirmed using GOV.UK One Login.", + "theDetailsOnYourLpaCannotBeUpdatedAsSigned": "

The details on your LPA cannot be updated to match the identity details you confirmed using GOV.UK One Login. This is because you have already signed the LPA.

The Office of the Public Guardian (OPG) will compare your details, and may contact you for more information.

OPG will then decide if we can register your LPA. We will let you know if there is anything else you need to do.

If OPG decides we can register your LPA, it will be updated with your confirmed identity details.

", + "ifYouDoNotWantRegisteredWithConfirmedDetailsWarning": "If you do not want your LPA to be registered with your confirmed identity details, you must withdraw your LPA. You will lose any fees you have paid for this LPA." } diff --git a/terraform/account/kms_key_event_recieved_sqs.tf b/terraform/account/kms_key_event_recieved_sqs.tf index 28d3da715e..d4c38063d8 100644 --- a/terraform/account/kms_key_event_recieved_sqs.tf +++ b/terraform/account/kms_key_event_recieved_sqs.tf @@ -32,6 +32,7 @@ data "aws_iam_policy_document" "event_recieved_sqs_kms" { ] actions = [ "kms:Encrypt", + "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey", @@ -40,7 +41,7 @@ data "aws_iam_policy_document" "event_recieved_sqs_kms" { principals { type = "AWS" identifiers = [ - local.account.account_name == "development" ? "arn:aws:iam::${data.aws_caller_identity.global.account_id}:root" : "arn:aws:iam::${data.aws_caller_identity.global.account_id}:role/${local.account.account_name}-app-task-role", + local.account.account_name == "development" ? "arn:aws:iam::${data.aws_caller_identity.global.account_id}:root" : "arn:aws:iam::${data.aws_caller_identity.global.account_id}:role/event-received-${local.account.account_name}", ] } } diff --git a/web/template/donor/identity_details.gohtml b/web/template/donor/identity_details.gohtml index a6bf97be61..53858e4d6a 100644 --- a/web/template/donor/identity_details.gohtml +++ b/web/template/donor/identity_details.gohtml @@ -14,8 +14,10 @@ {{ if not .Provided.WantVoucher.IsYes }} {{ template "notification-banner" (notificationBanner .App "success" (trHtml .App "youHaveSuccessfullyConfirmedYourIdentitySuccess:donor") "success" "contents" ) }} {{ end }} - {{ else }} + {{ else if .Provided.CanChange }} {{ template "warning-banner" (content .App "someDetailsDoNotMatchIdentityDetailsWarning") }} + {{ else }} + {{ template "notification-banner" (notificationBanner .App "important" (trHtml .App "theDetailsOnYourLpaDoNotMatch") "heading") }} {{ end}} {{ if or .DetailsUpdated (not .DetailsMatch) }} @@ -67,7 +69,15 @@ {{ template "identity-details" (card .App .Provided.IdentityUserData) }} - {{ if .DetailsMatch }} + {{ if not .Provided.CanChange }} +

{{ tr .App "whatHappensNext" }}

+ + {{ trHtml .App "theDetailsOnYourLpaCannotBeUpdatedAsSigned" }} + + {{ template "warning" (content .App "ifYouDoNotWantRegisteredWithConfirmedDetailsWarning") }} + {{ end }} + + {{ if or .DetailsMatch (not .Provided.CanChange) }} {{ template "button" (button .App "returnToTaskList" "link" (global.Paths.TaskList.Format .Provided.LpaID)) }} {{ else }}