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
+`)
+ 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
+
+