Skip to content

Commit a8b773b

Browse files
committed
Merge branch 'master' into VED-355-Mesh-Terraform-Mailbox
# Conflicts: # infra/variables.tf
2 parents 70a161d + e3c5e74 commit a8b773b

File tree

91 files changed

+2118
-2282
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+2118
-2282
lines changed

.github/workflows/sonarcloud.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ jobs:
5959
- name: Run unittest with recordforwarder-coverage
6060
working-directory: backend
6161
id: recordforwarder
62+
env:
63+
PYTHONPATH: ${{ github.workspace }}/backend/src:${{ github.workspace }}/backend/tests
6264
continue-on-error: true
6365
run: |
6466
poetry env use 3.11
@@ -90,6 +92,8 @@ jobs:
9092
9193
- name: Run unittest with coverage-fhir-api
9294
working-directory: backend
95+
env:
96+
PYTHONPATH: ${{ github.workspace }}/backend/src:${{ github.workspace }}/backend/tests
9397
id: fhirapi
9498
continue-on-error: true
9599
run: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ openapi.json
2929
!**/.vscode/settings.json.default
3030

3131
devtools/volume/
32+
backend/tests/.coverage

ack_backend/tests/test_splunk_logging.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from tests.utils_for_ack_backend_tests.utils_for_ack_backend_tests import generate_event
2020

2121
with patch.dict("os.environ", MOCK_ENVIRONMENT_DICT):
22-
from src.ack_processor import lambda_handler
22+
from ack_processor import lambda_handler
2323

2424
s3_client = boto3_client("s3")
2525

backend/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ package: build
66
docker run --rm -v $(shell pwd)/build:/build imms-lambda-build
77

88
test:
9-
python -m unittest
9+
@PYTHONPATH=src:tests python -m unittest
1010

1111
.PHONY: build package test

backend/poetry.lock

Lines changed: 37 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ python = "~3.11"
1212
boto3 = "~1.38.42"
1313
boto3-stubs-lite = {extras = ["dynamodb"], version = "~1.38.42"}
1414
aws-lambda-typing = "~2.20.0"
15+
redis = "^4.6.0"
1516
moto = "^5.1.6"
1617
requests = "~2.32.4"
1718
responses = "~0.25.7"

backend/src/authorization.py

Lines changed: 8 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from models.errors import UnauthorizedError
77

8-
PERMISSIONS_HEADER = "Permissions"
8+
99
AUTHENTICATION_HEADER = "AuthenticationType"
1010

1111

@@ -14,40 +14,16 @@ class UnknownPermission(RuntimeError):
1414
"""Error when the parsed value can't be converted to Permissions enum."""
1515

1616

17-
class EndpointOperation(Enum):
18-
"""The kind of operation.
19-
This maps one-to-one to each endpoint. Authorization class decides whether there are sufficient permissions or not.
20-
The caller is responsible for passing the correct operation.
21-
"""
22-
READ = 0,
23-
CREATE = 1,
24-
UPDATE = 2,
25-
DELETE = 3,
26-
SEARCH = 4,
27-
28-
2917
class AuthType(str, Enum):
3018
"""This backend supports all three types of authentication.
3119
An Apigee App should specify AuthenticationType in its custom attribute.
3220
Each Apigee app can only have one type of authentication which is enforced by onboarding process.
3321
See: https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation"""
3422
APP_RESTRICTED = "ApplicationRestricted",
35-
NHS_LOGIN = "NnsLogin",
23+
NHS_LOGIN = "NhsLogin",
3624
CIS2 = "Cis2",
3725

3826

39-
class Permission(str, Enum):
40-
"""Permission name for each operation that can be done to an Immunization Resource
41-
An Apigee App should specify a set of these as a comma-separated custom attribute.
42-
Permission works the same way as 'scope' but, in this case, they're called permission to distinguish them from
43-
OAuth2 scopes"""
44-
READ = "immunization:read"
45-
CREATE = "immunization:create"
46-
UPDATE = "immunization:update"
47-
DELETE = "immunization:delete"
48-
SEARCH = "immunization:search"
49-
50-
5127
class Authorization:
5228
""" Authorize the call based on the endpoint and the authentication type.
5329
This class uses the passed headers from Apigee to decide the type of authentication (Application Restricted,
@@ -56,52 +32,13 @@ class Authorization:
5632
UnknownPermission is due to proxy bad configuration, and should result in 500. Any invalid value, either
5733
insufficient permissions or bad string, will result in UnauthorizedError if it comes from user.
5834
"""
59-
60-
def authorize(self, operation: EndpointOperation, aws_event: dict):
35+
36+
def authorize(self, aws_event: dict):
6137
auth_type = self._parse_auth_type(aws_event["headers"])
62-
if auth_type == AuthType.APP_RESTRICTED:
63-
self._app_restricted(operation, aws_event)
64-
if auth_type == AuthType.CIS2:
65-
self._cis2(operation, aws_event)
66-
# TODO(NhsLogin_AMB-1923) add NHSLogin
67-
else:
68-
UnauthorizedError()
69-
70-
_app_restricted_map = {
71-
EndpointOperation.READ: {Permission.READ},
72-
EndpointOperation.CREATE: {Permission.CREATE},
73-
EndpointOperation.UPDATE: {Permission.UPDATE, Permission.CREATE},
74-
EndpointOperation.DELETE: {Permission.DELETE},
75-
EndpointOperation.SEARCH: {Permission.SEARCH},
76-
}
77-
78-
def _app_restricted(self, operation: EndpointOperation, aws_event: dict) -> None:
79-
allowed = self._parse_permissions(aws_event["headers"])
80-
requested = self._app_restricted_map[operation]
81-
if not requested.issubset(allowed):
38+
39+
if auth_type not in {AuthType.APP_RESTRICTED, AuthType.CIS2, AuthType.NHS_LOGIN}:
8240
raise UnauthorizedError()
8341

84-
def _cis2(self, operation: EndpointOperation, aws_event: dict) -> None:
85-
# Cis2 works exactly the same as ApplicationRestricted
86-
self._app_restricted(operation, aws_event)
87-
88-
@staticmethod
89-
def _parse_permissions(headers) -> Set[Permission]:
90-
"""Given headers return a set of Permissions. Raises UnknownPermission"""
91-
92-
content = headers.get(PERMISSIONS_HEADER, "")
93-
# comma-separate the Permissions header then trim and finally convert to lowercase
94-
parsed = [str.strip(str.lower(s)) for s in content.split(",")]
95-
96-
permissions = set()
97-
for s in parsed:
98-
try:
99-
permissions.add(Permission(s))
100-
except ValueError:
101-
raise UnknownPermission()
102-
103-
return permissions
104-
10542
@staticmethod
10643
def _parse_auth_type(headers) -> AuthType:
10744
try:
@@ -112,14 +49,13 @@ def _parse_auth_type(headers) -> AuthType:
11249
# we raise UnknownPermission in case of an error and not UnauthorizedError
11350
raise UnknownPermission()
11451

115-
116-
def authorize(operation: EndpointOperation):
52+
def authorize():
11753
def decorator(func):
11854
auth = Authorization()
11955

12056
@wraps(func)
12157
def wrapper(controller_instance, a):
122-
auth.authorize(operation, a)
58+
auth.authorize(a)
12359
return func(controller_instance, a)
12460

12561
return wrapper

backend/src/clients.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
1-
"""Initialise s3, kinesis and lambda clients"""
1+
"""Initialise s3, kinesis, lambda and redis clients"""
22

33
from boto3 import client as boto3_client
4+
import os
5+
import logging
6+
import redis
47

5-
REGION_NAME = "eu-west-2"
8+
REGION_NAME = os.getenv("AWS_REGION", "eu-west-2")
69

710
s3_client = boto3_client("s3", region_name=REGION_NAME)
811
kinesis_client = boto3_client("kinesis", region_name=REGION_NAME)
912
lambda_client = boto3_client("lambda", region_name=REGION_NAME)
1013
firehose_client = boto3_client("firehose", region_name=REGION_NAME)
1114
sqs_client = boto3_client("sqs", region_name=REGION_NAME)
15+
16+
REDIS_HOST = os.getenv("REDIS_HOST", "")
17+
REDIS_PORT = int(os.getenv("REDIS_PORT", 6379))
18+
19+
20+
logging.basicConfig(level="INFO")
21+
logger = logging.getLogger()
22+
logger.info(f"Connecting to Redis at {REDIS_HOST}:{REDIS_PORT}")
23+
24+
redis_client = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)

backend/src/create_imms_handler.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pprint
44
import uuid
55

6-
from authorization import Permission
6+
77
from fhir_controller import FhirController, make_controller
88
from local_lambda import load_string
99
from models.errors import Severity, Code, create_operation_outcome
@@ -42,7 +42,6 @@ def create_immunization(event, controller: FhirController):
4242
"headers": {
4343
"Content-Type": "application/x-www-form-urlencoded",
4444
"AuthenticationType": "ApplicationRestricted",
45-
"Permissions": (",".join([Permission.CREATE])),
4645
},
4746
}
4847

backend/src/delete_imms_handler.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pprint
44
import uuid
55

6-
from authorization import Permission
6+
77
from fhir_controller import FhirController, make_controller
88
from models.errors import Severity, Code, create_operation_outcome
99
from log_structure import function_info
@@ -40,8 +40,7 @@ def delete_immunization(event, controller: FhirController):
4040
"pathParameters": {"id": args.id},
4141
"headers": {
4242
"Content-Type": "application/x-www-form-urlencoded",
43-
"AuthenticationType": "ApplicationRestricted",
44-
"Permissions": (",".join([Permission.DELETE])),
43+
"AuthenticationType": "ApplicationRestricted"
4544
},
4645
}
4746
pprint.pprint(delete_imms_handler(event, {}))

0 commit comments

Comments
 (0)