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