Skip to content

Commit 5475223

Browse files
committed
Refactor TokenValidator
Moves some attributes out of the constructor which makes testing easier.
1 parent fcb6933 commit 5475223

3 files changed

Lines changed: 43 additions & 17 deletions

File tree

manage_breast_screening/dicom/tests/test_api.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pytest
77
from django.core.files.uploadedfile import SimpleUploadedFile
88
from ninja.testing import TestClient
9+
from pydicom.uid import generate_uid
910

1011
from manage_breast_screening.core.api import api
1112
from manage_breast_screening.gateway.models import GatewayActionStatus
@@ -21,8 +22,10 @@
2122

2223

2324
@pytest.fixture(autouse=True)
24-
def enable_api(monkeypatch):
25+
def setup(monkeypatch):
2526
monkeypatch.setenv("API_ENABLED", "true")
27+
monkeypatch.setenv("API_AUDIENCE", "test_audience")
28+
monkeypatch.setenv("TENANT_ID", "test_tenant_id")
2629

2730

2831
@pytest.fixture
@@ -181,6 +184,26 @@ def test_upload_invalid_auth(dicom_file):
181184
}
182185

183186

187+
def test_upload_bypass_token_validation(dicom_file):
188+
with patch.object(TokenValidator, "bypass_authentication", return_value=True):
189+
with patch.object(
190+
DicomRecorder,
191+
"get_or_create_records",
192+
return_value=(
193+
MagicMock(study_instance_uid=generate_uid()),
194+
MagicMock(series_instance_uid=generate_uid()),
195+
MagicMock(sop_instance_uid=generate_uid(), id=1),
196+
),
197+
):
198+
response = client.put(
199+
"/dicom/abc123",
200+
FILES={"file": dicom_file},
201+
headers={"Authorization": "Bearer anytoken"},
202+
)
203+
204+
assert response.status_code == 201
205+
206+
184207
@pytest.mark.django_db
185208
def test_report_failure(mock_token_validator):
186209
action = GatewayActionFactory()

manage_breast_screening/dicom/tests/test_token_validator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import jwt
55
import pytest
6+
from django.conf import settings
67

78
from ..token_validator import TokenValidator
89

@@ -133,7 +134,7 @@ def test_with_valid_token(
133134
}
134135

135136
def test_authentication_bypass_enabled(self, mock_logger):
136-
with patch.dict("os.environ", {"BYPASS_API_TOKEN_AUTH": "true"}):
137+
with patch.object(settings, "BYPASS_API_TOKEN_AUTH", return_value=True):
137138
validator = TokenValidator()
138139
assert validator(Mock(headers={"Authorization": "Bearer anytoken"})) == {
139140
"sub": "bypass_user"

manage_breast_screening/dicom/token_validator.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from urllib.request import urlopen
55

66
import jwt
7+
from django.conf import settings
78
from ninja.security import HttpBearer
89

910
logger = logging.getLogger(__name__)
@@ -12,19 +13,8 @@
1213

1314

1415
class TokenValidator(HttpBearer):
15-
def __init__(self):
16-
self.bypass_auth = os.getenv("BYPASS_API_TOKEN_AUTH", "false").lower() == "true"
17-
self.api_audience = os.environ["API_AUDIENCE"]
18-
self.tenant_id = os.environ["TENANT_ID"]
19-
self.discovery_keys_url = (
20-
"https://login.microsoftonline.com/"
21-
+ self.tenant_id
22-
+ "/discovery/v2.0/keys"
23-
)
24-
self.issuer_url = "https://sts.windows.net/" + self.tenant_id + "/"
25-
2616
def authenticate(self, request, token) -> dict | None:
27-
if self.bypass_auth:
17+
if self.bypass_authentication:
2818
logger.warning("Authentication bypass is enabled.")
2919
return {"sub": "bypass_user"}
3020

@@ -49,16 +39,16 @@ def _rsa_key(self, token) -> dict | None:
4939
"e": key["e"],
5040
}
5141
except Exception:
52-
logger.error("Unable to parse authentication token.", exc_info=True)
42+
logger.exception("Unable to parse authentication token.")
5343

5444
def _decode(self, token: str, rsa_key: dict) -> dict | None:
5545
try:
5646
payload = jwt.decode(
5747
token,
5848
rsa_key,
5949
algorithms=ALLOWED_ALGORITHMS,
60-
audience=self.api_audience,
61-
issuer=self.issuer_url,
50+
audience=os.getenv("API_AUDIENCE"),
51+
issuer=f"https://sts.windows.net/{self.tenant_id}/",
6252
)
6353
return payload
6454
except jwt.ExpiredSignatureError:
@@ -69,3 +59,15 @@ def _decode(self, token: str, rsa_key: dict) -> dict | None:
6959
logger.exception("Token is invalid")
7060
except Exception:
7161
logger.exception("Unable to parse authentication token.")
62+
63+
@property
64+
def discovery_keys_url(self) -> str:
65+
return f"https://login.microsoftonline.com/{self.tenant_id}/discovery/v2.0/keys"
66+
67+
@property
68+
def tenant_id(self) -> str | None:
69+
return os.getenv("TENANT_ID", "")
70+
71+
@property
72+
def bypass_authentication(self) -> bool:
73+
return getattr(settings, "BYPASS_API_TOKEN_AUTH", False)

0 commit comments

Comments
 (0)