From 81232bb4a6ba1a3119ed15205ac4438bf3aaf17b Mon Sep 17 00:00:00 2001 From: thomahas Date: Thu, 31 Oct 2024 13:39:23 +0100 Subject: [PATCH] feat: added validation of dt-asymmetric-signature. The data connector now includes a JWT in the header dt-asymmertric-signature that is signed by an internal DT key. The public key can be accessed from the oidc discovery endpoint. Added validation of the token. The signature secrets are now considered deprecated and optional. --- dtintegrations/__init__.py | 2 +- dtintegrations/data_connector/http_push.py | 197 +++++++++++++++++---- setup.cfg | 2 + tests/events.py | 10 +- tests/framework.py | 7 +- tests/test_http_push.py | 94 +++++++++- tests/test_outputs.py | 7 +- 7 files changed, 274 insertions(+), 45 deletions(-) diff --git a/dtintegrations/__init__.py b/dtintegrations/__init__.py index a97460e..16b3c24 100644 --- a/dtintegrations/__init__.py +++ b/dtintegrations/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.6.1" +__version__ = "0.7.0" from dtintegrations import data_connector as data_connector # noqa from dtintegrations import provider as provider # noqa diff --git a/dtintegrations/data_connector/http_push.py b/dtintegrations/data_connector/http_push.py index eee6383..6508836 100644 --- a/dtintegrations/data_connector/http_push.py +++ b/dtintegrations/data_connector/http_push.py @@ -1,6 +1,7 @@ import jwt import json import hashlib +import requests from typing import Any, Optional import disruptive # type: ignore @@ -21,15 +22,27 @@ class HttpPush(outputs.OutputBase): Labels from the source device forwarded by the Data Connector. """ - def __init__(self, headers: dict, body: bytes, secret: str): + def __init__( + self, + headers: dict, + body: bytes, + secret: str = '', + org_id: str = '', + oidc_config_uri: str = ( + 'https://identity.disruptive-technologies.com/' + 'data-connector/.well-known/openid-configuration' + ) + ): """ - Constructs the HtttpPush object given request contents. + Constructs the HttpPush object given request contents. """ self._headers = headers self._body = body self._secret = secret + self._org_id = org_id + self._oidc_config_uri = oidc_config_uri self._body_dict = self._decode(headers, body, secret) super().__init__(self._body_dict) @@ -43,6 +56,8 @@ def __repr__(self) -> str: 'headers={},'\ 'body={},'\ 'secret={},'\ + 'org_id={},'\ + 'oidc_config_uri={}'\ ')' return string.format( self.__class__.__module__, @@ -50,6 +65,8 @@ def __repr__(self) -> str: self._headers, self._body, repr(self._secret), + repr(self._org_id), + repr(self._oidc_config_uri), ) def get_device_metadata(self) -> Optional[metadata.DeviceMetadata]: @@ -69,7 +86,7 @@ def get_device_metadata(self) -> Optional[metadata.DeviceMetadata]: except KeyError: return None - def _decode(self, headers: dict, body: bytes, secret: str) -> dict: + def _decode(self, headers: dict, body: bytes, secret: str = '') -> dict: """ Decodes the incoming event, first validating the source- and origin using a signature secret and the request header- and body. @@ -84,6 +101,11 @@ def _decode(self, headers: dict, body: bytes, secret: str) -> dict: secret : str The secret to sign the request at source. + Deprecated: + This is now deprecated in favor of the + X-DT-JWT-Assertion header. + The secret is no longer required. + Returns ------- payload : HttpPush @@ -97,28 +119,136 @@ def _decode(self, headers: dict, body: bytes, secret: str) -> dict: """ - # Do some mild secret sanitization, ensuring populated string. - if isinstance(secret, str): - if len(secret) == 0: - raise disruptive.errors.ConfigurationError( - 'Secret is empty string.' - ) - else: - raise TypeError( - f'Got secret type <{type(secret).__name__}>. Expected .' - ) - # Isolate the token in request headers. + custom_token = None token = None for key in headers: if key.lower() == 'x-dt-signature': + custom_token = headers[key] + if key.lower() == 'dt-asymmetric-signature': token = headers[key] - break - if token is None: + + # Calculate the body SHA-256 checksum. + m = hashlib.sha256() + m.update(body) + checksum_sha256 = m.digest().hex() + + # Validate the custom token if it exists. + if custom_token: + self._validate_custom_token(custom_token, checksum_sha256, secret) + + # Validate the DT Data Connector token. + if token: + self._validate_dt_token(token, checksum_sha256) + else: raise disruptive.errors.ConfigurationError( - 'Missing header X-Dt-Signature.' + 'No Data Connector token found.' ) + # Convert the body bytes string into a dictionary. + body_dict = json.loads(body.decode('utf-8')) + + return dict(body_dict) + + def _validate_dt_token(self, token: str, checksum: str) -> None: + """ + Validates the Data Connector token using the signature secret. + + Parameters + ---------- + token : str + The token to validate. + checksum : str + The SHA-256 checksum of the request body. + + Raises + ------ + ConfigurationError + If the token is invalid, expired, or the checksum does not match, + or if is not signed by DTs OIDC provider. + + """ + + try: + oidc_config = requests.get(self._oidc_config_uri).json() + except requests.exceptions.RequestException: + raise disruptive.errors.ConfigurationError( + 'Failed to fetch OIDC configuration.' + ) + except json.JSONDecodeError: + raise disruptive.errors.ConfigurationError( + 'Failed to parse OIDC configuration.' + ) + + # Decode the token using the JWK client. + try: + jwks_client = jwt.PyJWKClient( + oidc_config['jwks_uri'], cache_jwk_set=True + ) + signing_key = jwks_client.get_signing_key_from_jwt(token) + payload = jwt.decode( + token, + signing_key.key, + algorithms=oidc_config[ + 'id_token_signing_alg_values_supported' + ], + issuer=oidc_config['issuer'], + options={ + 'verify_signature': True, + 'verify_iss': True, + 'verify_exp': True, + 'verify_iat': True, + } + ) + except jwt.exceptions.InvalidSignatureError: + raise disruptive.errors.ConfigurationError( + 'Invalid signature.' + ) + except jwt.exceptions.ExpiredSignatureError: + raise disruptive.errors.ConfigurationError( + 'Signature has expired.' + ) + except jwt.exceptions.InvalidIssuerError: + raise disruptive.errors.ConfigurationError( + 'Invalid issuer: {}'.format(payload['iss']) + ) + except jwt.exceptions.InvalidAlgorithmError: + raise disruptive.errors.ConfigurationError( + 'Invalid algorithm.' + ) + if self._org_id and payload['sub'] != self._org_id: + raise disruptive.errors.ConfigurationError( + 'Invalid subject, should match the organization ID.' + ) + + # Calculate and compare the body SHA-256 checksum. + if payload['checksum_sha256'] != checksum: + raise disruptive.errors.ConfigurationError( + 'Checksum mismatch.' + ) + + @staticmethod + def _validate_custom_token(token: str, checksum: str, secret: str) -> None: + """ + Validates the custom token using the signature secret. + + Parameters + ---------- + token : str + The token to validate. + body : bytes + The request body bytes. + secret : str + The secret to sign the token at source. + + Raises + ------ + ConfigurationError + If the token is invalid, expired, or the checksum does not match, + or if the secret does not match the token signature. + + """ + # Decode the token using the signature secret. try: payload = jwt.decode( @@ -136,30 +266,31 @@ def _decode(self, headers: dict, body: bytes, secret: str) -> dict: ) # Calculate and compare the body SHA-256 checksum. - m = hashlib.sha256() - m.update(body) - checksum_sha256 = m.digest().hex() - if payload['checksum_sha256'] != checksum_sha256: + if payload['checksum_sha256'] != checksum: raise disruptive.errors.ConfigurationError( 'Checksum mismatch.' ) - # Convert the body bytes string into a dictionary. - body_dict = json.loads(body.decode('utf-8')) - - return dict(body_dict) - @staticmethod - def from_provider(request: Any, provider: str, secret: str) -> Any: + def from_provider( + request: Any, + provider: str, + secret: str = '', + org_id: str = '', + oidc_config_uri: str = ( + 'https://identity.disruptive-technologies.com/' + 'data-connector/.well-known/openid-configuration' + ), + ) -> Any: """ Decodes the incoming event using a specified provider, first validating - the the source- and origin using a signature secretand + the the source- and origin using a signature secret and the provider-specific request. Parameters ---------- request : Any - Unmodified incoming request format of the spcified provider. + Unmodified incoming request format of the specified provider. provider : {"flask", "gcloud", "lambda", "azure"}, str Name of the :ref:`provider ` used to receive the request. @@ -183,4 +314,10 @@ def from_provider(request: Any, provider: str, secret: str) -> Any: r = dtrequest.Request(request, provider) # Use a more generic function for the validation process. - return HttpPush(r.headers, r.body_bytes, secret) + return HttpPush( + r.headers, + r.body_bytes, + secret, + org_id=org_id, + oidc_config_uri=oidc_config_uri, + ) diff --git a/setup.cfg b/setup.cfg index 07f8936..937ebd9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,6 +29,8 @@ classifiers = python_requires = >=3.8 install_requires = disruptive >= 1.6.0 + cryptography + types-requests packages = find: [options.packages.find] diff --git a/tests/events.py b/tests/events.py index 6c277fb..a943e2f 100644 --- a/tests/events.py +++ b/tests/events.py @@ -16,20 +16,22 @@ def __init__(self, touch = Event( headers={ - 'X-Dt-Signature': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGVja3N1bSI6ImYyYjVkYzA0OTY0MzcyMDFkM2NlZTE1NmMyMzNhMTMzNTYwM2Q0NjUiLCJjaGVja3N1bV9zaGEyNTYiOiJhZmQ4MWExNmJiOWMwNzZlNTdmOTI3MjhmMjg0ZmY1NTgzMWJmZGYxZDNjMmMxMGI4MzM1NjdmNTIxZDc4MmRlIiwiZXhwIjoxNjQ0NTAwNTMzLCJpYXQiOjE2NDQ0OTY5MzMsImp0aSI6ImM4MmdnOTloZ2EzNWpjMmFma2VnIn0.gm8m_HrhlOnAyS08CfR_RNYdZQIpGV6E8bk3UiAR3uo' # noqa + 'X-Dt-Signature': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGVja3N1bSI6ImYyYjVkYzA0OTY0MzcyMDFkM2NlZTE1NmMyMzNhMTMzNTYwM2Q0NjUiLCJjaGVja3N1bV9zaGEyNTYiOiJhZmQ4MWExNmJiOWMwNzZlNTdmOTI3MjhmMjg0ZmY1NTgzMWJmZGYxZDNjMmMxMGI4MzM1NjdmNTIxZDc4MmRlIiwiZXhwIjoxNjQ0NTAwNTMzLCJpYXQiOjE2NDQ0OTY5MzMsImp0aSI6ImM4MmdnOTloZ2EzNWpjMmFma2VnIn0.gm8m_HrhlOnAyS08CfR_RNYdZQIpGV6E8bk3UiAR3uo', # noqa + 'DT-Asymmetric-Signature': 'eyJhbGciOiJFUzI1NiIsImtpZCI6ImNwNjVnZG9uMWU0YmhiNWljYm1nIiwidHlwIjoiSldUIn0.eyJjaGVja3N1bV9zaGEyNTYiOiJhZmQ4MWExNmJiOWMwNzZlNTdmOTI3MjhmMjg0ZmY1NTgzMWJmZGYxZDNjMmMxMGI4MzM1NjdmNTIxZDc4MmRlIiwiZXhwIjoxNzMwMzgyNTg0LCJpYXQiOjE3MzAzODIyODQsImlzcyI6Imh0dHBzOi8vaWRlbnRpdHkuZGV2LmRpc3J1cHRpdmUtdGVjaG5vbG9naWVzLmNvbS9kYXRhLWNvbm5lY3RvciIsInN1YiI6InRlc3Qtb3JnLWlkIn0.RYTAdKD_CVhhu-kChnqtHzSB3wFOtidBYYhu39k5mJCuO3PLBzkHwzKe73kx4hEJ1YUR-jQGNZzqhSxpQ523Yw' # noqa }, body_str='{"event":{"eventId":"c82gg9catj3vmh4248og","targetName":"projects/c14u9p094l47cdv1o3pg/devices/emuc17m6d7lq0bgk44smcqg","eventType":"touch","data":{"touch":{"updateTime":"2022-02-10T12:42:13.313949Z"}},"timestamp":"2022-02-10T12:42:13.313949Z"},"labels":{"name":"touch"},"metadata":{"deviceId":"emuc17m6d7lq0bgk44smcqg","projectId":"c14u9p094l47cdv1o3pg","deviceType":"touch","productNumber":""}}', # noqa body={'event': {'eventId': 'c82gg9catj3vmh4248og', 'targetName': 'projects/c14u9p094l47cdv1o3pg/devices/emuc17m6d7lq0bgk44smcqg', 'eventType': 'touch', 'data': {'touch': {'updateTime': '2022-02-10T12:42:13.313949Z'}}, 'timestamp': '2022-02-10T12:42:13.313949Z'}, 'labels': {'name': 'touch'}, 'metadata': {'deviceId': 'emuc17m6d7lq0bgk44smcqg', 'projectId': 'c14u9p094l47cdv1o3pg', 'deviceType': 'touch', 'productNumber': ''}}, # noqa - payload={'checksum': 'f2b5dc0496437201d3cee156c233a1335603d465', 'checksum_sha256': 'afd81a16bb9c076e57f92728f284ff55831bfdf1d3c2c10b833567f521d782de', 'exp': 1644500533, 'iat': 1644496933, 'jti': 'c82gg99hga35jc2afkeg'}, # noqa + payload={'checksum': 'f2b5dc0496437201d3cee156c233a1335603d465', 'checksum_sha256': 'afd81a16bb9c076e57f92728f284ff55831bfdf1d3c2c10b833567f521d782de', 'exp': 1644500533, 'iat': 1644496933, 'jti': 'c82gg99hga35jc2afkeg', 'sub': 'test-org-id'}, # noqa ) temperature = Event( headers={ - 'X-Dt-Signature': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGVja3N1bSI6IjBmNzUzOTM5YTEzNzdhM2M5NGFiZDJjZmViZGMyY2I0NzU2YzNmYzMiLCJjaGVja3N1bV9zaGEyNTYiOiI1YjVmYzJlN2M3NjM3YWIzN2RiNDJiNzQ1YmY2ZjEyOWZhZTk3ODJmOTUzZWQzZGM2NDM1YzMyMTQ4ZjQxNWQ0IiwiZXhwIjoxNzE2OTgyNDQzLCJpYXQiOjE3MTY5Nzg4NDMsImp0aSI6ImNwYmc5NnQxaXNqbHIydmRvcmMwIn0.A4r91SKDW4IqYtb1eXuTQCUxM6GnouPbt94Ywd-vCpU' # noqa + 'X-Dt-Signature': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGVja3N1bSI6IjBmNzUzOTM5YTEzNzdhM2M5NGFiZDJjZmViZGMyY2I0NzU2YzNmYzMiLCJjaGVja3N1bV9zaGEyNTYiOiI1YjVmYzJlN2M3NjM3YWIzN2RiNDJiNzQ1YmY2ZjEyOWZhZTk3ODJmOTUzZWQzZGM2NDM1YzMyMTQ4ZjQxNWQ0IiwiZXhwIjoxNzE2OTgyNDQzLCJpYXQiOjE3MTY5Nzg4NDMsImp0aSI6ImNwYmc5NnQxaXNqbHIydmRvcmMwIn0.A4r91SKDW4IqYtb1eXuTQCUxM6GnouPbt94Ywd-vCpU', # noqa + 'DT-Asymmetric-Signature': 'eyJhbGciOiJFUzI1NiIsImtpZCI6ImNwNjVnZG9uMWU0YmhiNWljYm1nIiwidHlwIjoiSldUIn0.eyJjaGVja3N1bV9zaGEyNTYiOiJhZmQ4MWExNmJiOWMwNzZlNTdmOTI3MjhmMjg0ZmY1NTgzMWJmZGYxZDNjMmMxMGI4MzM1NjdmNTIxZDc4MmRlIiwiZXhwIjoxNzMwMzgyNTg0LCJpYXQiOjE3MzAzODIyODQsImlzcyI6Imh0dHBzOi8vaWRlbnRpdHkuZGV2LmRpc3J1cHRpdmUtdGVjaG5vbG9naWVzLmNvbS9kYXRhLWNvbm5lY3RvciIsInN1YiI6InRlc3Qtb3JnLWlkIn0.RYTAdKD_CVhhu-kChnqtHzSB3wFOtidBYYhu39k5mJCuO3PLBzkHwzKe73kx4hEJ1YUR-jQGNZzqhSxpQ523Yw' # noqa }, body_str='{"event":{"eventId":"cpbg96qngr7ai7ldarrg","targetName":"projects/ccol8iuk9smqiha4e8l0/devices/emucdd6ef2eu277bo8f52vg","eventType":"temperature","data":{"temperature":{"value":27,"updateTime":"2024-05-29T10:34:03.970415Z","samples":[{"value":27,"sampleTime":"2024-05-29T10:34:03.970415Z"}],"isBackfilled":false}},"timestamp":"2024-05-29T10:34:03.970415Z"},"labels":{"name":"test temp"},"metadata":{"deviceId":"emucdd6ef2eu277bo8f52vg","projectId":"ccol8iuk9smqiha4e8l0","deviceType":"temperature","productNumber":""}}', # noqa body={'event':{'eventId':'cpbg96qngr7ai7ldarrg','targetName':'projects/ccol8iuk9smqiha4e8l0/devices/emucdd6ef2eu277bo8f52vg','eventType':'temperature','data':{'temperature':{'value':27,'updateTime':'2024-05-29T10:34:03.970415Z','samples':[{'value':27,'sampleTime':'2024-05-29T10:34:03.970415Z'}],'isBackfilled':False}},'timestamp':'2024-05-29T10:34:03.970415Z'},'labels':{'name':'test temp'},'metadata':{'deviceId':'emucdd6ef2eu277bo8f52vg','projectId':'ccol8iuk9smqiha4e8l0','deviceType':'temperature','productNumber':''}}, # noqa - payload={'checksum': '0f753939a1377a3c94abd2cfebdc2cb4756c3fc3', 'checksum_sha256': '5b5fc2e7c7637ab37db42b745bf6f129fae9782f953ed3dc6435c32148f415d4', 'exp': 1716982443, 'iat': 1716978843, 'jti': 'cpbg96t1isjlr2vdorc0'}, # noqa + payload={'checksum': '0f753939a1377a3c94abd2cfebdc2cb4756c3fc3', 'checksum_sha256': '5b5fc2e7c7637ab37db42b745bf6f129fae9782f953ed3dc6435c32148f415d4', 'exp': 1716982443, 'iat': 1716978843, 'jti': 'cpbg96t1isjlr2vdorc0', 'sub': 'test-org-id'}, # noqa ) metadata = { diff --git a/tests/framework.py b/tests/framework.py index 4631982..86857a2 100644 --- a/tests/framework.py +++ b/tests/framework.py @@ -91,5 +91,10 @@ def __init__(self, mocker): side_effect=self._patched_jwt_decode, ) - def _patched_jwt_decode(self, token: str, secret: str, algorithms: list): + def _patched_jwt_decode(self, + token: str, + secret: str, + algorithms: list, + issuer: str = '', + options: str = '') -> dict: return self.event.payload diff --git a/tests/test_http_push.py b/tests/test_http_push.py index c759758..0ba0b04 100644 --- a/tests/test_http_push.py +++ b/tests/test_http_push.py @@ -5,33 +5,59 @@ import tests.events as events from tests import framework +oidc_config_uri = ( "https://identity.dev.disruptive-technologies.com/" + "data-connector/.well-known/openid-configuration" +) class TestHttpPush(): - def test_decode_secret_empty_string(self): + def test_decode_secret_invalid_type(self): + with pytest.raises(TypeError): + test_event = events.touch + data_connector.HttpPush( + headers={test_event.headers}, + body=b'', + secret=22, + ) + + def test_decode_missing_header_token(self): with pytest.raises(disruptive.errors.ConfigurationError): data_connector.HttpPush( headers={}, body=b'', - secret='', + secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) - - def test_decode_secret_invalid_type(self): - with pytest.raises(TypeError): + + def test_decode_missing_header_org_id(self): + with pytest.raises(disruptive.errors.ConfigurationError): data_connector.HttpPush( headers={}, body=b'', - secret=22, + secret='test-secret', + oidc_config_uri=oidc_config_uri, ) - - def test_decode_missing_header_token(self): + + def test_decode_missing_header_oidc_config_uri(self): with pytest.raises(disruptive.errors.ConfigurationError): data_connector.HttpPush( headers={}, body=b'', secret='test-secret', + org_id='test-org-id', + ) + def test_wrong_org_id(self): + with pytest.raises(disruptive.errors.ConfigurationError): + data_connector.HttpPush( + headers=events.touch.headers, + body=events.touch.body_str.encode('utf-8'), + secret='test-secret', + org_id='wrong-org-id', + oidc_config_uri=oidc_config_uri, ) + def test_decode_expired_signature(self): test_event = events.touch with pytest.raises(disruptive.errors.ConfigurationError): @@ -39,6 +65,8 @@ def test_decode_expired_signature(self): headers=test_event.headers, body=test_event.body_str.encode('utf-8'), secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_checksum_mismatch(self, decode_mock): @@ -57,6 +85,8 @@ def test_decode_checksum_mismatch(self, decode_mock): headers=test_event.headers, body=body_str.encode('utf-8'), secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) # ------------------------- Flask ------------------------- @@ -72,6 +102,8 @@ def test_decode_flask(self, decode_mock): request=framework.FlaskRequestFormat(test_event), provider=provider.FLASK, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -89,6 +121,8 @@ def test_decode_temperature(self, decode_mock): request=framework.FlaskRequestFormat(test_event), provider=provider.FLASK, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -106,6 +140,8 @@ def test_decode_flask_name_casing(self, decode_mock): request=framework.FlaskRequestFormat(test_event), provider='fLAsk', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_flask_bad_secret(self): @@ -114,6 +150,8 @@ def test_decode_flask_bad_secret(self): request=framework.FlaskRequestFormat(events.touch), provider=provider.FLASK, secret='bad-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_flask_bad_name(self): @@ -122,6 +160,8 @@ def test_decode_flask_bad_name(self): request=framework.FlaskRequestFormat(events.touch), provider='Xflask', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) # ------------------------- Django ------------------------- @@ -137,6 +177,8 @@ def test_decode_django(self, decode_mock): request=framework.DjangoRequestFormat(test_event), provider=provider.DJANGO, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -154,6 +196,8 @@ def test_decode_temperature(self, decode_mock): request=framework.DjangoRequestFormat(test_event), provider=provider.DJANGO, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -171,6 +215,8 @@ def test_decode_django_name_casing(self, decode_mock): request=framework.DjangoRequestFormat(test_event), provider='djANgO', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_django_bad_secret(self): @@ -179,6 +225,8 @@ def test_decode_django_bad_secret(self): request=framework.DjangoRequestFormat(events.touch), provider=provider.DJANGO, secret='bad-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_django_bad_name(self): @@ -187,6 +235,8 @@ def test_decode_django_bad_name(self): request=framework.DjangoRequestFormat(events.touch), provider='Xdjango', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) # ------------------------- Gcloud ------------------------- @@ -202,6 +252,8 @@ def test_decode_gcloud(self, decode_mock): request=framework.GcloudRequestFormat(test_event), provider=provider.GCLOUD, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -219,6 +271,8 @@ def test_decode_temperature(self, decode_mock): request=framework.GcloudRequestFormat(test_event), provider=provider.GCLOUD, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -236,6 +290,8 @@ def test_decode_gcloud_name_casing(self, decode_mock): request=framework.GcloudRequestFormat(test_event), provider='GcLouD', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_gcloud_bad_secret(self): @@ -244,6 +300,7 @@ def test_decode_gcloud_bad_secret(self): request=framework.GcloudRequestFormat(events.touch), provider=provider.GCLOUD, secret='bad-secret', + oidc_config_uri=oidc_config_uri, ) def test_decode_gcloud_bad_name(self): @@ -252,6 +309,8 @@ def test_decode_gcloud_bad_name(self): request=framework.GcloudRequestFormat(events.touch), provider='Xgcloud', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) # ------------------------- Lambda ------------------------- @@ -267,6 +326,8 @@ def test_decode_lambda(self, decode_mock): request=framework.lambda_request_format(test_event), provider=provider.LAMBDA, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -284,6 +345,7 @@ def test_decode_temperature(self, decode_mock): request=framework.lambda_request_format(test_event), provider=provider.LAMBDA, secret='test-secret', + org_id='test-org-id', ) assert isinstance(payload.event, disruptive.events.Event) @@ -301,6 +363,8 @@ def test_decode_lambda_name_casing(self, decode_mock): request=framework.lambda_request_format(test_event), provider='lAMbdA', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_lambda_bad_secret(self): @@ -309,6 +373,8 @@ def test_decode_lambda_bad_secret(self): request=framework.lambda_request_format(events.touch), provider=provider.LAMBDA, secret='bad-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_lambda_bad_name(self): @@ -317,6 +383,8 @@ def test_decode_lambda_bad_name(self): request=framework.lambda_request_format(events.touch), provider='Xlambda', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) # ------------------------- Azure ------------------------- @@ -332,6 +400,8 @@ def test_decode_azure(self, decode_mock): request=framework.AzureRequestFormat(test_event), provider=provider.AZURE, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -349,6 +419,8 @@ def test_decode_temperature(self, decode_mock): request=framework.AzureRequestFormat(test_event), provider=provider.AZURE, secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) assert isinstance(payload.event, disruptive.events.Event) @@ -366,6 +438,8 @@ def test_decode_azure_name_casing(self, decode_mock): request=framework.AzureRequestFormat(test_event), provider='AzuRE', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_azure_bad_secret(self): @@ -374,6 +448,8 @@ def test_decode_azure_bad_secret(self): request=framework.AzureRequestFormat(events.touch), provider=provider.AZURE, secret='bad-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) def test_decode_azure_bad_name(self): @@ -382,4 +458,6 @@ def test_decode_azure_bad_name(self): request=framework.AzureRequestFormat(events.touch), provider='Xazure', secret='test-secret', + org_id='test-org-id', + oidc_config_uri=oidc_config_uri, ) diff --git a/tests/test_outputs.py b/tests/test_outputs.py index ab97385..58947f1 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -3,6 +3,9 @@ from tests import framework +oidc_config_uri = ( "https://identity.dev.disruptive-technologies.com/" + "data-connector/.well-known/openid-configuration" +) class TestOutputs(): @@ -18,6 +21,7 @@ def test_HttpPush_dunder(self, decode_mock): request=framework.FlaskRequestFormat(test_event), provider=dtintegrations.provider.FLASK, secret='test-secret', + oidc_config_uri=oidc_config_uri, ) print(payload) @@ -34,6 +38,7 @@ def test_HttpPush_dunder_eval(self, decode_mock): request=framework.FlaskRequestFormat(test_event), provider=dtintegrations.provider.FLASK, secret='test-secret', + oidc_config_uri=oidc_config_uri, ) - eval(repr(payload)) + eval(repr(payload)) \ No newline at end of file