diff --git a/.env.example b/.env.example index 5bb66c3715..54faff4528 100644 --- a/.env.example +++ b/.env.example @@ -31,6 +31,7 @@ AWS_SECRET_KEY=👻 FILE_API_KEY=👻 FILE_API_KEY_NEXUS=👻 FILE_API_KEY_BARNET=👻 +FILE_API_KEY_SOUTHWARK=👻 # Editor EDITOR_URL_EXT=http://localhost:3000 diff --git a/api.planx.uk/.env.test.example b/api.planx.uk/.env.test.example index 947deafa0b..e728faab1f 100644 --- a/api.planx.uk/.env.test.example +++ b/api.planx.uk/.env.test.example @@ -20,6 +20,7 @@ AWS_SECRET_KEY=👻 FILE_API_KEY=👻 FILE_API_KEY_NEXUS=👻 FILE_API_KEY_BARNET=👻 +FILE_API_KEY_SOUTHWARK=👻 # Editor EDITOR_URL_EXT=example.com diff --git a/api.planx.uk/modules/auth/middleware.ts b/api.planx.uk/modules/auth/middleware.ts index b280b2b135..b5e66c9e39 100644 --- a/api.planx.uk/modules/auth/middleware.ts +++ b/api.planx.uk/modules/auth/middleware.ts @@ -87,7 +87,14 @@ export const useFilePermission: RequestHandler = (req, _res, next): void => { req.headers["api-key"] as string, process.env.FILE_API_KEY_NEXUS!, ) || - isEqual(req.headers["api-key"] as string, process.env.FILE_API_KEY_BARNET!); + isEqual( + req.headers["api-key"] as string, + process.env.FILE_API_KEY_BARNET!, + ) || + isEqual( + req.headers["api-key"] as string, + process.env.FILE_API_KEY_SOUTHWARK!, + ); if (!isAuthenticated) return next({ status: 401, message: "Unauthorised" }); return next(); }; diff --git a/doc/how-to/how-to-setup-aws-s3-submissions.md b/doc/how-to/how-to-setup-aws-s3-submissions.md new file mode 100644 index 0000000000..998f963a72 --- /dev/null +++ b/doc/how-to/how-to-setup-aws-s3-submissions.md @@ -0,0 +1,26 @@ +# How to setup AWS S3 submissions + +## Context + +When building a submission service, the "Send" component offers an option "Upload to AWS S3" which uploads the application data payload to PlanX's private S3 Bucket - which then in turn allows a council to receive a notification via a Power Automate Webhook and run a script to securely retrieve the application JSON file, download any associated user-uploaded files, generate custom documents, and save the application to their local Microsoft SharePoint environment. + +There's full documentation on how this method works in the PlanX Service Specification on Notion [here](https://opensystemslab.notion.site/How-you-can-receive-process-PlanX-applications-using-Microsoft-365-tools-like-Power-Automate-13197a4bbd24421eaf7b5021ddd07741?pvs=4). + +Once a council has confirmed they're cleared from their IT dept to use this method (eg allowed to receive external webhook requests) & shared a Power Automate Webhook URL, there's a few steps we need to take: + +1. Add the council-provided URL to `team_integrations.power_automate_webhook_url` as plain text via the production Hasura console + +2. Create 2x tokens for sending secure requests to the Power Automate webhook and add both encrypted values to: + - `team_integrations.production_power_automate_api_key` & `team_integrations.staging_power_automate_api_key` via the production Hasura console + - See `how-to-generate-a-secret` for how to properly generate tokens and encrypt values + +3. Create 2x tokens for downloading files from the PlanX S3 Bucket and add values to: + - Root `.env.example` & `.env`, API's `.env.test` & `.env.test.example` as `FILE_API_KEY_{TEAM_SLUG}` + - In addition to committing these changes, also manually sync to AWS `pizza-secrets` via `scripts/push-secrets.sh` + - Root `docker-compose.yml` + - API's `modules/auth/middleware.ts` function `isAuthenticated` + - Pulumi's `infrastructure/application/index.ts` list of `apiService` "environment" variables + - Run `pulumi config set file-api-key-{team_slug} --stack {stack}` 2x for each staging & production stacks + - Encrypt the values using _our_ encrypt scripts (again see `how-to-generate-a-secret`) and add to `team_integrations.production_file_api_key` & `team_integrations.staging_file_api_key` via the production Hasura console. Please note these values are _not_ currently read, but suitable for a potential future refactor (just a bit tricky because file API keys are issued to a mix of _teams_ and _systems_ (eg BOPS & Idox)). + +4. Securely share tokens back to council contact via onetimesecret or similar diff --git a/docker-compose.yml b/docker-compose.yml index 3a8af48c12..ea9c787219 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -126,6 +126,7 @@ services: EDITOR_URL_EXT: ${EDITOR_URL_EXT} ENCRYPTION_KEY: ${ENCRYPTION_KEY} FILE_API_KEY_BARNET: ${FILE_API_KEY_BARNET} + FILE_API_KEY_SOUTHWARK: ${FILE_API_KEY_SOUTHWARK} FILE_API_KEY_NEXUS: ${FILE_API_KEY_NEXUS} FILE_API_KEY: ${FILE_API_KEY} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} diff --git a/editor.planx.uk/src/@planx/components/Send/Editor.tsx b/editor.planx.uk/src/@planx/components/Send/Editor.tsx index 6cad4a9328..37d7e894a9 100644 --- a/editor.planx.uk/src/@planx/components/Send/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/Send/Editor.tsx @@ -113,10 +113,10 @@ const SendComponent: React.FC = (props) => { if ( value === "s3" && newCheckedValues.includes(value) && - !["barnet", "lambeth"].includes(teamSlug) + !["barnet", "southwark"].includes(teamSlug) ) { alert( - "AWS S3 uploads are currently being prototyped with Barnet and Lambeth only. Please do not select this option for other councils yet.", + "AWS S3 uploads require API tokens and are currently being prototyped with select councils. Reach out on Slack #planx-alternatives-to-integrations before selecting this option please.", ); } }; diff --git a/infrastructure/application/Pulumi.production.yaml b/infrastructure/application/Pulumi.production.yaml index 23f8804851..cef9dd5308 100644 --- a/infrastructure/application/Pulumi.production.yaml +++ b/infrastructure/application/Pulumi.production.yaml @@ -12,6 +12,8 @@ config: secure: AAABANMl+fVFsRVGXvJV/aLManXO+TldXVDhp5QH6KGWJoG7O9Ket63zIW1iOiinINWJ2I5OizI= application:file-api-key-nexus: secure: AAABAB2cv4GAf8RqN1hHbRbO68p8o4kLJYWsip9BoPdobrNtQB787M3s+gJnKKl9DfyXRHOXHGc= + application:file-api-key-southwark: + secure: AAABAL5G2cNl6XIQA0vcP6El4Us7Vk8Cz9JViRon25crc8MC0ix4ox2mE+XawsxYbLRwGfaRIJo= application:google-client-id: 987324067365-vpsk3kgeq5n32ihjn760ihf8l7m5rhh8.apps.googleusercontent.com application:google-client-secret: secure: AAABAN5E+De3A3HtpLVaSNTDwk9Uz4r2d5g8SIRVbNOd2fj3eU+lGJXjVbEAnxezr14hwabbfwW2ptjcFzqkhG7OmQ== diff --git a/infrastructure/application/Pulumi.staging.yaml b/infrastructure/application/Pulumi.staging.yaml index 9ffd625e37..a037794324 100644 --- a/infrastructure/application/Pulumi.staging.yaml +++ b/infrastructure/application/Pulumi.staging.yaml @@ -13,6 +13,8 @@ config: secure: AAABAFpZq81zy3CKFXUgi9oEGIGp7LDVD3TNlYkZD4liX0bxOrmMJYdDpMmyGt4aGARF63nEUmo= application:file-api-key-nexus: secure: AAABAJFgaBoTWNmZyXDkGRngwU8KpOt6CeBLxGBgBG0JFMsKK7rWT39TsjJ9pL1wZaBoT0YZhCg= + application:file-api-key-southwark: + secure: AAABAK8LsYKKNgIS4fepW5Sh6+WKxNopxsos51eBttT7O8E8K0HYOswgrIWuYJ0R1eJHDLKRHqQ= application:google-client-id: 987324067365-vpsk3kgeq5n32ihjn760ihf8l7m5rhh8.apps.googleusercontent.com application:google-client-secret: secure: AAABAGQuqQDU4S+vR+cQaFoa6xAeWU9clVaNonQ/dq0R8Dke+o0y7ALOmYMy4fOX4Pa6HiZl85npU/cbwy8HdMYaiA== diff --git a/infrastructure/application/index.ts b/infrastructure/application/index.ts index a9bed3ad7c..8fde151cd4 100644 --- a/infrastructure/application/index.ts +++ b/infrastructure/application/index.ts @@ -348,6 +348,10 @@ export = async () => { name: "FILE_API_KEY_BARNET", value: config.requireSecret("file-api-key-barnet"), }, + { + name: "FILE_API_KEY_SOUTHWARK", + value: config.requireSecret("file-api-key-southwark"), + }, { name: "GOOGLE_CLIENT_ID", value: config.require("google-client-id"),