Skip to content

Commit

Permalink
IMN-610 - BFF Privacy Notice (#827)
Browse files Browse the repository at this point in the history
Co-authored-by: paologaleotti <[email protected]>
Co-authored-by: Vittorio Caprio <[email protected]>
  • Loading branch information
3 people authored Sep 13, 2024
1 parent 3ed59c7 commit 53c4780
Show file tree
Hide file tree
Showing 28 changed files with 2,210 additions and 1,869 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ packages/*/src/generated
# MinIO container data
docker/minio-data

# DynamoDB container data
docker/dynamo-db-data

### Turbo ###
# Turborepo task cache
.turbo
Expand Down
48 changes: 43 additions & 5 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ services:
- 9000:9000
- 9001:9001
environment:
MINIO_ROOT_USER: test-aws-key # use this as AWS S3 access key ID
MINIO_ROOT_PASSWORD: test-aws-secret # use this as AWS S3 secret access key
MINIO_ROOT_USER: testawskey # use this as AWS S3 access key ID
MINIO_ROOT_PASSWORD: testawssecret # use this as AWS S3 secret access key
MINIO_SITE_REGION: eu-central-1
volumes:
- ./minio-data:/data
Expand All @@ -127,7 +127,7 @@ services:
- minio
entrypoint: >
/bin/sh -c "
mc alias set minio http://minio:9000 test-aws-key test-aws-secret;
mc alias set minio http://minio:9000 testawskey testawssecret;
mc ready minio;
echo 'MinIO is ready. Seeding data...';
mc cp --recursive data/ minio/interop-local-bucket/;
Expand Down Expand Up @@ -177,8 +177,46 @@ services:
- 8005:8005
command: "/bin/bash -c 'npm install -g aws-ses-v2-local; aws-ses-v2-local --port=8005 --host=0.0.0.0'"

# dynamodb-local is a local DynamoDB-compatible database, to replace AWS DynamoDB in local development
dynamodb-local:
image: amazon/dynamodb-local:2.5.2
ports:
- 7001:8000
volumes:
- ./dynamo-db-data:/data
working_dir: /home/dynamodblocal
command: ["-jar", "DynamoDBLocal.jar", "-inMemory", "-sharedDb"]

# migration for dynamodb with privacy-notice table
migration-dynamodb:
image: amazon/aws-cli:2.17.44
container_name: migration-dynamodb
working_dir: /home/dynamodblocal
volumes:
- ./dynamo-db:/home/dynamodblocal
depends_on:
- dynamodb-local
environment:
- AWS_ACCESS_KEY_ID=local
- AWS_SECRET_ACCESS_KEY=local
- AWS_REGION=eu-west-1
- TABLES=privacy-notice,privacy-notice-acceptances
entrypoint: ./dynamo-migrations.sh

# GUI for DynamoDB Local
dynamodb-console:
image: aaronshaf/dynamodb-admin:4.6.1
container_name: dynamodb-admin
depends_on:
- dynamodb-local
restart: always
ports:
- "8001:8001"
environment:
- DYNAMO_ENDPOINT=http://dynamodb-local:8000
- AWS_REGION=eu-west-1
# Redis local cache database used by the RateLimiter
redis:
image: 'redis:7.2.5-alpine3.20'
image: "redis:7.2.5-alpine3.20"
ports:
- '6379:6379'
- "6379:6379"
12 changes: 12 additions & 0 deletions docker/dynamo-db/dynamo-migrations.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

IFS=',' read -ra TABLES <<< "$TABLES"
for TABLE_NAME in "${TABLES[@]}"; do
echo "Checking if table $TABLE_NAME exists..."
TABLE_EXISTS=$(aws dynamodb list-tables --endpoint-url http://dynamodb-local:8000 | grep -w ${TABLE_NAME})
if [ -z "$TABLE_EXISTS" ]; then
echo "Table $TABLE_NAME does not exist. Creating table..."
aws dynamodb create-table --cli-input-json file://./schema/$TABLE_NAME-dynamo-db.json --billing-mode PAY_PER_REQUEST --endpoint-url http://dynamodb-local:8000
aws dynamodb batch-write-item --request-items file://./seed/$TABLE_NAME-seed.json --endpoint-url http://dynamodb-local:8000
fi
done
11 changes: 11 additions & 0 deletions docker/dynamo-db/schema/privacy-notice-acceptances-dynamo-db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"TableName": "privacy-notice-acceptances",
"KeySchema": [{ "AttributeName": "pnIdWithUserId", "KeyType": "HASH" }],
"AttributeDefinitions": [
{ "AttributeName": "pnIdWithUserId", "AttributeType": "S" }
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 5
}
}
11 changes: 11 additions & 0 deletions docker/dynamo-db/schema/privacy-notice-dynamo-db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"TableName": "privacy-notice",
"KeySchema": [{ "AttributeName": "privacyNoticeId", "KeyType": "HASH" }],
"AttributeDefinitions": [
{ "AttributeName": "privacyNoticeId", "AttributeType": "S" }
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 5
}
}
42 changes: 42 additions & 0 deletions docker/dynamo-db/seed/privacy-notice-acceptances-seed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"privacy-notice-acceptances": [
{
"PutRequest": {
"Item": {
"pnIdWithUserId": {
"S": "0df21ff6-3e8f-4320-af8f-23dea9135d57"
},
"versionNumber": {
"N": "3"
},
"acceptedAt": {
"S": "2024-04-17T08:52:24.995218Z"
},
"privacyNoticeId": {
"S": "0df21ff6-3e8f-4320-af8f-23dea9135d57"
},
"userId": {
"S": "7d7eab7b-fd79-438c-9ce6-019df34796ef"
},
"version": {
"M": {
"kind": {
"M": {
"PP": {
"S": "PP"
}
}
},
"version": {
"N": "3"
},
"versionId": {
"S": "f4090e0b-5c3f-4cc5-ba46-b17af6a79561"
}
}
}
}
}
}
]
}
44 changes: 44 additions & 0 deletions docker/dynamo-db/seed/privacy-notice-seed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"privacy-notice": [
{
"PutRequest": {
"Item": {
"privacyNoticeId": {
"S": "6bf8412a-41a7-41a0-82dc-26286ce61b1a"
},
"createdDate": {
"S": "2023-03-10T15:14:39.847Z"
},
"lastPublishedDate": {
"S": "2024-07-05T10:33:26.423Z"
},
"organizationId": {
"S": "5d6e166b-531a-4014-96b6-1cae7db0f64e"
},
"persistedAt": {
"S": "2024-09-04T15:01:24.467Z"
},
"privacyNoticeVersion": {
"M": {
"name": {
"S": "Interoperabilità - Backoffice - ToS"
},
"publishedDate": {
"S": "2024-07-05T10:33:26.423Z"
},
"status": {
"S": "ACTIVE"
},
"version": {
"N": "11"
},
"versionId": {
"S": "0805a4a4-5c7e-4092-96bd-b6dfeda4adfa"
}
}
}
}
}
}
]
}
4 changes: 2 additions & 2 deletions packages/agreement-process/aws.config.local
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[default]
aws_access_key_id=test-aws-key
aws_secret_access_key=test-aws-secret
aws_access_key_id=testawskey
aws_secret_access_key=testawssecret
region=eu-central-1
4 changes: 2 additions & 2 deletions packages/authorization-updater/aws.config.local
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[default]
aws_access_key_id=test-aws-key
aws_secret_access_key=test-aws-secret
aws_access_key_id=testawskey
aws_secret_access_key=testawssecret
region=eu-central-1
services=local

Expand Down
9 changes: 9 additions & 0 deletions packages/bff/.env
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ SUPPORT_LANDING_TOKEN_DURATION_SECONDS=300
SUPPORT_TOKEN_DURATION_SECONDS=3600
RISK_ANALYSIS_DOCUMENTS_PATH="risk-analysis/docs"

PRIVACY_NOTICES_CONTAINER="interop-local-bucket"
PRIVACY_NOTICES_PATH="privacy-notices-path"
PRIVACY_NOTICES_FILE_NAME="privacy-notice"

PRIVACY_NOTICES_TOS_UUID="6bf8412a-41a7-41a0-82dc-26286ce61b1a"
PRIVACY_NOTICES_PP_UUID="0df21ff6-3e8f-4320-af8f-23dea9135d57"
PRIVACY_NOTICES_DYNAMO_TABLE_NAME="privacy-notice"
PRIVACY_NOTICES_USERS_DYNAMO_TABLE_NAME="privacy-notice-acceptances"
S3_BUCKET=interop-local-bucket
ESERVICE_DOCUMENTS_PATH="interop-eservice-documents"

CONSUMER_DOCUMENTS_PATH="interop-consumer-documents"
Expand Down
7 changes: 5 additions & 2 deletions packages/bff/aws.config.local
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[default]
aws_access_key_id=test-aws-key
aws_secret_access_key=test-aws-secret
aws_access_key_id=testawskey
aws_secret_access_key=testawssecret
region=eu-central-1
services=local

[services local]
kms=
endpoint_url=http://localhost:4566

dynamodb=
endpoint_url=http://localhost:7001
2 changes: 2 additions & 0 deletions packages/bff/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"vitest": "1.6.0"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "3.602.0",
"@aws-sdk/util-dynamodb": "3.602.0",
"@zodios/core": "10.9.6",
"@zodios/express": "10.6.1",
"axios": "1.7.4",
Expand Down
2 changes: 2 additions & 0 deletions packages/bff/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
rateLimiterMiddleware,
} from "pagopa-interop-commons";
import { config } from "./config/config.js";
import privacyNoticeRouter from "./routers/privacyNoticeRouter.js";
import { getInteropBeClients } from "./providers/clientProvider.js";
import healthRouter from "./routers/HealthRouter.js";
import agreementRouter from "./routers/agreementRouter.js";
Expand Down Expand Up @@ -71,6 +72,7 @@ app.use(supportRouter(zodiosCtx, clients, redisRateLimiter));
app.use(toolRouter(zodiosCtx));
app.use(tenantRouter(zodiosCtx, clients));
app.use(clientRouter(zodiosCtx, clients));
app.use(privacyNoticeRouter(zodiosCtx));
app.use(producerKeychainRouter(zodiosCtx, clients));

export default app;
32 changes: 32 additions & 0 deletions packages/bff/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,35 @@ export type AuthorizationProcessServerConfig = z.infer<
typeof AuthorizationProcessServerConfig
>;

export const S3PrivacyNoticeConfig = z
.object({
PRIVACY_NOTICES_CONTAINER: z.string(),
PRIVACY_NOTICES_PATH: z.string(),
PRIVACY_NOTICES_FILE_NAME: z.string(),
RISK_ANALYSIS_DOCUMENTS_PATH: z.string(),
})
.transform((c) => ({
privacyNoticesContainer: c.PRIVACY_NOTICES_CONTAINER,
privacyNoticesPath: c.PRIVACY_NOTICES_PATH,
privacyNoticesFileName: c.PRIVACY_NOTICES_FILE_NAME,
riskAnalysisDocumentsPath: c.RISK_ANALYSIS_DOCUMENTS_PATH,
}));

export const PrivactNoticeConfig = z
.object({
PRIVACY_NOTICES_TOS_UUID: z.string(),
PRIVACY_NOTICES_PP_UUID: z.string(),
PRIVACY_NOTICES_DYNAMO_TABLE_NAME: z.string(),
PRIVACY_NOTICES_USERS_DYNAMO_TABLE_NAME: z.string(),
})
.transform((c) => ({
privacyNoticesTosUuid: c.PRIVACY_NOTICES_TOS_UUID,
privacyNoticesPpUuid: c.PRIVACY_NOTICES_PP_UUID,
privacyNoticesDynamoTableName: c.PRIVACY_NOTICES_DYNAMO_TABLE_NAME,
privacyNoticesUsersDynamoTableName:
c.PRIVACY_NOTICES_USERS_DYNAMO_TABLE_NAME,
}));

export const AllowedListConfig = z
.object({
ALLOW_LIST_CONTAINER: z.string(),
Expand Down Expand Up @@ -153,6 +182,9 @@ const BffProcessConfig = CommonHTTPServiceConfig.and(TenantProcessServerConfig)
.and(SessionTokenGenerationConfig)
.and(FileManagerConfig)
.and(AllowedListConfig)
.and(PrivactNoticeConfig)
.and(S3Config)
.and(S3PrivacyNoticeConfig)
.and(S3Config)
.and(RiskAnalysisDocumentConfig)
.and(ExportFileConfig)
Expand Down
45 changes: 45 additions & 0 deletions packages/bff/src/model/domain/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { constants } from "http2";
import {
ApiError,
parseErrorMessage,
makeApiProblemBuilder,
AttributeId,
} from "pagopa-interop-models";
Expand Down Expand Up @@ -38,6 +39,10 @@ export const errorCodes = {
contractNotFound: "0030",
contractException: "0031",
notValidDescriptor: "0032",
dynamoReadingError: "0033",
privacyNoticeNotFoundInConfiguration: "0034",
privacyNoticeNotFound: "0035",
privacyNoticeVersionIsNotTheLatest: "0036",
};

export type ErrorCodes = keyof typeof errorCodes;
Expand Down Expand Up @@ -77,6 +82,46 @@ export function purposeNotFound(purposeId: string): ApiError<ErrorCodes> {
});
}

export function dynamoReadingError(error: unknown): ApiError<ErrorCodes> {
return new ApiError({
detail: `Error while reading data from Dynamo -> ${parseErrorMessage(
error
)}`,
code: "dynamoReadingError",
title: "Dynamo reading error",
});
}

export function privacyNoticeNotFoundInConfiguration(
privacyNoticeKind: string
): ApiError<ErrorCodes> {
return new ApiError({
detail: `Privacy Notice ${privacyNoticeKind} not found in configuration`,
code: "privacyNoticeNotFoundInConfiguration",
title: "Privacy Notice not found in configuration",
});
}

export function privacyNoticeNotFound(
privacyNoticeKind: string
): ApiError<ErrorCodes> {
return new ApiError({
detail: `Privacy Notice ${privacyNoticeKind} not found`,
code: "privacyNoticeNotFound",
title: "Privacy Notice not found",
});
}

export function privacyNoticeVersionIsNotTheLatest(
versionId: string
): ApiError<ErrorCodes> {
return new ApiError({
detail: `PrivacyNotice version ${versionId} not found`,
code: "privacyNoticeVersionIsNotTheLatest",
title: "Privacy Notice version is not the latest",
});
}

export function agreementDescriptorNotFound(
agreementId: string
): ApiError<ErrorCodes> {
Expand Down
Loading

0 comments on commit 53c4780

Please sign in to comment.