Skip to content

Commit 34bd648

Browse files
authored
Merge branch 'master' into APM-000-AMP-3654-removed-interactive-product-backlog
2 parents d6f5e12 + af6c41e commit 34bd648

File tree

149 files changed

+5985
-2115
lines changed

Some content is hidden

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

149 files changed

+5985
-2115
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

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ See https://nhsd-confluence.digital.nhs.uk/display/APM/Glossary.
3939
| Folder | Description |
4040
|------------------------|-------------|
4141
| `infra` | Base infrastructure components. |
42+
| `infra_old` | Old infra code used to create INT to mimic prod. |
4243
| `grafana` | Terraform configuration for Grafana, built on top of core infra. |
4344
| `terraform` | Core Terraform infrastructure code. This is run in each PR and sets up lambdas associated with the PR.|
45+
| `terraform_old` | Old tf code used to create INT to mimic prod. |
4446
| `terraform_sandbox` | Sandbox environment for testing infrastructure changes. |
4547
| `terraform_aws_backup` | Streamlined backup processing with AWS. |
4648
| `mesh-infra` | Infrastructure setup for Imms batch MESH integration. |

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: 40 additions & 51 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

0 commit comments

Comments
 (0)