diff --git a/.secrets.baseline b/.secrets.baseline index c87da7404b..dc8e13b24a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -118,7 +118,7 @@ "filename": "cmd/mlpa/main.go", "hashed_secret": "dc9c2ac186b77d3f4f84400225d460ddcc5940db", "is_verified": false, - "line_number": 165 + "line_number": 186 } ], "cypress/e2e/attorney/trust-corporation.cy.js": [ @@ -127,7 +127,7 @@ "filename": "cypress/e2e/attorney/trust-corporation.cy.js", "hashed_secret": "6740d1ecb48c5c9ca3b2a3cb1ca2f4b4d4487473", "is_verified": false, - "line_number": 11 + "line_number": 12 } ], "cypress/e2e/certificate-provider/enter-reference-number.cy.js": [ @@ -136,7 +136,7 @@ "filename": "cypress/e2e/certificate-provider/enter-reference-number.cy.js", "hashed_secret": "6740d1ecb48c5c9ca3b2a3cb1ca2f4b4d4487473", "is_verified": false, - "line_number": 13 + "line_number": 15 } ], "docker/localstack/localstack-init.sh": [ @@ -154,7 +154,7 @@ "filename": "internal/notify/client_test.go", "hashed_secret": "e4615f10410260e2f37baec79cae53739ac3d1f5", "is_verified": false, - "line_number": 108 + "line_number": 102 } ], "internal/onelogin/client.go": [ @@ -163,51 +163,51 @@ "filename": "internal/onelogin/client.go", "hashed_secret": "5004c13435a31d83002dd4b4c36e61290782e833", "is_verified": false, - "line_number": 60 + "line_number": 50 } ], - "internal/onelogin/client_test.go": [ + "internal/onelogin/configuration_test.go": [ { "type": "Base64 High Entropy String", - "filename": "internal/onelogin/client_test.go", + "filename": "internal/onelogin/configuration_test.go", "hashed_secret": "202abfba0a5645bdd42c4365448acbccd996fa4b", "is_verified": false, - "line_number": 32 + "line_number": 38 }, { "type": "Base64 High Entropy String", - "filename": "internal/onelogin/client_test.go", + "filename": "internal/onelogin/configuration_test.go", "hashed_secret": "baf6f44796f2de09b769741c74019125d190ec10", "is_verified": false, - "line_number": 32 + "line_number": 38 }, { "type": "Base64 High Entropy String", - "filename": "internal/onelogin/client_test.go", + "filename": "internal/onelogin/configuration_test.go", "hashed_secret": "ce36062048bf275566cd44572e9b8938e765a7b2", "is_verified": false, - "line_number": 32 + "line_number": 38 }, { "type": "Base64 High Entropy String", - "filename": "internal/onelogin/client_test.go", + "filename": "internal/onelogin/configuration_test.go", "hashed_secret": "d151606c5a71f1a890ed49e96a05aca41d78430f", "is_verified": false, - "line_number": 32 + "line_number": 38 }, { "type": "Hex High Entropy String", - "filename": "internal/onelogin/client_test.go", + "filename": "internal/onelogin/configuration_test.go", "hashed_secret": "d2b22571923d78d2ec43c00b0ab37c26cf31dc0a", "is_verified": false, - "line_number": 32 + "line_number": 38 }, { "type": "Hex High Entropy String", - "filename": "internal/onelogin/client_test.go", + "filename": "internal/onelogin/configuration_test.go", "hashed_secret": "ddef058197b1e333dd8535856a8942e1a96ea975", "is_verified": false, - "line_number": 32 + "line_number": 38 } ], "internal/onelogin/exchange_test.go": [ @@ -219,15 +219,6 @@ "line_number": 93 } ], - "internal/onelogin/user_info_test.go": [ - { - "type": "Secret Keyword", - "filename": "internal/onelogin/user_info_test.go", - "hashed_secret": "feeedadaffb3ce9f8d45984cff35778d9a42339d", - "is_verified": false, - "line_number": 88 - } - ], "internal/page/attorney/enter_reference_number_test.go": [ { "type": "Hex High Entropy String", @@ -250,14 +241,14 @@ "filename": "internal/page/certificateprovider/enter_reference_number_test.go", "hashed_secret": "6740d1ecb48c5c9ca3b2a3cb1ca2f4b4d4487473", "is_verified": false, - "line_number": 221 + "line_number": 206 }, { "type": "Hex High Entropy String", "filename": "internal/page/certificateprovider/enter_reference_number_test.go", "hashed_secret": "6c688927eafd673c7d9194b46b2b88e096e6c9f7", "is_verified": false, - "line_number": 232 + "line_number": 217 } ], "internal/page/share_code.go": [ @@ -266,7 +257,7 @@ "filename": "internal/page/share_code.go", "hashed_secret": "6740d1ecb48c5c9ca3b2a3cb1ca2f4b4d4487473", "is_verified": false, - "line_number": 35 + "line_number": 60 } ], "internal/page/share_code_test.go": [ @@ -275,7 +266,7 @@ "filename": "internal/page/share_code_test.go", "hashed_secret": "6740d1ecb48c5c9ca3b2a3cb1ca2f4b4d4487473", "is_verified": false, - "line_number": 124 + "line_number": 82 } ], "internal/place/client.go": [ @@ -345,5 +336,5 @@ } ] }, - "generated_at": "2023-11-28T11:59:12Z" + "generated_at": "2023-12-12T16:55:24Z" } diff --git a/Makefile b/Makefile index b904eb0199..df33238724 100644 --- a/Makefile +++ b/Makefile @@ -81,46 +81,58 @@ run-structurizr-export: docker run --rm -v $(PWD)/docs/architecture/dsl/local:/usr/local/structurizr structurizr/cli \ export -workspace /usr/local/structurizr/workspace.dsl -format mermaid -scan-lpas: ##@app dumps all entries in the lpas dynamodb table - docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb scan --table-name lpas +scan-lpas: ##@dynamodb dumps all entries in the lpas dynamodb table + docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb --region eu-west-1 scan --table-name lpas -get-lpa: ##@app dumps all entries in the lpas dynamodb table that are related to the LPA id supplied e.g. get-lpa id=abc-123 - docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb \ +get-lpa: ##@dynamodb dumps all entries in the lpas dynamodb table that are related to the LPA id supplied e.g. get-lpa id=abc-123 + docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb --region eu-west-1 \ query --table-name lpas --key-condition-expression 'PK = :pk' --expression-attribute-values '{":pk": {"S": "LPA#$(id)"}}' -get-donor-session-id: ##@app get donor session id by the LPA id supplied e.g. get-donor-session-id lpaId=abc-123 - docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb \ +get-donor-session-id: ##@dynamodb get donor session id by the LPA id supplied e.g. get-donor-session-id lpaId=abc-123 + docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb --region eu-west-1 \ query --table-name lpas --key-condition-expression 'PK = :pk and begins_with(SK, :sk)' --expression-attribute-values '{":pk": {"S": "LPA#$(lpaId)"}, ":sk": {"S": "#DONOR#"}}' | jq -r .Items[0].SK.S | sed 's/#DONOR#//g' -get-documents: ##@app dumps all documents in the lpas dynamodb table that are related to the LPA id supplied e.g. get-documents lpaId=abc-123 - docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb \ +get-documents: ##@dynamodb dumps all documents in the lpas dynamodb table that are related to the LPA id supplied e.g. get-documents lpaId=abc-123 + docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb --region eu-west-1 \ query --table-name lpas --key-condition-expression 'PK = :pk and begins_with(SK, :sk)' --expression-attribute-values '{":pk": {"S": "LPA#$(lpaId)"}, ":sk": {"S": "#DOCUMENT#"}}' -emit-evidence-received: ##@app emits an evidence-received event with the given LpaUID e.g. emit-evidence-received uid=abc-123 +delete-all-items: ##@dynamodb deletes and recreates lpas dynamodb table + docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb --region eu-west-1 \ + delete-table --table-name lpas + + docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb create-table \ + --region eu-west-1 \ + --table-name lpas \ + --attribute-definitions AttributeName=PK,AttributeType=S AttributeName=SK,AttributeType=S AttributeName=LpaUID,AttributeType=S AttributeName=UpdatedAt,AttributeType=S \ + --key-schema AttributeName=PK,KeyType=HASH AttributeName=SK,KeyType=RANGE \ + --provisioned-throughput ReadCapacityUnits=1000,WriteCapacityUnits=1000 \ + --global-secondary-indexes file://dynamodb-lpa-gsi-schema.json + +emit-evidence-received: ##@events emits an evidence-received event with the given LpaUID e.g. emit-evidence-received uid=abc-123 curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"evidence-received","source":"opg.poas.sirius","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"UID":"$(uid)"}}' -emit-reduced-fee-approved: ##@app emits a reduced-fee-approved event with the given LpaUID e.g. emit-reduced-fee-approved uid=abc-123 +emit-reduced-fee-approved: ##@events emits a reduced-fee-approved event with the given LpaUID e.g. emit-reduced-fee-approved uid=abc-123 curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"reduced-fee-approved","source":"opg.poas.sirius","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"UID":"$(uid)"}}' -emit-reduced-fee-declined: ##@app emits a reduced-fee-declined event with the given LpaUID e.g. emit-reduced-fee-declined uid=abc-123 +emit-reduced-fee-declined: ##@events emits a reduced-fee-declined event with the given LpaUID e.g. emit-reduced-fee-declined uid=abc-123 curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"reduced-fee-declined","source":"opg.poas.sirius","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"UID":"$(uid)"}}' -emit-more-evidence-required: ##@app emits a more-evidence-required event with the given LpaUID e.g. emit-more-evidence-required uid=abc-123 +emit-more-evidence-required: ##@events emits a more-evidence-required event with the given LpaUID e.g. emit-more-evidence-required uid=abc-123 curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"more-evidence-required","source":"opg.poas.sirius","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"UID":"$(uid)"}}' -emit-object-tags-added-with-virus: ##@app emits a ObjectTagging:Put event with the given S3 key e.g. emit-object-tags-added-with-virus key=doc/key. Also ensures a tag with virus-scan-status exists on an existing object set to infected +emit-object-tags-added-with-virus: ##@events emits a ObjectTagging:Put event with the given S3 key e.g. emit-object-tags-added-with-virus key=doc/key. Also ensures a tag with virus-scan-status exists on an existing object set to infected docker compose -f docker/docker-compose.yml exec localstack awslocal s3api \ put-object-tagging --bucket evidence --key $(key) --tagging '{"TagSet": [{ "Key": "virus-scan-status", "Value": "infected" }]}' curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"Records":[{"eventSource":"aws:s3","eventTime":"2023-10-23T15:58:33.081Z","eventName":"ObjectTagging:Put","s3":{"bucket":{"name":"uploads-opg-modernising-lpa-eu-west-1"},"object":{"key":"$(key)"}}}]}' -emit-object-tags-added-without-virus: ##@app emits a ObjectTagging:Put event with the given S3 key e.g. emit-object-tags-added-with-virus key=doc/key. Also ensures a tag with virus-scan-status exists on an existing object set to ok +emit-object-tags-added-without-virus: ##@events emits a ObjectTagging:Put event with the given S3 key e.g. emit-object-tags-added-with-virus key=doc/key. Also ensures a tag with virus-scan-status exists on an existing object set to ok docker compose -f docker/docker-compose.yml exec localstack awslocal s3api \ put-object-tagging --bucket evidence --key $(key) --tagging '{"TagSet": [{ "Key": "virus-scan-status", "Value": "ok" }]}' curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"Records":[{"eventSource":"aws:s3","eventTime":"2023-10-23T15:58:33.081Z","eventName":"ObjectTagging:Put","s3":{"bucket":{"name":"uploads-opg-modernising-lpa-eu-west-1"},"object":{"key":"$(key)"}}}]}' -emit-uid-requested: ##@app emits a uid-requested event with the given detail e.g. emit-uid-requested lpaId=abc sessionId=xyz +emit-uid-requested: ##@events emits a uid-requested event with the given detail e.g. emit-uid-requested lpaId=abc sessionId=xyz curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"uid-requested","source":"opg.poas.makeregister","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"LpaID":"$(lpaId)","DonorSessionID":"$(sessionId)","Type":"pfa","Donor":{"Name":"abc","Dob":"2000-01-01","Postcode":"F1 1FF"}}}' logs: ##@app tails logs for all containers running diff --git a/cmd/mock-onelogin/main.go b/cmd/mock-onelogin/main.go index 67ed4edb7d..d4588b3eaa 100644 --- a/cmd/mock-onelogin/main.go +++ b/cmd/mock-onelogin/main.go @@ -39,6 +39,7 @@ type sessionData struct { user string nonce string identity bool + sub string } type OpenIdConfig struct { @@ -132,6 +133,19 @@ func authorize() http.HandlerFunc { return } + if r.Method == http.MethodGet { + io.WriteString(w, ` + +

Mock GOV.UK One Login

+
+

Sign in using a OneLogin sub (to ignore, leave empty)

+ + + +
`) + return + } + redirectUri := r.FormValue("redirect_uri") if redirectUri == "" { log.Fatal("Required query param 'redirect_uri' missing from request") @@ -156,7 +170,9 @@ func authorize() http.HandlerFunc { nonce: r.FormValue("nonce"), user: r.FormValue("user"), identity: wantsIdentity, + sub: r.FormValue("sub"), } + u.RawQuery = q.Encode() log.Printf("Redirecting to %s", u.String()) @@ -166,8 +182,16 @@ func authorize() http.HandlerFunc { func userInfo(privateKey *ecdsa.PrivateKey) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + token := tokens[strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")] + + sub := randomString("sub-", 12) + + if token.sub != "" { + sub = token.sub + } + userInfo := UserInfoResponse{ - Sub: randomString("sub-", 12), + Sub: sub, Email: "simulate-delivered@notifications.service.gov.uk", EmailVerified: true, Phone: "01406946277", @@ -175,8 +199,6 @@ func userInfo(privateKey *ecdsa.PrivateKey) http.HandlerFunc { UpdatedAt: 1311280970, } - token := tokens[strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")] - if token.identity { givenName, familyName, birthDate := userDetails(token.user) @@ -204,6 +226,7 @@ func userInfo(privateKey *ecdsa.PrivateKey) http.HandlerFunc { }).SignedString(privateKey) } + log.Printf("Logging in with sub %s", sub) json.NewEncoder(w).Encode(userInfo) } } diff --git a/cypress/e2e/attorney/trust-corporation.cy.js b/cypress/e2e/attorney/trust-corporation.cy.js index 26c33e362f..af0190719f 100644 --- a/cypress/e2e/attorney/trust-corporation.cy.js +++ b/cypress/e2e/attorney/trust-corporation.cy.js @@ -6,6 +6,7 @@ describe('As a trust corporation', () => { // start cy.contains('a', 'Start').click(); + cy.contains('button', 'Sign in').click(); // enter reference number cy.get('#f-reference-number').type('abcdef123456'); diff --git a/cypress/e2e/certificate-provider/enter-reference-number.cy.js b/cypress/e2e/certificate-provider/enter-reference-number.cy.js index f09b7c16ee..ecf6d99240 100644 --- a/cypress/e2e/certificate-provider/enter-reference-number.cy.js +++ b/cypress/e2e/certificate-provider/enter-reference-number.cy.js @@ -2,12 +2,14 @@ const { TestEmail } = require("../../support/e2e"); describe('Enter reference number', () => { beforeEach(() => { - cy.visit('/fixtures/certificate-provider?redirect=/certificate-provider-start&use-test-code=1&email=' + TestEmail); - }); + cy.visit(`/fixtures/certificate-provider?redirect=/certificate-provider-start&use-test-code=1&email=${TestEmail}`); - it('can enter a valid reference number', { pageLoadTimeout: 6000 }, () => { cy.contains('a', 'Start').click() + cy.contains('button', 'Sign in').click(); + cy.url().should('contain', '/certificate-provider-enter-reference-number') + }); + it('can enter a valid reference number', { pageLoadTimeout: 6000 }, () => { cy.checkA11yApp(); cy.get('#f-reference-number').type('abcdef123456'); @@ -17,12 +19,10 @@ describe('Enter reference number', () => { }); it('errors when empty number', () => { - cy.contains('a', 'Start').click() + cy.contains('Save and continue').click(); cy.checkA11yApp(); - cy.contains('Save and continue').click(); - cy.get('.govuk-error-summary').within(() => { cy.contains('Enter your 12 character reference number'); }); @@ -31,13 +31,11 @@ describe('Enter reference number', () => { }); it('errors when incorrect code', () => { - cy.contains('a', 'Start').click() - - cy.checkA11yApp(); - cy.get('#f-reference-number').type('notATestCode'); cy.contains('Save and continue').click(); + cy.checkA11yApp(); + cy.get('.govuk-error-summary').within(() => { cy.contains('The reference number you entered is incorrect, please check it and try again'); }); @@ -46,17 +44,16 @@ describe('Enter reference number', () => { }); it('errors when incorrect code length', () => { - cy.contains('a', 'Start').click() - - cy.checkA11yApp(); - cy.get('#f-reference-number').type('tooShort'); cy.contains('Save and continue').click(); + cy.checkA11yApp(); + cy.get('.govuk-error-summary').within(() => { cy.contains('The reference number you enter must be 12 characters'); }); cy.contains('[for=f-reference-number] ~ .govuk-error-message', 'The reference number you enter must be 12 characters'); }); + }); diff --git a/cypress/e2e/certificate-provider/start.cy.js b/cypress/e2e/certificate-provider/start.cy.js index a3a0e8d605..9b0e781c03 100644 --- a/cypress/e2e/certificate-provider/start.cy.js +++ b/cypress/e2e/certificate-provider/start.cy.js @@ -7,7 +7,7 @@ describe('Start', () => { cy.contains('a', 'Start').click(); if (Cypress.config().baseUrl.includes('localhost')) { - cy.url().should('contain', '/certificate-provider-enter-reference-number') + cy.url().should('contain', '/authorize') } else { cy.origin('https://signin.integration.account.gov.uk', () => { cy.url().should('contain', '/') diff --git a/go.sum b/go.sum index 7602ac938b..ddc36ed905 100644 --- a/go.sum +++ b/go.sum @@ -1,107 +1,59 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= -github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y= github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= -github.com/aws/aws-sdk-go-v2 v1.23.4 h1:2P20ZjH0ouSAu/6yZep8oCmTReathLuEu6dwoqEgjts= -github.com/aws/aws-sdk-go-v2 v1.23.4/go.mod h1:t3szzKfP0NeRU27uBFczDivYJjsmSnqI8kIvKyWb9ds= github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk= github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 h1:Zx9+31KyB8wQna6SXFWOewlgoY5uGdDAu6PTOEU3OQI= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3/go.mod h1:zxbEJhRdKTH1nqS2qu6UJ7zGe25xaHxZXaC2CvuQFnA= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= -github.com/aws/aws-sdk-go-v2/config v1.25.10 h1:qw/e8emDtNufTkrAU86DlQ18DruMyyM7ttW6Lgwp4v0= -github.com/aws/aws-sdk-go-v2/config v1.25.10/go.mod h1:203YiAtb6XyoGxXMPsUVwEcuxCiTQY/r8P27IDjfvMc= github.com/aws/aws-sdk-go-v2/config v1.26.0 h1:uItWWbD/FmHPGSa6GJFyZJD/RPakVjS0fmoq1vccjNw= github.com/aws/aws-sdk-go-v2/config v1.26.0/go.mod h1:8Rf77VTcX9MMkoMIsCnuwmef+Y1bs2Zhvw9IXHdD/Po= -github.com/aws/aws-sdk-go-v2/credentials v1.16.8 h1:phw9nRLy/77bPk6Mfu2SHCOnHwfVB7WWrOa5rZIY2Fc= -github.com/aws/aws-sdk-go-v2/credentials v1.16.8/go.mod h1:MrS4SOin6adbO6wgWhdifyPiq+TX7fPPwyA/ZLC1F5M= github.com/aws/aws-sdk-go-v2/credentials v1.16.11 h1:Gcut3tJSU7F/C5W/NnFimqnJqljF58rmaw7QlbigN3U= github.com/aws/aws-sdk-go-v2/credentials v1.16.11/go.mod h1:CysUbSCfqvEbEQTd9Ubg2RrJy2EFM+AUHJOqqj0guTo= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.8 h1:KwIBysVyixaXIRB+2VubJvWIg+SQDiZo9Jzk307VypE= -github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.8/go.mod h1:Xh7e5+pCHD/yuLu5nJDyZGt8xjCkznHrwYRDwLeQCBU= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.11 h1:OkRlFn6J6t+sFh+mmBAAj4QPLeIpYnRLv+GfxkKSdq0= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.11/go.mod h1:YHEC9mcDnGPUNBrhlESfkH+OWTlFjoLPKqCqDI5gk/s= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.8 h1:tQZLSPC2Zj2CqZHonLmWEvCsbpMX5tQvaYJWHadcPek= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.8/go.mod h1:5+YpvTHDFffykWr5qAGjqwoh8oVYZOddL3sSrEN7lws= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.7 h1:eMqD7ku6WGdmcWWXPYun9m6yk6feSULLhJlAtN6rYG4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.7/go.mod h1:0oBIfcDV6LScxEW0VgOqxT3e4aqKRp+SYhB9wAd5E3Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.7 h1:+XYhWhgWs5F3Zx8oa49CXzNvfXrItaDjZB/M172fcHQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.7/go.mod h1:L6tcSRyCGxcKfDWUrmv2jv8G1cLDU7d0FUpEFpG9bVE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.7 h1:3VaUNB1LclLomv82VnP5QnxAfowG+Ro4m82+af9wjZ4= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.7/go.mod h1:D5i0c+qvEY0LV5F4elFZd+mYnvHQbufCLHNHoBfQR2g= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.2 h1:IPMh5Selz3UKr1rY8FaNTv4Dx/Tl/G/yGpnZlhyuk+A= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.2/go.mod h1:Mj372IvfZ9ftME7Kdo74stz3KAjMA+WC7Fzzry9uCDI= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.5 h1:9ooibu+7PEhE2/4oFrYXSEwFCZ+Ii1CcYCO7/zpeG50= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.5/go.mod h1:o7TD9sjdgrl8l/g2a2IkYjuhxjPy9DMP2sWo7piaRBQ= -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.1 h1:vitPmwVNVZm/3aWsOQJj+/9ZuHiNEz/kyEpFPJ3Wlqo= -github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.1/go.mod h1:ePEkLfOEomruqa7azMd42TEB5G8KRwzKO0DCEu7aPrE= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.4 h1:EHZlr+YE2DWVeaw97XMpWuhtvLjkTJJHFD2yN1ALoqo= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.4/go.mod h1:T461RxBmf94zuOuIUifdy5Zim3DJTo0X4nXE3vodXQI= -github.com/aws/aws-sdk-go-v2/service/eventbridge v1.26.1 h1:QYOoMd15u8f30dEBqWgPm6P+l5+6EZ9O4ifpLTF5Sqc= -github.com/aws/aws-sdk-go-v2/service/eventbridge v1.26.1/go.mod h1:gygD37EGouKmykQmtWhtgKnwl1Ysp/FwSFG6gWo1N9M= github.com/aws/aws-sdk-go-v2/service/eventbridge v1.26.4 h1:jZEgLxiDUK6QWPaGS52+kNxTdHfmadd5eZhotq3d0h8= github.com/aws/aws-sdk-go-v2/service/eventbridge v1.26.4/go.mod h1:QGQ7G5ny9UZIl+2nxlZWFi/FMC+QSbPJ5fhRadEPhmA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 h1:e3PCNeEaev/ZF01cQyNZgmYE9oYYePIMJs2mWSKG514= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3/go.mod h1:gIeeNyaL8tIEqZrzAnTeyhHcE0yysCtcaP+N9kxLZ+E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.7 h1:Mft1tmIK1fkFS9l9sYVYiN+OdgXeOcQ9ZS3SxKOh3A4= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.7/go.mod h1:QWI83fhocxDaN3b74N8rrvET60CBaike5lQ+5sm3OcE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.8.8 h1:5I9xgPkS/LKLZOsyRzAUgRRNbhImCz4Zs9lAeBnIYWM= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.8.8/go.mod h1:s1pcqNgty0l9w56NUljd4HDTFRlzp2MsiyrrDjucE0I= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.8.10 h1:h8uweImUHGgyNKrxIUwpPs6XiH0a6DJ17hSJvFLgPAo= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.8.10/go.mod h1:LZKVtMBiZfdvUWgwg61Qo6kyAmE5rn9Dw36AqnycvG8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.7 h1:dU+ZyhvqMB/T/TxjGagHMCdyUiqaThRIaMu3YvKiSQI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.7/go.mod h1:SGORuNqoXyWfTvTp/gBGJfv8jRvW/+nha0XhnIXVI+o= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.7 h1:ybtGXm0qFVFi0hFUF7eFAVnL3ntl9MO7lrxhhGP7KYU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.7/go.mod h1:BUyWJUKAnNqoEq1LfyQxy+Eh4U8Y3c5w2C6m21f3yvI= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM= -github.com/aws/aws-sdk-go-v2/service/s3 v1.47.1 h1:0/W5F+LlXzKZ7KTsRcD8pugasVnsrjUWmhOsN/LdSFY= -github.com/aws/aws-sdk-go-v2/service/s3 v1.47.1/go.mod h1:TqThLn4bRCn/UYf960hNZgPPjmxc17fQcwmjfuG6D5k= github.com/aws/aws-sdk-go-v2/service/s3 v1.47.4 h1:iEkLh6fe2ATtH5PGynlJ1SdnbZuZgoWLdvSedjwmqKk= github.com/aws/aws-sdk-go-v2/service/s3 v1.47.4/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.1 h1:gQm31RBvxft4PYLy8d6V/8wjlUWAA8/NWSOfx+d/5Fk= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.1/go.mod h1:N/QCLJS0Om2/uvAIQ/UCYW8BpJl0q3DiyyIgRq3WAT4= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.4 h1:gsiwBC1ca43hCwyYilWEsC1y/NSkLj9fGyIV7pRt42U= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.4/go.mod h1:4Ae1NCLK6ghmjzd45Tc33GgCKhUWD2ORAlULtMO1Cbs= -github.com/aws/aws-sdk-go-v2/service/sqs v1.29.1 h1:OZI2aJxnfOZzB0uhyTaYIW6MeRMb1Qd2eLMjh0bFsRg= -github.com/aws/aws-sdk-go-v2/service/sqs v1.29.1/go.mod h1:GiU88YWgOho2cyEyS2YZo3GYz/j4etRYKWbJdcYgpuQ= github.com/aws/aws-sdk-go-v2/service/sqs v1.29.4 h1:sJok0qaklBIURtbOVH3Yp4+YXZ1zP2pnh2ToQVlArKg= github.com/aws/aws-sdk-go-v2/service/sqs v1.29.4/go.mod h1:mCUv04gd/7g+/HNzDB4X6dzJuygji0ckvB3Lg/TdG5Y= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.1 h1:V40g2daNO3l1J94JYwqfkyvQMYXi5I25fs3fNQW8iDs= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.1/go.mod h1:0ZWQJP/mBOUxkCvZKybZNz1XmdUKSBxoF0dzgfxtvDs= github.com/aws/aws-sdk-go-v2/service/sso v1.18.4 h1:2UVO4N/polvKeP+yCA8TLEmidEKxmNTeVpsZnj/bbgA= github.com/aws/aws-sdk-go-v2/service/sso v1.18.4/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.1 h1:uQrj7SpUNC3r55vc1CDh3qV9wJC66lz546xM9dhSo5s= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.1/go.mod h1:oyaTk5xEAOuPXX1kCD7HmIeuLqdj3Bk5yGkqGXtGi14= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.4 h1:3JXkQ1F5n73qTpSPas6AQ8/6HFksgnB24JlNPLt3SlM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.4/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38= -github.com/aws/aws-sdk-go-v2/service/sts v1.26.1 h1:K33V7L0XDdb23FMOZySr8bon1jou5SHn1fiv7NJ1SUg= -github.com/aws/aws-sdk-go-v2/service/sts v1.26.1/go.mod h1:YtXUl/sfnS06VksYhr855hTQf2HphfT1Xv/EwuzbPjg= github.com/aws/aws-sdk-go-v2/service/sts v1.26.4 h1:gaRFldXhoT36jVMfQ+AjAYwSfjO5LMgy1u0ObcKFhhc= github.com/aws/aws-sdk-go-v2/service/sts v1.26.4/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU= -github.com/aws/smithy-go v1.18.1 h1:pOdBTUfXNazOlxLrgeYalVnuTpKreACHtc62xLwIB3c= -github.com/aws/smithy-go v1.18.1/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf h1:WCnJxXZXx9c8gwz598wvdqmu+YTzB9wx2X1OovK3Le8= @@ -218,8 +170,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/nicksnyder/go-i18n/v2 v2.2.2 h1:Iv/FL6pvYmDqybEZkr4TrOv8jSHezwpE77K68kcaft8= -github.com/nicksnyder/go-i18n/v2 v2.2.2/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ= github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -258,7 +208,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/contrib/detectors/aws/ecs v1.21.1 h1:dFXbiLSr2NMG/Rqj2u9go799AHbCohQx2kplNcDtysQ= go.opentelemetry.io/contrib/detectors/aws/ecs v1.21.1/go.mod h1:PgdaTLOTeKMoBgYRRkskLgH5rtQyWWf50HpU/CvhYwM= go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.46.1 h1:PGmSzEMllKQwBQHe9SERAsCytvgLhsb8OrRLeW+40xw= @@ -290,15 +239,12 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= -golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -309,7 +255,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -317,7 +262,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -327,17 +271,12 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -348,7 +287,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -390,7 +328,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/app/app.go b/internal/app/app.go index 80745ce5a3..1268fef409 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -50,6 +50,7 @@ type DynamoClient interface { DeleteOne(ctx context.Context, pk, sk string) error Update(ctx context.Context, pk, sk string, values map[string]dynamodbtypes.AttributeValue, expression string) error BatchPut(ctx context.Context, items []interface{}) error + OneByUID(ctx context.Context, uid string, v interface{}) error } //go:generate mockery --testonly --inpackage --name S3Client --structname mockS3Client @@ -152,6 +153,7 @@ func App( addressClient, notifyClient, shareCodeSender, + dashboardStore, ) attorney.Register( @@ -166,6 +168,7 @@ func App( shareCodeStore, errorHandler, notFoundHandler, + dashboardStore, ) donor.Register( @@ -188,6 +191,7 @@ func App( evidenceReceivedStore, documentStore, eventClient, + dashboardStore, ) return withAppData(page.ValidateCsrf(rootMux, sessionStore, random.String, errorHandler), localizer, lang, rumConfig, staticHash, oneloginURL) diff --git a/internal/app/dashboard_store.go b/internal/app/dashboard_store.go index 22164c937e..53b3dc542a 100644 --- a/internal/app/dashboard_store.go +++ b/internal/app/dashboard_store.go @@ -48,6 +48,21 @@ func (k keys) isAttorneyDetails() bool { return strings.HasPrefix(k.SK, attorneyKey("")) } +func (s *dashboardStore) SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) { + var links []lpaLink + if err := s.dynamoClient.AllForActor(ctx, subKey(sub), &links); err != nil { + return false, err + } + + for _, link := range links { + if link.ActorType == actorType { + return true, nil + } + } + + return false, nil +} + func (s *dashboardStore) GetAll(ctx context.Context) (donor, attorney, certificateProvider []page.LpaAndActorTasks, err error) { data, err := page.SessionDataFromContext(ctx) if err != nil { diff --git a/internal/app/dashboard_store_test.go b/internal/app/dashboard_store_test.go index 6994553fd1..4ded6d2947 100644 --- a/internal/app/dashboard_store_test.go +++ b/internal/app/dashboard_store_test.go @@ -182,3 +182,51 @@ func TestDashboardStoreGetAllWhenAllByKeysErrors(t *testing.T) { _, _, _, err := dashboardStore.GetAll(ctx) assert.Equal(t, expectedError, err) } + +func TestDashboardStoreSubExists(t *testing.T) { + testCases := map[string]struct { + lpas []lpaLink + expectedExists bool + actorType actor.Type + }{ + "lpas exist - correct actor": { + lpas: []lpaLink{{PK: "LPA#123", SK: "#SUB#a-sub-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}}, + expectedExists: true, + actorType: actor.TypeDonor, + }, + "lpas exist - incorrect actor": { + lpas: []lpaLink{{PK: "LPA#123", SK: "#SUB#a-sub-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}}, + expectedExists: false, + actorType: actor.TypeAttorney, + }, + "lpas do not exist": { + expectedExists: false, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + dynamoClient := newMockDynamoClient(t) + dynamoClient.ExpectAllForActor(context.Background(), "#SUB#a-sub-id", + tc.lpas, nil) + + dashboardStore := &dashboardStore{dynamoClient: dynamoClient} + exists, err := dashboardStore.SubExistsForActorType(context.Background(), "a-sub-id", tc.actorType) + + assert.Nil(t, err) + assert.Equal(t, tc.expectedExists, exists) + }) + } +} + +func TestDashboardStoreSubExistsWhenDynamoError(t *testing.T) { + dynamoClient := newMockDynamoClient(t) + dynamoClient.ExpectAllForActor(context.Background(), "#SUB#a-sub-id", + []lpaLink{}, expectedError) + + dashboardStore := &dashboardStore{dynamoClient: dynamoClient} + exists, err := dashboardStore.SubExistsForActorType(context.Background(), "a-sub-id", actor.TypeDonor) + + assert.Equal(t, expectedError, err) + assert.False(t, exists) +} diff --git a/internal/app/mock_DynamoClient_test.go b/internal/app/mock_DynamoClient_test.go index 9ce2f7278d..1eb03bc61e 100644 --- a/internal/app/mock_DynamoClient_test.go +++ b/internal/app/mock_DynamoClient_test.go @@ -194,6 +194,20 @@ func (_m *mockDynamoClient) OneByPartialSk(ctx context.Context, pk string, parti return r0 } +// OneByUID provides a mock function with given fields: ctx, uid, v +func (_m *mockDynamoClient) OneByUID(ctx context.Context, uid string, v interface{}) error { + ret := _m.Called(ctx, uid, v) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + r0 = rf(ctx, uid, v) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // Put provides a mock function with given fields: ctx, v func (_m *mockDynamoClient) Put(ctx context.Context, v interface{}) error { ret := _m.Called(ctx, v) diff --git a/internal/page/attorney/mock_DashboardStore_test.go b/internal/page/attorney/mock_DashboardStore_test.go new file mode 100644 index 0000000000..a1091914a2 --- /dev/null +++ b/internal/page/attorney/mock_DashboardStore_test.go @@ -0,0 +1,101 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package attorney + +import ( + context "context" + + actor "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + + mock "github.com/stretchr/testify/mock" + + page "github.com/ministryofjustice/opg-modernising-lpa/internal/page" +) + +// mockDashboardStore is an autogenerated mock type for the DashboardStore type +type mockDashboardStore struct { + mock.Mock +} + +// GetAll provides a mock function with given fields: ctx +func (_m *mockDashboardStore) GetAll(ctx context.Context) ([]page.LpaAndActorTasks, []page.LpaAndActorTasks, []page.LpaAndActorTasks, error) { + ret := _m.Called(ctx) + + var r0 []page.LpaAndActorTasks + var r1 []page.LpaAndActorTasks + var r2 []page.LpaAndActorTasks + var r3 error + if rf, ok := ret.Get(0).(func(context.Context) ([]page.LpaAndActorTasks, []page.LpaAndActorTasks, []page.LpaAndActorTasks, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []page.LpaAndActorTasks); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) []page.LpaAndActorTasks); ok { + r1 = rf(ctx) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(2).(func(context.Context) []page.LpaAndActorTasks); ok { + r2 = rf(ctx) + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(3).(func(context.Context) error); ok { + r3 = rf(ctx) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 +} + +// SubExistsForActorType provides a mock function with given fields: ctx, sub, actorType +func (_m *mockDashboardStore) SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) { + ret := _m.Called(ctx, sub, actorType) + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) (bool, error)); ok { + return rf(ctx, sub, actorType) + } + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) bool); ok { + r0 = rf(ctx, sub, actorType) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, actor.Type) error); ok { + r1 = rf(ctx, sub, actorType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTnewMockDashboardStore interface { + mock.TestingT + Cleanup(func()) +} + +// newMockDashboardStore creates a new instance of mockDashboardStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockDashboardStore(t mockConstructorTestingTnewMockDashboardStore) *mockDashboardStore { + mock := &mockDashboardStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/page/attorney/register.go b/internal/page/attorney/register.go index 695dcff3ba..7e9b4510d8 100644 --- a/internal/page/attorney/register.go +++ b/internal/page/attorney/register.go @@ -70,6 +70,12 @@ type AddressClient interface { LookupPostcode(ctx context.Context, postcode string) ([]place.Address, error) } +//go:generate mockery --testonly --inpackage --name DashboardStore --structname mockDashboardStore +type DashboardStore interface { + GetAll(ctx context.Context) (donor, attorney, certificateProvider []page.LpaAndActorTasks, err error) + SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) +} + func Register( rootMux *http.ServeMux, logger Logger, @@ -82,13 +88,14 @@ func Register( shareCodeStore ShareCodeStore, errorHandler page.ErrorHandler, notFoundHandler page.Handler, + dashboardStore DashboardStore, ) { handleRoot := makeHandle(rootMux, sessionStore, errorHandler) handleRoot(page.Paths.Attorney.Login, None, page.Login(oneLoginClient, sessionStore, random.String, page.Paths.Attorney.LoginCallback)) handleRoot(page.Paths.Attorney.LoginCallback, None, - page.LoginCallback(oneLoginClient, sessionStore, page.Paths.Attorney.EnterReferenceNumber)) + page.LoginCallback(oneLoginClient, sessionStore, page.Paths.Attorney.EnterReferenceNumber, dashboardStore, actor.TypeAttorney)) handleRoot(page.Paths.Attorney.EnterReferenceNumber, RequireSession, EnterReferenceNumber(tmpls.Get("attorney_enter_reference_number.gohtml"), shareCodeStore, sessionStore, attorneyStore)) diff --git a/internal/page/attorney/register_test.go b/internal/page/attorney/register_test.go index 21c8914ea1..09385c269a 100644 --- a/internal/page/attorney/register_test.go +++ b/internal/page/attorney/register_test.go @@ -17,7 +17,7 @@ import ( func TestRegister(t *testing.T) { mux := http.NewServeMux() - Register(mux, nil, template.Templates{}, nil, nil, nil, nil, nil, nil, nil, nil) + Register(mux, nil, template.Templates{}, nil, nil, nil, nil, nil, nil, nil, nil, &mockDashboardStore{}) assert.Implements(t, (*http.Handler)(nil), mux) } diff --git a/internal/page/certificateprovider/mock_DashboardStore_test.go b/internal/page/certificateprovider/mock_DashboardStore_test.go new file mode 100644 index 0000000000..c7d1637649 --- /dev/null +++ b/internal/page/certificateprovider/mock_DashboardStore_test.go @@ -0,0 +1,101 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package certificateprovider + +import ( + context "context" + + actor "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + + mock "github.com/stretchr/testify/mock" + + page "github.com/ministryofjustice/opg-modernising-lpa/internal/page" +) + +// mockDashboardStore is an autogenerated mock type for the DashboardStore type +type mockDashboardStore struct { + mock.Mock +} + +// GetAll provides a mock function with given fields: ctx +func (_m *mockDashboardStore) GetAll(ctx context.Context) ([]page.LpaAndActorTasks, []page.LpaAndActorTasks, []page.LpaAndActorTasks, error) { + ret := _m.Called(ctx) + + var r0 []page.LpaAndActorTasks + var r1 []page.LpaAndActorTasks + var r2 []page.LpaAndActorTasks + var r3 error + if rf, ok := ret.Get(0).(func(context.Context) ([]page.LpaAndActorTasks, []page.LpaAndActorTasks, []page.LpaAndActorTasks, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []page.LpaAndActorTasks); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) []page.LpaAndActorTasks); ok { + r1 = rf(ctx) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(2).(func(context.Context) []page.LpaAndActorTasks); ok { + r2 = rf(ctx) + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(3).(func(context.Context) error); ok { + r3 = rf(ctx) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 +} + +// SubExistsForActorType provides a mock function with given fields: ctx, sub, actorType +func (_m *mockDashboardStore) SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) { + ret := _m.Called(ctx, sub, actorType) + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) (bool, error)); ok { + return rf(ctx, sub, actorType) + } + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) bool); ok { + r0 = rf(ctx, sub, actorType) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, actor.Type) error); ok { + r1 = rf(ctx, sub, actorType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTnewMockDashboardStore interface { + mock.TestingT + Cleanup(func()) +} + +// newMockDashboardStore creates a new instance of mockDashboardStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockDashboardStore(t mockConstructorTestingTnewMockDashboardStore) *mockDashboardStore { + mock := &mockDashboardStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/page/certificateprovider/register.go b/internal/page/certificateprovider/register.go index 489c837b4c..8595f53328 100644 --- a/internal/page/certificateprovider/register.go +++ b/internal/page/certificateprovider/register.go @@ -90,6 +90,12 @@ type Localizer interface { FormatDateTime(time.Time) string } +//go:generate mockery --testonly --inpackage --name DashboardStore --structname mockDashboardStore +type DashboardStore interface { + GetAll(ctx context.Context) (donor, attorney, certificateProvider []page.LpaAndActorTasks, err error) + SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) +} + func Register( rootMux *http.ServeMux, logger Logger, @@ -104,13 +110,14 @@ func Register( addressClient AddressClient, notifyClient NotifyClient, shareCodeSender ShareCodeSender, + dashboardStore DashboardStore, ) { handleRoot := makeHandle(rootMux, sessionStore, errorHandler) handleRoot(page.Paths.CertificateProvider.Login, page.Login(oneLoginClient, sessionStore, random.String, page.Paths.CertificateProvider.LoginCallback)) handleRoot(page.Paths.CertificateProvider.LoginCallback, - page.LoginCallback(oneLoginClient, sessionStore, page.Paths.CertificateProvider.EnterReferenceNumber)) + page.LoginCallback(oneLoginClient, sessionStore, page.Paths.CertificateProvider.EnterReferenceNumber, dashboardStore, actor.TypeCertificateProvider)) handleRoot(page.Paths.CertificateProvider.EnterReferenceNumber, EnterReferenceNumber(tmpls.Get("certificate_provider_enter_reference_number.gohtml"), shareCodeStore, sessionStore, certificateProviderStore)) diff --git a/internal/page/certificateprovider/register_test.go b/internal/page/certificateprovider/register_test.go index d1c5c3e042..b2ab20d185 100644 --- a/internal/page/certificateprovider/register_test.go +++ b/internal/page/certificateprovider/register_test.go @@ -20,7 +20,7 @@ import ( func TestRegister(t *testing.T) { mux := http.NewServeMux() - Register(mux, &log.Logger{}, template.Templates{}, nil, nil, &onelogin.Client{}, nil, nil, nil, nil, &place.Client{}, ¬ify.Client{}, nil) + Register(mux, &log.Logger{}, template.Templates{}, nil, nil, &onelogin.Client{}, nil, nil, nil, nil, &place.Client{}, ¬ify.Client{}, nil, &mockDashboardStore{}) assert.Implements(t, (*http.Handler)(nil), mux) } diff --git a/internal/page/dashboard.go b/internal/page/dashboard.go index 62e262bb82..96e68809e9 100644 --- a/internal/page/dashboard.go +++ b/internal/page/dashboard.go @@ -12,6 +12,7 @@ import ( //go:generate mockery --testonly --inpackage --name DashboardStore --structname mockDashboardStore type DashboardStore interface { GetAll(ctx context.Context) (donor, attorney, certificateProvider []LpaAndActorTasks, err error) + SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) } type LpaAndActorTasks struct { diff --git a/internal/page/donor/mock_DashboardStore_test.go b/internal/page/donor/mock_DashboardStore_test.go new file mode 100644 index 0000000000..ced8d4c0e0 --- /dev/null +++ b/internal/page/donor/mock_DashboardStore_test.go @@ -0,0 +1,101 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package donor + +import ( + context "context" + + actor "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + + mock "github.com/stretchr/testify/mock" + + page "github.com/ministryofjustice/opg-modernising-lpa/internal/page" +) + +// mockDashboardStore is an autogenerated mock type for the DashboardStore type +type mockDashboardStore struct { + mock.Mock +} + +// GetAll provides a mock function with given fields: ctx +func (_m *mockDashboardStore) GetAll(ctx context.Context) ([]page.LpaAndActorTasks, []page.LpaAndActorTasks, []page.LpaAndActorTasks, error) { + ret := _m.Called(ctx) + + var r0 []page.LpaAndActorTasks + var r1 []page.LpaAndActorTasks + var r2 []page.LpaAndActorTasks + var r3 error + if rf, ok := ret.Get(0).(func(context.Context) ([]page.LpaAndActorTasks, []page.LpaAndActorTasks, []page.LpaAndActorTasks, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []page.LpaAndActorTasks); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) []page.LpaAndActorTasks); ok { + r1 = rf(ctx) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(2).(func(context.Context) []page.LpaAndActorTasks); ok { + r2 = rf(ctx) + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).([]page.LpaAndActorTasks) + } + } + + if rf, ok := ret.Get(3).(func(context.Context) error); ok { + r3 = rf(ctx) + } else { + r3 = ret.Error(3) + } + + return r0, r1, r2, r3 +} + +// SubExistsForActorType provides a mock function with given fields: ctx, sub, actorType +func (_m *mockDashboardStore) SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) { + ret := _m.Called(ctx, sub, actorType) + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) (bool, error)); ok { + return rf(ctx, sub, actorType) + } + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) bool); ok { + r0 = rf(ctx, sub, actorType) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, actor.Type) error); ok { + r1 = rf(ctx, sub, actorType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTnewMockDashboardStore interface { + mock.TestingT + Cleanup(func()) +} + +// newMockDashboardStore creates a new instance of mockDashboardStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func newMockDashboardStore(t mockConstructorTestingTnewMockDashboardStore) *mockDashboardStore { + mock := &mockDashboardStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/page/donor/register.go b/internal/page/donor/register.go index 0f6969e73f..8321cecd22 100644 --- a/internal/page/donor/register.go +++ b/internal/page/donor/register.go @@ -155,6 +155,12 @@ type EventClient interface { SendReducedFeeRequested(context.Context, event.ReducedFeeRequested) error } +//go:generate mockery --testonly --inpackage --name DashboardStore --structname mockDashboardStore +type DashboardStore interface { + GetAll(ctx context.Context) (donor, attorney, certificateProvider []page.LpaAndActorTasks, err error) + SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) +} + func Register( rootMux *http.ServeMux, logger Logger, @@ -175,6 +181,7 @@ func Register( evidenceReceivedStore EvidenceReceivedStore, documentStore DocumentStore, eventClient EventClient, + dashboardStore DashboardStore, ) { payer := &payHelper{ logger: logger, @@ -189,7 +196,7 @@ func Register( handleRoot(page.Paths.Login, page.None, page.Login(oneLoginClient, sessionStore, random.String, page.Paths.LoginCallback)) handleRoot(page.Paths.LoginCallback, page.None, - page.LoginCallback(oneLoginClient, sessionStore, page.Paths.Dashboard)) + page.LoginCallback(oneLoginClient, sessionStore, page.Paths.Dashboard, dashboardStore, actor.TypeDonor)) lpaMux := http.NewServeMux() rootMux.Handle("/lpa/", page.RouteToPrefix("/lpa/", lpaMux, notFoundHandler)) diff --git a/internal/page/donor/register_test.go b/internal/page/donor/register_test.go index 390186ca39..f6cf91f584 100644 --- a/internal/page/donor/register_test.go +++ b/internal/page/donor/register_test.go @@ -23,7 +23,7 @@ import ( func TestRegister(t *testing.T) { mux := http.NewServeMux() - Register(mux, &log.Logger{}, template.Templates{}, nil, nil, &onelogin.Client{}, &place.Client{}, "http://example.org", &pay.Client{}, nil, &mockWitnessCodeSender{}, nil, nil, nil, nil, ¬ify.Client{}, nil, nil, nil) + Register(mux, &log.Logger{}, template.Templates{}, nil, nil, &onelogin.Client{}, &place.Client{}, "http://example.org", &pay.Client{}, nil, &mockWitnessCodeSender{}, nil, nil, nil, nil, ¬ify.Client{}, nil, nil, nil, &mockDashboardStore{}) assert.Implements(t, (*http.Handler)(nil), mux) } diff --git a/internal/page/fixtures/attorney.go b/internal/page/fixtures/attorney.go index 8d1d48d7dd..6b133d0f1b 100644 --- a/internal/page/fixtures/attorney.go +++ b/internal/page/fixtures/attorney.go @@ -3,6 +3,7 @@ package fixtures import ( "context" "encoding/base64" + "log" "net/http" "slices" "time" @@ -59,10 +60,15 @@ func Attorney( progress = slices.Index(progressValues, r.FormValue("progress")) email = r.FormValue("email") redirect = r.FormValue("redirect") + attorneySub = r.FormValue("attorneySub") ) + if attorneySub == "" { + attorneySub = random.String(16) + } + if r.Method != http.MethodPost && !r.URL.Query().Has("redirect") { - return tmpl(w, &fixturesData{App: appData}) + return tmpl(w, &fixturesData{App: appData, Sub: attorneySub}) } if lpaType == "hw" && isTrustCorporation { @@ -71,7 +77,6 @@ func Attorney( var ( donorSub = random.String(16) - attorneySub = random.String(16) certificateProviderSub = random.String(16) donorSessionID = base64.StdEncoding.EncodeToString([]byte(donorSub)) certificateProviderSessionID = base64.StdEncoding.EncodeToString([]byte(certificateProviderSub)) @@ -288,6 +293,8 @@ func Attorney( redirect = "/attorney/" + donor.LpaID + redirect } + log.Println("Logging in with sub", attorneySub) + http.Redirect(w, r, redirect, http.StatusFound) return nil } diff --git a/internal/page/fixtures/certificate_provider.go b/internal/page/fixtures/certificate_provider.go index 2f7592dc12..f4b4567a6b 100644 --- a/internal/page/fixtures/certificate_provider.go +++ b/internal/page/fixtures/certificate_provider.go @@ -2,6 +2,7 @@ package fixtures import ( "encoding/base64" + "log" "net/http" "slices" "time" @@ -17,6 +18,12 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/sesh" ) +type lpaLink struct { + PK string + SK string + ActorType actor.Type +} + func CertificateProvider( tmpl template.Template, sessionStore sesh.Store, @@ -38,15 +45,19 @@ func CertificateProvider( email = r.FormValue("email") redirect = r.FormValue("redirect") asProfessionalCertificateProvider = r.FormValue("relationship") == "professional" + certificateProviderSub = r.FormValue("certificateProviderSub") ) + if certificateProviderSub == "" { + certificateProviderSub = random.String(16) + } + if r.Method != http.MethodPost && !r.URL.Query().Has("redirect") { - return tmpl(w, &fixturesData{App: appData}) + return tmpl(w, &fixturesData{App: appData, Sub: certificateProviderSub}) } var ( donorSub = random.String(16) - certificateProviderSub = random.String(16) donorSessionID = base64.StdEncoding.EncodeToString([]byte(donorSub)) certificateProviderSessionID = base64.StdEncoding.EncodeToString([]byte(certificateProviderSub)) ) @@ -169,6 +180,8 @@ func CertificateProvider( redirect = "/certificate-provider/" + donor.LpaID + redirect } + log.Println("Logging in with sub", certificateProviderSub) + http.Redirect(w, r, redirect, http.StatusFound) return nil } diff --git a/internal/page/fixtures/donor.go b/internal/page/fixtures/donor.go index 4d39ab3318..d0ea1bc85a 100644 --- a/internal/page/fixtures/donor.go +++ b/internal/page/fixtures/donor.go @@ -3,6 +3,7 @@ package fixtures import ( "context" "encoding/base64" + "log" "net/http" "slices" "time" @@ -21,6 +22,11 @@ import ( "github.com/ministryofjustice/opg-modernising-lpa/internal/uid" ) +type DynamoClient interface { + OneByUID(ctx context.Context, uid string, v interface{}) error + AllByPartialSk(ctx context.Context, pk, partialSk string, v interface{}) error +} + type DocumentStore interface { GetAll(context.Context) (page.Documents, error) Put(context.Context, page.Document) error @@ -71,16 +77,18 @@ func Donor( useRealUID = r.FormValue("uid") == "real" certificateProviderEmail = r.FormValue("certificateProviderEmail") certificateProviderMobile = r.FormValue("certificateProviderMobile") + donorSub = r.FormValue("donorSub") ) + if donorSub == "" { + donorSub = random.String(16) + } + if r.Method != http.MethodPost && !r.URL.Query().Has("redirect") { - return tmpl(w, &fixturesData{App: appData}) + return tmpl(w, &fixturesData{App: appData, Sub: donorSub}) } - var ( - donorSub = random.String(16) - donorSessionID = base64.StdEncoding.EncodeToString([]byte(donorSub)) - ) + donorSessionID := base64.StdEncoding.EncodeToString([]byte(donorSub)) if err := sesh.SetLoginSession(sessionStore, r, w, &sesh.LoginSession{Sub: donorSub, Email: testEmail}); err != nil { return err @@ -389,6 +397,7 @@ func Donor( redirect = "/lpa/" + donorDetails.LpaID + redirect } + log.Println("Logging in with sub", donorSub) random.UseTestCode = true http.Redirect(w, r, redirect, http.StatusFound) return nil diff --git a/internal/page/fixtures/fixtures.go b/internal/page/fixtures/fixtures.go index 39e38c7842..8196f0afc7 100644 --- a/internal/page/fixtures/fixtures.go +++ b/internal/page/fixtures/fixtures.go @@ -26,6 +26,7 @@ const ( type fixturesData struct { App page.AppData + Sub string Errors validation.List } diff --git a/internal/page/login_callback.go b/internal/page/login_callback.go index e31e8cf89a..1ad0a4c065 100644 --- a/internal/page/login_callback.go +++ b/internal/page/login_callback.go @@ -2,8 +2,10 @@ package page import ( "context" + "encoding/base64" "net/http" + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" "github.com/ministryofjustice/opg-modernising-lpa/internal/onelogin" "github.com/ministryofjustice/opg-modernising-lpa/internal/sesh" ) @@ -13,7 +15,7 @@ type LoginCallbackOneLoginClient interface { UserInfo(ctx context.Context, accessToken string) (onelogin.UserInfo, error) } -func LoginCallback(oneLoginClient LoginCallbackOneLoginClient, sessionStore sesh.Store, redirect Path) Handler { +func LoginCallback(oneLoginClient LoginCallbackOneLoginClient, sessionStore sesh.Store, redirect Path, dashboardStore DashboardStore, actorType actor.Type) Handler { return func(appData AppData, w http.ResponseWriter, r *http.Request) error { oneLoginSession, err := sesh.OneLogin(sessionStore, r) if err != nil { @@ -38,6 +40,22 @@ func LoginCallback(oneLoginClient LoginCallbackOneLoginClient, sessionStore sesh return err } + if actorType != actor.TypeDonor { + exists, err := dashboardStore.SubExistsForActorType( + r.Context(), + base64.StdEncoding.EncodeToString([]byte(userInfo.Sub)), + actorType, + ) + + if err != nil { + return err + } + + if exists { + redirect = Paths.Dashboard + } + } + return appData.Redirect(w, r, redirect.Format()) } } diff --git a/internal/page/login_callback_test.go b/internal/page/login_callback_test.go index 94ed6712d7..29c6b93361 100644 --- a/internal/page/login_callback_test.go +++ b/internal/page/login_callback_test.go @@ -1,11 +1,13 @@ package page import ( + "encoding/base64" "net/http" "net/http/httptest" "testing" "github.com/gorilla/sessions" + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" "github.com/ministryofjustice/opg-modernising-lpa/internal/onelogin" "github.com/ministryofjustice/opg-modernising-lpa/internal/sesh" "github.com/stretchr/testify/assert" @@ -13,57 +15,74 @@ import ( ) func TestLoginCallback(t *testing.T) { - w := httptest.NewRecorder() - r, _ := http.NewRequest(http.MethodGet, "/?code=auth-code&state=my-state", nil) + testCases := map[string]struct { + subExists bool + expectedRedirect Path + }{ + "Sub exists": {subExists: true, expectedRedirect: Paths.Dashboard}, + "Sub does not exist": {subExists: false, expectedRedirect: Paths.Attorney.EnterReferenceNumber}, + } - client := newMockOneLoginClient(t) - client. - On("Exchange", r.Context(), "auth-code", "my-nonce"). - Return("id-token", "a JWT", nil) - client. - On("UserInfo", r.Context(), "a JWT"). - Return(onelogin.UserInfo{Sub: "random", Email: "name@example.com"}, nil) + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/?code=auth-code&state=my-state", nil) - sessionStore := newMockSessionStore(t) + client := newMockOneLoginClient(t) + client. + On("Exchange", r.Context(), "auth-code", "my-nonce"). + Return("id-token", "a JWT", nil) + client. + On("UserInfo", r.Context(), "a JWT"). + Return(onelogin.UserInfo{Sub: "random", Email: "name@example.com"}, nil) - session := sessions.NewSession(sessionStore, "session") - session.Options = &sessions.Options{ - Path: "/", - MaxAge: 86400, - SameSite: http.SameSiteLaxMode, - HttpOnly: true, - Secure: true, - } - session.Values = map[any]any{ - "session": &sesh.LoginSession{ - IDToken: "id-token", - Sub: "random", - Email: "name@example.com", - }, - } + sessionStore := newMockSessionStore(t) - sessionStore. - On("Get", r, "params"). - Return(&sessions.Session{ - Values: map[any]any{ - "one-login": &sesh.OneLoginSession{ - State: "my-state", - Nonce: "my-nonce", - Locale: "en", - Redirect: "/redirect", + session := sessions.NewSession(sessionStore, "session") + session.Options = &sessions.Options{ + Path: "/", + MaxAge: 86400, + SameSite: http.SameSiteLaxMode, + HttpOnly: true, + Secure: true, + } + session.Values = map[any]any{ + "session": &sesh.LoginSession{ + IDToken: "id-token", + Sub: "random", + Email: "name@example.com", }, - }, - }, nil) - sessionStore. - On("Save", r, w, session). - Return(nil) + } + + sessionStore. + On("Get", r, "params"). + Return(&sessions.Session{ + Values: map[any]any{ + "one-login": &sesh.OneLoginSession{ + State: "my-state", + Nonce: "my-nonce", + Locale: "en", + Redirect: "/redirect", + }, + }, + }, nil) + sessionStore. + On("Save", r, w, session). + Return(nil) - err := LoginCallback(client, sessionStore, Paths.Attorney.EnterReferenceNumber)(AppData{}, w, r) - assert.Nil(t, err) - resp := w.Result() + dashboardStore := newMockDashboardStore(t) + dashboardStore. + On("SubExistsForActorType", r.Context(), base64.StdEncoding.EncodeToString([]byte("random")), actor.TypeAttorney). + Return(tc.subExists, nil) - assert.Equal(t, http.StatusFound, resp.StatusCode) - assert.Equal(t, Paths.Attorney.EnterReferenceNumber.Format(), resp.Header.Get("Location")) + err := LoginCallback(client, sessionStore, Paths.Attorney.EnterReferenceNumber, dashboardStore, actor.TypeAttorney)(AppData{}, w, r) + assert.Nil(t, err) + resp := w.Result() + + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, tc.expectedRedirect.Format(), resp.Header.Get("Location")) + }) + } } func TestLoginCallbackSessionMissing(t *testing.T) { @@ -114,7 +133,7 @@ func TestLoginCallbackSessionMissing(t *testing.T) { On("Get", r, "params"). Return(tc.session, tc.getErr) - err := LoginCallback(nil, sessionStore, Paths.Attorney.LoginCallback)(AppData{}, w, r) + err := LoginCallback(nil, sessionStore, Paths.Attorney.LoginCallback, nil, actor.TypeAttorney)(AppData{}, w, r) assert.Equal(t, tc.expectedErr, err) }) } @@ -138,7 +157,7 @@ func TestLoginCallbackWhenExchangeErrors(t *testing.T) { }, }, nil) - err := LoginCallback(client, sessionStore, Paths.LoginCallback)(AppData{}, w, r) + err := LoginCallback(client, sessionStore, Paths.LoginCallback, nil, actor.TypeAttorney)(AppData{}, w, r) assert.Equal(t, expectedError, err) } @@ -163,7 +182,7 @@ func TestLoginCallbackWhenUserInfoError(t *testing.T) { }, }, nil) - err := LoginCallback(client, sessionStore, Paths.LoginCallback)(AppData{}, w, r) + err := LoginCallback(client, sessionStore, Paths.LoginCallback, nil, actor.TypeAttorney)(AppData{}, w, r) assert.Equal(t, expectedError, err) } @@ -196,6 +215,44 @@ func TestLoginCallbackWhenSessionError(t *testing.T) { On("Save", r, w, mock.Anything). Return(expectedError) - err := LoginCallback(client, sessionStore, Paths.LoginCallback)(AppData{}, w, r) + err := LoginCallback(client, sessionStore, Paths.LoginCallback, nil, actor.TypeAttorney)(AppData{}, w, r) + assert.Equal(t, expectedError, err) +} + +func TestLoginCallbackWhenDashboardStoreError(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/?code=auth-code&state=my-state", nil) + + client := newMockOneLoginClient(t) + client. + On("Exchange", r.Context(), "auth-code", "my-nonce"). + Return("id-token", "a JWT", nil) + client. + On("UserInfo", r.Context(), "a JWT"). + Return(onelogin.UserInfo{Sub: "random", Email: "name@example.com"}, nil) + + sessionStore := newMockSessionStore(t) + sessionStore. + On("Get", r, "params"). + Return(&sessions.Session{ + Values: map[any]any{ + "one-login": &sesh.OneLoginSession{ + State: "my-state", + Nonce: "my-nonce", + Locale: "en", + Redirect: Paths.LoginCallback.Format(), + }, + }, + }, nil) + sessionStore. + On("Save", r, w, mock.Anything). + Return(nil) + + dashboardStore := newMockDashboardStore(t) + dashboardStore. + On("SubExistsForActorType", r.Context(), mock.Anything, mock.Anything). + Return(false, expectedError) + + err := LoginCallback(client, sessionStore, Paths.LoginCallback, dashboardStore, actor.TypeAttorney)(AppData{}, w, r) assert.Equal(t, expectedError, err) } diff --git a/internal/page/mock_DashboardStore_test.go b/internal/page/mock_DashboardStore_test.go index 4f114d1dd1..7c2b326ef6 100644 --- a/internal/page/mock_DashboardStore_test.go +++ b/internal/page/mock_DashboardStore_test.go @@ -5,6 +5,8 @@ package page import ( context "context" + actor "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + mock "github.com/stretchr/testify/mock" ) @@ -57,6 +59,30 @@ func (_m *mockDashboardStore) GetAll(ctx context.Context) ([]LpaAndActorTasks, [ return r0, r1, r2, r3 } +// SubExistsForActorType provides a mock function with given fields: ctx, sub, actorType +func (_m *mockDashboardStore) SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) { + ret := _m.Called(ctx, sub, actorType) + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) (bool, error)); ok { + return rf(ctx, sub, actorType) + } + if rf, ok := ret.Get(0).(func(context.Context, string, actor.Type) bool); ok { + r0 = rf(ctx, sub, actorType) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, actor.Type) error); ok { + r1 = rf(ctx, sub, actorType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + type mockConstructorTestingTnewMockDashboardStore interface { mock.TestingT Cleanup(func()) diff --git a/internal/page/paths.go b/internal/page/paths.go index e91485fdd9..d31f4944d5 100644 --- a/internal/page/paths.go +++ b/internal/page/paths.go @@ -160,6 +160,7 @@ type AppPaths struct { CookiesConsent Path Dashboard Path DashboardFixtures Path + DonorSubByLpaUID Path Fixtures Path Login Path LoginCallback Path diff --git a/internal/page/share_code.go b/internal/page/share_code.go index 616bd7a8a3..0f14601ab4 100644 --- a/internal/page/share_code.go +++ b/internal/page/share_code.go @@ -105,7 +105,7 @@ func (s *ShareCodeSender) sendOriginalAttorney(ctx context.Context, appData AppD return nil } - return s.sendAttorney(ctx, appData, attorney.Email, + return s.sendAttorney(ctx, attorney.Email, notify.InitialOriginalAttorneyEmail{ AttorneyFullName: attorney.FullName(), DonorFirstNames: donor.Donor.FirstNames, @@ -126,7 +126,7 @@ func (s *ShareCodeSender) sendReplacementAttorney(ctx context.Context, appData A return nil } - return s.sendAttorney(ctx, appData, attorney.Email, + return s.sendAttorney(ctx, attorney.Email, notify.InitialReplacementAttorneyEmail{ AttorneyFullName: attorney.FullName(), DonorFirstNames: donor.Donor.FirstNames, @@ -147,7 +147,7 @@ func (s *ShareCodeSender) sendTrustCorporation(ctx context.Context, appData AppD return nil } - return s.sendAttorney(ctx, appData, trustCorporation.Email, + return s.sendAttorney(ctx, trustCorporation.Email, notify.InitialOriginalAttorneyEmail{ AttorneyFullName: trustCorporation.Name, DonorFirstNames: donor.Donor.FirstNames, @@ -168,7 +168,7 @@ func (s *ShareCodeSender) sendReplacementTrustCorporation(ctx context.Context, a return nil } - return s.sendAttorney(ctx, appData, trustCorporation.Email, + return s.sendAttorney(ctx, trustCorporation.Email, notify.InitialReplacementAttorneyEmail{ AttorneyFullName: trustCorporation.Name, DonorFirstNames: donor.Donor.FirstNames, @@ -185,7 +185,7 @@ func (s *ShareCodeSender) sendReplacementTrustCorporation(ctx context.Context, a }) } -func (s *ShareCodeSender) sendAttorney(ctx context.Context, appData AppData, to string, email shareCodeEmail, shareCodeData actor.ShareCodeData) error { +func (s *ShareCodeSender) sendAttorney(ctx context.Context, to string, email shareCodeEmail, shareCodeData actor.ShareCodeData) error { shareCode := s.randomString(12) if s.useTestCode { shareCode = "abcdef123456" diff --git a/lang/cy.json b/lang/cy.json index f10da8d0a0..de2eab0d35 100644 --- a/lang/cy.json +++ b/lang/cy.json @@ -975,5 +975,7 @@ "en": "Saesneg", "cy": "Cymraeg", "whichLanguageYoudLikeUsToUseWhenWeContactYou": "Welsh", - "preferredContactLanguage": "Welsh" + "preferredContactLanguage": "Welsh", + "addAnotherLpaIfInvited": "Welsh {{ .ActorType }}.", + "addAnLpa": "Welsh" } diff --git a/lang/en.json b/lang/en.json index 86847de8e2..1b751ed91e 100644 --- a/lang/en.json +++ b/lang/en.json @@ -922,5 +922,7 @@ "en": "English", "cy": "Welsh", "whichLanguageYoudLikeUsToUseWhenWeContactYou": "which language you’d like us to use when we contact you", - "preferredContactLanguage": "Preferred contact language" + "preferredContactLanguage": "Preferred contact language", + "addAnotherLpaIfInvited": "Add another LPA if you have been sent an email inviting you to be a {{ .ActorType }}.", + "addAnLpa": "Add an LPA" } diff --git a/web/template/attorney_fixtures.gohtml b/web/template/attorney_fixtures.gohtml index a444d998b5..f00f1042e4 100644 --- a/web/template/attorney_fixtures.gohtml +++ b/web/template/attorney_fixtures.gohtml @@ -6,6 +6,11 @@

Entering an email will cause a reference code to be sent and redirect you to the start page. Leave it blank to be signed in and taken to the dashboard instead.

{{ template "input" (input . "email" "email" "" "classes" "govuk-input--width-20" "type" "email" "spellcheck" "false" "autocomplete" "email") }} + {{ template "input" (input . "attorneySub" "Attorney OneLogin sub" .Sub "classes" "govuk-input--width-20") }} +
+ Copy this value or change to your own to log back in to an existing LPA from the start page +
+
As diff --git a/web/template/certificate_provider_fixtures.gohtml b/web/template/certificate_provider_fixtures.gohtml index bf5110e62c..5f53931554 100644 --- a/web/template/certificate_provider_fixtures.gohtml +++ b/web/template/certificate_provider_fixtures.gohtml @@ -7,6 +7,11 @@ {{ template "input" (input . "email" "email" "" "classes" "govuk-input--width-20" "type" "email" "spellcheck" "false" "autocomplete" "email") }} + {{ template "input" (input . "certificateProviderSub" "Certificate provider OneLogin sub" .Sub "classes" "govuk-input--width-20") }} +
+ Copy this value or change to your own to log back in to an existing LPA from the start page +
+
From diff --git a/web/template/dashboard.gohtml b/web/template/dashboard.gohtml index 216576fd9f..7019288ca4 100644 --- a/web/template/dashboard.gohtml +++ b/web/template/dashboard.gohtml @@ -203,10 +203,10 @@ {{ trHtml .App "registeredLpasContent" }} {{ template "details" (details . "whatIsTheUseService" "whatIsTheUseServiceDetails" false) }} - + {{ range .DonorLpas }} {{ if not .Donor.RegisteredAt.IsZero }} - {{ template "donorCard" (card $.App .) }} + {{ template "donorCard" (card $.App .) }} {{ end }} {{ end }} {{ end }} @@ -216,6 +216,13 @@ {{ if .AttorneyLpas }} {{ if .UseTabs }}
{{ end }}

{{ tr .App "imAnAttorney" }}

+ +
+ {{ $attorney := tr .App .App.ActorTypes.Attorney.String }} +

{{ trFormat .App "addAnotherLpaIfInvited" "ActorType" ( lowerFirst $attorney) }}

+ {{ tr .App "addAnLpa" }} +
+

{{ tr .App "viewAndManageAttorney" }}

{{ $hasRegistered := false }} {{ range .AttorneyLpas }} @@ -232,19 +239,26 @@ {{ trHtml .App "registeredLpasContentAttorney" }} {{ template "details" (details . "whatIsTheUseService" "whatIsTheUseServiceDetails" false) }} - + {{ range .AttorneyLpas }} {{ if not .Donor.RegisteredAt.IsZero }} - {{ template "donorCard" (card $.App .) }} + {{ template "donorCard" (card $.App .) }} {{ end }} {{ end }} - {{ end }} + {{ end }} {{ if .UseTabs }}
{{ end }} {{ end }} {{ if .CertificateProviderLpas }} {{ if .UseTabs }}
{{ end }}

{{ tr .App "imACertificateProvider" }}

+ +
+ {{ $certificateProvider := tr .App .App.ActorTypes.CertificateProvider.String }} +

{{ trFormat .App "addAnotherLpaIfInvited" "ActorType" ( lowerFirst $certificateProvider) }}

+ {{ tr .App "addAnLpa" }} +
+

{{ tr .App "viewAndManageCertificateProvider" }}

{{ range .CertificateProviderLpas }} {{ template "certificateProviderCard" (card $.App .) }} diff --git a/web/template/fixtures.gohtml b/web/template/fixtures.gohtml index b1595a5999..e77f307f87 100644 --- a/web/template/fixtures.gohtml +++ b/web/template/fixtures.gohtml @@ -35,6 +35,11 @@
+ {{ template "input" (input . "donorSub" "Donor OneLogin sub" .Sub "classes" "govuk-input--width-20") }} +
+ Copy this value or change to your own to log back in to an existing LPA from the start page +
+ {{ template "input" (input . "certificateProviderEmail" "Certificate provider email" "" "classes" "govuk-input--width-20" "type" "email" "spellcheck" "false" "autocomplete" "email") }} {{ template "input" (input . "certificateProviderMobile" "Certificate provider mobile" "" "classes" "govuk-input--width-20" "type" "tel" "spellcheck" "false" "autocomplete" "tel") }}