From 6d04a6af696c9b500b09834787c97ffe33a821ec Mon Sep 17 00:00:00 2001 From: Laszlo Szomor Date: Sun, 6 Aug 2023 19:56:52 +0200 Subject: [PATCH 1/5] Add HMAC verification support to webhook plugin --- .../eda/plugins/event_source/webhook.py | 79 +++++- .../test_webhook_hmac_rules.yml | 19 ++ .../test_webhook_source.py | 61 +++++ tests/unit/event_source/test_webhook.py | 241 ++++++++++++++++++ 4 files changed, 396 insertions(+), 4 deletions(-) create mode 100644 tests/integration/event_source_webhook/test_webhook_hmac_rules.yml diff --git a/extensions/eda/plugins/event_source/webhook.py b/extensions/eda/plugins/event_source/webhook.py index d0d672a7..81790918 100644 --- a/extensions/eda/plugins/event_source/webhook.py +++ b/extensions/eda/plugins/event_source/webhook.py @@ -12,10 +12,21 @@ certfile: The optional path to a certificate file to enable TLS support keyfile: The optional path to a key file to be used together with certfile password: The optional password to be used when loading the certificate chain + hmac_secret: The optional HMAC secret used to verify the payload from the client + hmac_algo: The optional HMAC algorithm used to calculate the payload hash. + See your python's hashlib.algorithms_available set for available options. + Defaults to sha256 + hmac_header: The optional HMAC header sent by the client with the payload signature. + Defaults to x-hub-signature-256 + hmac_format: The optional HMAC signature format format. Supported formats: hex, base64 + Defaults to hex """ import asyncio +import base64 +import hashlib +import hmac import json import logging import ssl @@ -56,6 +67,36 @@ def _parse_token(request: web.Request) -> (str, str): return scheme, token +async def _hmac_verify(request: web.Request) -> bool: + result = False + + hmac_secret = request.app["hmac_secret"] + hmac_header = request.app["hmac_header"] + hmac_algo = request.app["hmac_algo"] + hmac_format = request.app["hmac_format"] + + if hmac_header not in request.headers: + raise web.HTTPBadRequest(text="Signature header not found") + + hmac_header_digest = request.headers[hmac_header].strip() + + if hmac_header_digest.startswith(f"{hmac_algo}="): + hmac_header_digest = hmac_header_digest[len(f"{hmac_algo}="):] + + body = await request.text() + + event_hmac = hmac.new(key=hmac_secret, + msg=body.encode("utf-8"), digestmod=hmac_algo) + if hmac_format == "base64": + event_digest = base64.b64encode(event_hmac.digest()).decode("utf-8") + elif hmac_format == "hex": + event_digest = event_hmac.hexdigest() + + result = hmac.compare_digest(hmac_header_digest, event_digest) + + return result + + @web.middleware async def bearer_auth(request: web.Request, handler: Callable) -> web.StreamResponse: """Verify authorization is Bearer type.""" @@ -69,16 +110,46 @@ async def bearer_auth(request: web.Request, handler: Callable) -> web.StreamResp return await handler(request) +@web.middleware +async def hmac_verify(request: web.Request, handler: Callable) -> web.StreamResponse: + """Verify event's HMAC signature.""" + hmac_verified = await _hmac_verify(request) + if not hmac_verified: + raise web.HTTPUnauthorized(text="HMAC verification failed") + + return await handler(request) + + async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: """Receive events via webhook.""" if "port" not in args: msg = "Missing required argument: port" raise ValueError(msg) + + middlewares = [] + app_attrs = {} + if "token" in args: - app = web.Application(middlewares=[bearer_auth]) - app["token"] = args["token"] - else: - app = web.Application() + middlewares.append(bearer_auth) + app_attrs["token"] = args["token"] + + if "hmac_secret" in args: + middlewares.append(hmac_verify) + + app_attrs["hmac_secret"] = args["hmac_secret"].encode("utf-8") + app_attrs["hmac_algo"] = args.get("hmac_algo", "sha256") + if app_attrs["hmac_algo"] not in hashlib.algorithms_available: + msg = f"Unsupported HMAC algorithm: {app_attrs['hmac_algo']}" + raise ValueError(msg) + app_attrs["hmac_header"] = args.get("hmac_header", "x-hub-signature-256") + app_attrs["hmac_format"] = args.get("hmac_format", "hex") + if app_attrs["hmac_format"] not in ["hex", "base64"]: + msg = f"Unsupported HMAC header format {app_attrs['hmac_format']}" + raise ValueError(msg) + + app = web.Application(middlewares=middlewares) + for (key, value) in app_attrs.items(): + app[key] = value app["queue"] = queue app.add_routes(routes) diff --git a/tests/integration/event_source_webhook/test_webhook_hmac_rules.yml b/tests/integration/event_source_webhook/test_webhook_hmac_rules.yml new file mode 100644 index 00000000..6986c231 --- /dev/null +++ b/tests/integration/event_source_webhook/test_webhook_hmac_rules.yml @@ -0,0 +1,19 @@ +--- +- name: test webhook source plugin + hosts: localhost + sources: + - ansible.eda.webhook: + port: "{{ WH_PORT | default(5000) }}" + hmac_secret: "{{ HMAC_SECRET }}" + hmac_algo: "{{ HMAC_ALGO }}" + rules: + - name: match webhook event + condition: event.payload.ping == "pong" + action: + debug: + msg: "Rule fired successfully" + + - name: shutdown + condition: event.payload.shutdown is defined + action: + shutdown: diff --git a/tests/integration/event_source_webhook/test_webhook_source.py b/tests/integration/event_source_webhook/test_webhook_source.py index b8162529..f0adee2d 100644 --- a/tests/integration/event_source_webhook/test_webhook_source.py +++ b/tests/integration/event_source_webhook/test_webhook_source.py @@ -86,3 +86,64 @@ def test_webhook_source_with_busy_port(subprocess_teardown): stdout, _unused_stderr = proc2.communicate() assert "address already in use" in stdout.decode() assert proc2.returncode == 1 + + +def test_webhook_source_hmac_sanity(subprocess_teardown): + """ + Check the successful execution, response and shutdown + of the webhook source plugin. + """ + msgs = [ + (json.dumps({"ping": "pong"}).encode("ascii"), 'sha256=23fff24c4b9835c6179de19103c6c640150d07d8a72c987b030b541a9d988736'), + (json.dumps({"shutdown": ""}).encode("ascii"), '185e5a6124894d6fed1c69c8bea049da241adec83b468c867c4e83627e64d9b9') + ] + + port = 5000 + url = f"http://127.0.0.1:{port}/webhook" + + env = os.environ.copy() + env["WH_PORT"] = str(port) + env["HMAC_SECRET"] = "secret" + env["HMAC_ALGO"] = "sha256" + + rules_file = TESTS_PATH + "/event_source_webhook/test_webhook_hmac_rules.yml" + + proc = CLIRunner( + rules=rules_file, envvars="WH_PORT,HMAC_SECRET,HMAC_ALGO", env=env, debug=True + ).run_in_background() + subprocess_teardown(proc) + + wait_for_events(proc) + + for msg, digest in msgs: + headers = {"x-hub-signature-256": digest} + requests.post(url, data=msg, headers=headers) + + try: + stdout, _unused_stderr = proc.communicate(timeout=5) + except subprocess.TimeoutExpired: + proc.terminate() + stdout, _unused_stderr = proc.communicate() + + assert "Rule fired successfully" in stdout.decode() + assert f"'Host': '127.0.0.1:{port}'" in stdout.decode() + assert proc.returncode == 0 + + +def test_webhook_source_with_unsupported_hmac_algo(subprocess_teardown): + """ + Ensure the CLI responds correctly if the desired HMAC algorithm is not supported. + """ + + port = 5000 + env = os.environ.copy() + env["WH_PORT"] = str(port) + env["HMAC_SECRET"] = "secret" + env["HMAC_ALGO"] = "invalid_hmac_algo" + + rules_file = TESTS_PATH + "/event_source_webhook/test_webhook_hmac_rules.yml" + proc = CLIRunner(rules=rules_file, envvars="WH_PORT,HMAC_SECRET,HMAC_ALGO", env=env, debug=True).run_in_background() + proc.wait(timeout=15) + stdout, _unused_stderr = proc.communicate() + assert f"Unsupported HMAC algorithm: {env['HMAC_ALGO']}" in stdout.decode() + assert proc.returncode == 1 diff --git a/tests/unit/event_source/test_webhook.py b/tests/unit/event_source/test_webhook.py index 4fa9fdb7..d062c124 100644 --- a/tests/unit/event_source/test_webhook.py +++ b/tests/unit/event_source/test_webhook.py @@ -23,6 +23,25 @@ async def post_code(server_task, info): server_task.cancel() +async def assert_post(server_task, info, expected_status=HTTPStatus.OK, expected_text=None): + url = f'http://{info["host"]}/{info["endpoint"]}' + payload = info["payload"] + headers = {} + + if "token" in info: + headers["Authorization"] = f"Bearer {info['token']}" + + if "hmac_header" in info: + headers[info['hmac_header']] = info['hmac_digest'] + + async with aiohttp.ClientSession() as session: + async with session.post(url, json=payload, headers=headers) as resp: + server_task.cancel() + assert resp.status == expected_status + if expected_text: + assert expected_text in await resp.text() + + async def cancel_code(server_task): server_task.cancel() @@ -77,3 +96,225 @@ async def do_request(): plugin_task = asyncio.create_task(start_server(queue, args)) request_task = asyncio.create_task(do_request()) await asyncio.gather(plugin_task, request_task) + + +@pytest.mark.asyncio +async def test_post_hmac_hex_endpoint(): + queue = asyncio.Queue() + + args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", + "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", + "hmac_format": "hex"} + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": args["hmac_header"], + "hmac_digest": "sha256=9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info)) + + await asyncio.gather(plugin_task, post_task) + + data = await queue.get() + assert data["payload"] == task_info["payload"] + assert data["meta"]["endpoint"] == task_info["endpoint"] + assert data["meta"]["headers"]["Host"] == task_info["host"] + + +@pytest.mark.asyncio +async def test_post_hmac_hex_wo_digest_prefix_endpoint(): + queue = asyncio.Queue() + + args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", + "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", + "hmac_format": "hex"} + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": args["hmac_header"], + "hmac_digest": "9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info)) + + await asyncio.gather(plugin_task, post_task) + + data = await queue.get() + assert data["payload"] == task_info["payload"] + assert data["meta"]["endpoint"] == task_info["endpoint"] + assert data["meta"]["headers"]["Host"] == task_info["host"] + + +@pytest.mark.asyncio +async def test_post_hmac_hex_endpoint_invalid_signature(): + queue = asyncio.Queue() + + args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", + "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", + "hmac_format": "hex"} + + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": args["hmac_header"], + "hmac_digest": "sha256=11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED)) + + await asyncio.gather(plugin_task, post_task) + + +@pytest.mark.asyncio +async def test_post_hmac_hex_endpoint_missing_signature(): + queue = asyncio.Queue() + + args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", + "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", + "hmac_format": "hex"} + + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": "x-not-a-signature-header", + "hmac_digest": "sha256=205009e3e895e0fe0ff982e1020dd0fb4b6d16cf9c666652b3492e20429ccdb8", + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.BAD_REQUEST)) + + await asyncio.gather(plugin_task, post_task) + + +@pytest.mark.asyncio +async def test_post_hmac_base64_endpoint(): + queue = asyncio.Queue() + + args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", + "hmac_algo": "sha256", "hmac_header": "x-custom-signature", + "hmac_format": "base64"} + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": args["hmac_header"], + "hmac_digest": "sha256=nsgnKTejaktEJ9T5q3sEJYVsXvXX4bSW+GSq+ZwZEMo=", + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info)) + + await asyncio.gather(plugin_task, post_task) + + data = await queue.get() + assert data["payload"] == task_info["payload"] + assert data["meta"]["endpoint"] == task_info["endpoint"] + assert data["meta"]["headers"]["Host"] == task_info["host"] + + +@pytest.mark.asyncio +async def test_post_hmac_base64_endpoint_invalid_signature(): + queue = asyncio.Queue() + + args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", + "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", + "hmac_format": "hex"} + + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": args["hmac_header"], + "hmac_digest": "nsgnKTejaktEJ9T5q3sEJYVsXvXX4bSW+GSq+ZwZEMo=", + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED)) + + await asyncio.gather(plugin_task, post_task) + + +@pytest.mark.asyncio +async def test_post_token_and_hmac_hex_endpoint(): + queue = asyncio.Queue() + + args = {"host": "127.0.0.1", "port": 8000, "token": "secret", "hmac_secret": "secret"} + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": "x-hub-signature-256", + "hmac_digest": "sha256=9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", + "token": args["token"], + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info)) + + await asyncio.gather(plugin_task, post_task) + + data = await queue.get() + assert data["payload"] == task_info["payload"] + assert data["meta"]["endpoint"] == task_info["endpoint"] + assert data["meta"]["headers"]["Host"] == task_info["host"] + + +@pytest.mark.asyncio +async def test_post_token_and_hmac_hex_endpoint_invalid_signature(): + queue = asyncio.Queue() + + args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", "hmac_secret": "secret"} + + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": "x-hub-signature-256", + "hmac_digest": "11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", + "token": args["token"], + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED, + expected_text="HMAC verification failed")) + + await asyncio.gather(plugin_task, post_task) + + +@pytest.mark.asyncio +async def test_post_token_and_hmac_hex_endpoint_invalid_token(): + queue = asyncio.Queue() + + args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", "hmac_secret": "secret"} + + plugin_task = asyncio.create_task(start_server(queue, args)) + + task_info = { + "payload": {"src_path": "https://example.com/payload"}, + "hmac_header": "x-hub-signature-256", + "hmac_digest": "11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", + "token": "invalid_token", + "endpoint": "test", + "host": f'{args["host"]}:{args["port"]}', + } + + post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED, + expected_text="Invalid authorization token")) + + await asyncio.gather(plugin_task, post_task) From da63e77a73cb1f1b0b47fc6e40a3ae53c5c2b146 Mon Sep 17 00:00:00 2001 From: Laszlo Szomor Date: Mon, 7 Aug 2023 06:42:33 +0200 Subject: [PATCH 2/5] Fix Flake8 and Tox checks --- .../eda/plugins/event_source/webhook.py | 9 ++-- .../test_webhook_source.py | 9 ++-- tests/unit/event_source/test_webhook.py | 47 ++++++++++++------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/extensions/eda/plugins/event_source/webhook.py b/extensions/eda/plugins/event_source/webhook.py index 81790918..f9aa57be 100644 --- a/extensions/eda/plugins/event_source/webhook.py +++ b/extensions/eda/plugins/event_source/webhook.py @@ -18,7 +18,8 @@ Defaults to sha256 hmac_header: The optional HMAC header sent by the client with the payload signature. Defaults to x-hub-signature-256 - hmac_format: The optional HMAC signature format format. Supported formats: hex, base64 + hmac_format: The optional HMAC signature format format. + Supported formats: hex, base64 Defaults to hex """ @@ -68,8 +69,6 @@ def _parse_token(request: web.Request) -> (str, str): async def _hmac_verify(request: web.Request) -> bool: - result = False - hmac_secret = request.app["hmac_secret"] hmac_header = request.app["hmac_header"] hmac_algo = request.app["hmac_algo"] @@ -92,9 +91,7 @@ async def _hmac_verify(request: web.Request) -> bool: elif hmac_format == "hex": event_digest = event_hmac.hexdigest() - result = hmac.compare_digest(hmac_header_digest, event_digest) - - return result + return hmac.compare_digest(hmac_header_digest, event_digest) @web.middleware diff --git a/tests/integration/event_source_webhook/test_webhook_source.py b/tests/integration/event_source_webhook/test_webhook_source.py index f0adee2d..ee43a785 100644 --- a/tests/integration/event_source_webhook/test_webhook_source.py +++ b/tests/integration/event_source_webhook/test_webhook_source.py @@ -94,8 +94,10 @@ def test_webhook_source_hmac_sanity(subprocess_teardown): of the webhook source plugin. """ msgs = [ - (json.dumps({"ping": "pong"}).encode("ascii"), 'sha256=23fff24c4b9835c6179de19103c6c640150d07d8a72c987b030b541a9d988736'), - (json.dumps({"shutdown": ""}).encode("ascii"), '185e5a6124894d6fed1c69c8bea049da241adec83b468c867c4e83627e64d9b9') + (json.dumps({"ping": "pong"}).encode("ascii"), + 'sha256=23fff24c4b9835c6179de19103c6c640150d07d8a72c987b030b541a9d988736'), + (json.dumps({"shutdown": ""}).encode("ascii"), + '185e5a6124894d6fed1c69c8bea049da241adec83b468c867c4e83627e64d9b9') ] port = 5000 @@ -142,7 +144,8 @@ def test_webhook_source_with_unsupported_hmac_algo(subprocess_teardown): env["HMAC_ALGO"] = "invalid_hmac_algo" rules_file = TESTS_PATH + "/event_source_webhook/test_webhook_hmac_rules.yml" - proc = CLIRunner(rules=rules_file, envvars="WH_PORT,HMAC_SECRET,HMAC_ALGO", env=env, debug=True).run_in_background() + proc = CLIRunner(rules=rules_file, envvars="WH_PORT,HMAC_SECRET,HMAC_ALGO", env=env, + debug=True).run_in_background() proc.wait(timeout=15) stdout, _unused_stderr = proc.communicate() assert f"Unsupported HMAC algorithm: {env['HMAC_ALGO']}" in stdout.decode() diff --git a/tests/unit/event_source/test_webhook.py b/tests/unit/event_source/test_webhook.py index d062c124..88589647 100644 --- a/tests/unit/event_source/test_webhook.py +++ b/tests/unit/event_source/test_webhook.py @@ -23,7 +23,8 @@ async def post_code(server_task, info): server_task.cancel() -async def assert_post(server_task, info, expected_status=HTTPStatus.OK, expected_text=None): +async def assert_post(server_task, info, expected_status=HTTPStatus.OK, + expected_text=None): url = f'http://{info["host"]}/{info["endpoint"]}' payload = info["payload"] headers = {} @@ -110,7 +111,7 @@ async def test_post_hmac_hex_endpoint(): task_info = { "payload": {"src_path": "https://example.com/payload"}, "hmac_header": args["hmac_header"], - "hmac_digest": "sha256=9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", + "hmac_digest": "sha256=9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", # noqa: E501 "endpoint": "test", "host": f'{args["host"]}:{args["port"]}', } @@ -137,7 +138,7 @@ async def test_post_hmac_hex_wo_digest_prefix_endpoint(): task_info = { "payload": {"src_path": "https://example.com/payload"}, "hmac_header": args["hmac_header"], - "hmac_digest": "9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", + "hmac_digest": "9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", # noqa: E501 "endpoint": "test", "host": f'{args["host"]}:{args["port"]}', } @@ -165,12 +166,13 @@ async def test_post_hmac_hex_endpoint_invalid_signature(): task_info = { "payload": {"src_path": "https://example.com/payload"}, "hmac_header": args["hmac_header"], - "hmac_digest": "sha256=11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", + "hmac_digest": "sha256=11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", # noqa: E501 "endpoint": "test", "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED)) + post_task = asyncio.create_task(assert_post(plugin_task, task_info, + HTTPStatus.UNAUTHORIZED)) await asyncio.gather(plugin_task, post_task) @@ -188,12 +190,13 @@ async def test_post_hmac_hex_endpoint_missing_signature(): task_info = { "payload": {"src_path": "https://example.com/payload"}, "hmac_header": "x-not-a-signature-header", - "hmac_digest": "sha256=205009e3e895e0fe0ff982e1020dd0fb4b6d16cf9c666652b3492e20429ccdb8", + "hmac_digest": "sha256=205009e3e895e0fe0ff982e1020dd0fb4b6d16cf9c666652b3492e20429ccdb8", # noqa: E501 "endpoint": "test", "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.BAD_REQUEST)) + post_task = asyncio.create_task(assert_post(plugin_task, task_info, + HTTPStatus.BAD_REQUEST)) await asyncio.gather(plugin_task, post_task) @@ -243,7 +246,8 @@ async def test_post_hmac_base64_endpoint_invalid_signature(): "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED)) + post_task = asyncio.create_task(assert_post(plugin_task, task_info, + HTTPStatus.UNAUTHORIZED)) await asyncio.gather(plugin_task, post_task) @@ -252,13 +256,14 @@ async def test_post_hmac_base64_endpoint_invalid_signature(): async def test_post_token_and_hmac_hex_endpoint(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "token": "secret", "hmac_secret": "secret"} + args = {"host": "127.0.0.1", "port": 8000, "token": "secret", + "hmac_secret": "secret"} plugin_task = asyncio.create_task(start_server(queue, args)) task_info = { "payload": {"src_path": "https://example.com/payload"}, "hmac_header": "x-hub-signature-256", - "hmac_digest": "sha256=9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", + "hmac_digest": "sha256=9ec8272937a36a4b4427d4f9ab7b0425856c5ef5d7e1b496f864aaf99c1910ca", # noqa: E501 "token": args["token"], "endpoint": "test", "host": f'{args["host"]}:{args["port"]}', @@ -278,21 +283,24 @@ async def test_post_token_and_hmac_hex_endpoint(): async def test_post_token_and_hmac_hex_endpoint_invalid_signature(): queue = asyncio.Queue() - args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", "hmac_secret": "secret"} + args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", + "hmac_secret": "secret"} plugin_task = asyncio.create_task(start_server(queue, args)) task_info = { "payload": {"src_path": "https://example.com/payload"}, "hmac_header": "x-hub-signature-256", - "hmac_digest": "11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", + "hmac_digest": "11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", # noqa: E501 "token": args["token"], "endpoint": "test", "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED, - expected_text="HMAC verification failed")) + expected_text = "HMAC verification failed" + post_task = asyncio.create_task(assert_post(plugin_task, task_info, + HTTPStatus.UNAUTHORIZED, + expected_text)) await asyncio.gather(plugin_task, post_task) @@ -301,20 +309,23 @@ async def test_post_token_and_hmac_hex_endpoint_invalid_signature(): async def test_post_token_and_hmac_hex_endpoint_invalid_token(): queue = asyncio.Queue() - args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", "hmac_secret": "secret"} + args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", + "hmac_secret": "secret"} plugin_task = asyncio.create_task(start_server(queue, args)) task_info = { "payload": {"src_path": "https://example.com/payload"}, "hmac_header": "x-hub-signature-256", - "hmac_digest": "11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", + "hmac_digest": "11f8feeab79372c842f0097fc105dd66d90c41412ab9d3c4071859d7b6ae864b", # noqa: E501 "token": "invalid_token", "endpoint": "test", "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED, - expected_text="Invalid authorization token")) + expected_text = "Invalid authorization token" + post_task = asyncio.create_task(assert_post(plugin_task, task_info, + HTTPStatus.UNAUTHORIZED, + expected_text)) await asyncio.gather(plugin_task, post_task) From 6ada0c65386b5fc2e898fca24948dff8834104a8 Mon Sep 17 00:00:00 2001 From: Laszlo Szomor Date: Tue, 8 Aug 2023 19:25:18 +0200 Subject: [PATCH 3/5] Apply black recommendations --- .../eda/plugins/event_source/webhook.py | 12 +- .../test_webhook_source.py | 17 ++- tests/unit/event_source/test_webhook.py | 128 ++++++++++++------ 3 files changed, 108 insertions(+), 49 deletions(-) diff --git a/extensions/eda/plugins/event_source/webhook.py b/extensions/eda/plugins/event_source/webhook.py index f9aa57be..d1fa3638 100644 --- a/extensions/eda/plugins/event_source/webhook.py +++ b/extensions/eda/plugins/event_source/webhook.py @@ -80,12 +80,16 @@ async def _hmac_verify(request: web.Request) -> bool: hmac_header_digest = request.headers[hmac_header].strip() if hmac_header_digest.startswith(f"{hmac_algo}="): - hmac_header_digest = hmac_header_digest[len(f"{hmac_algo}="):] + hmac_prefix_len = len(f"{hmac_algo}=") + hmac_header_digest = hmac_header_digest[hmac_prefix_len:] body = await request.text() - event_hmac = hmac.new(key=hmac_secret, - msg=body.encode("utf-8"), digestmod=hmac_algo) + event_hmac = hmac.new( + key=hmac_secret, + msg=body.encode("utf-8"), + digestmod=hmac_algo, + ) if hmac_format == "base64": event_digest = base64.b64encode(event_hmac.digest()).decode("utf-8") elif hmac_format == "hex": @@ -145,7 +149,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: raise ValueError(msg) app = web.Application(middlewares=middlewares) - for (key, value) in app_attrs.items(): + for key, value in app_attrs.items(): app[key] = value app["queue"] = queue diff --git a/tests/integration/event_source_webhook/test_webhook_source.py b/tests/integration/event_source_webhook/test_webhook_source.py index ee43a785..0a65bef6 100644 --- a/tests/integration/event_source_webhook/test_webhook_source.py +++ b/tests/integration/event_source_webhook/test_webhook_source.py @@ -94,10 +94,14 @@ def test_webhook_source_hmac_sanity(subprocess_teardown): of the webhook source plugin. """ msgs = [ - (json.dumps({"ping": "pong"}).encode("ascii"), - 'sha256=23fff24c4b9835c6179de19103c6c640150d07d8a72c987b030b541a9d988736'), - (json.dumps({"shutdown": ""}).encode("ascii"), - '185e5a6124894d6fed1c69c8bea049da241adec83b468c867c4e83627e64d9b9') + ( + json.dumps({"ping": "pong"}).encode("ascii"), + "sha256=23fff24c4b9835c6179de19103c6c640150d07d8a72c987b030b541a9d988736", + ), + ( + json.dumps({"shutdown": ""}).encode("ascii"), + "185e5a6124894d6fed1c69c8bea049da241adec83b468c867c4e83627e64d9b9", + ), ] port = 5000 @@ -144,8 +148,9 @@ def test_webhook_source_with_unsupported_hmac_algo(subprocess_teardown): env["HMAC_ALGO"] = "invalid_hmac_algo" rules_file = TESTS_PATH + "/event_source_webhook/test_webhook_hmac_rules.yml" - proc = CLIRunner(rules=rules_file, envvars="WH_PORT,HMAC_SECRET,HMAC_ALGO", env=env, - debug=True).run_in_background() + proc = CLIRunner( + rules=rules_file, envvars="WH_PORT,HMAC_SECRET,HMAC_ALGO", env=env, debug=True + ).run_in_background() proc.wait(timeout=15) stdout, _unused_stderr = proc.communicate() assert f"Unsupported HMAC algorithm: {env['HMAC_ALGO']}" in stdout.decode() diff --git a/tests/unit/event_source/test_webhook.py b/tests/unit/event_source/test_webhook.py index 88589647..70e34c33 100644 --- a/tests/unit/event_source/test_webhook.py +++ b/tests/unit/event_source/test_webhook.py @@ -23,8 +23,9 @@ async def post_code(server_task, info): server_task.cancel() -async def assert_post(server_task, info, expected_status=HTTPStatus.OK, - expected_text=None): +async def assert_post( + server_task, info, expected_status=HTTPStatus.OK, expected_text=None +): url = f'http://{info["host"]}/{info["endpoint"]}' payload = info["payload"] headers = {} @@ -33,7 +34,7 @@ async def assert_post(server_task, info, expected_status=HTTPStatus.OK, headers["Authorization"] = f"Bearer {info['token']}" if "hmac_header" in info: - headers[info['hmac_header']] = info['hmac_digest'] + headers[info["hmac_header"]] = info["hmac_digest"] async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: @@ -103,9 +104,15 @@ async def do_request(): async def test_post_hmac_hex_endpoint(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", - "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", - "hmac_format": "hex"} + args = { + "host": "127.0.0.1", + "port": 8000, + "hmac_secret": "secret", + "hmac_algo": "sha256", + "hmac_header": "x-hub-signature-256", + "hmac_format": "hex", + } + plugin_task = asyncio.create_task(start_server(queue, args)) task_info = { @@ -130,9 +137,15 @@ async def test_post_hmac_hex_endpoint(): async def test_post_hmac_hex_wo_digest_prefix_endpoint(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", - "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", - "hmac_format": "hex"} + args = { + "host": "127.0.0.1", + "port": 8000, + "hmac_secret": "secret", + "hmac_algo": "sha256", + "hmac_header": "x-hub-signature-256", + "hmac_format": "hex", + } + plugin_task = asyncio.create_task(start_server(queue, args)) task_info = { @@ -157,9 +170,14 @@ async def test_post_hmac_hex_wo_digest_prefix_endpoint(): async def test_post_hmac_hex_endpoint_invalid_signature(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", - "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", - "hmac_format": "hex"} + args = { + "host": "127.0.0.1", + "port": 8000, + "hmac_secret": "secret", + "hmac_algo": "sha256", + "hmac_header": "x-hub-signature-256", + "hmac_format": "hex", + } plugin_task = asyncio.create_task(start_server(queue, args)) @@ -171,8 +189,9 @@ async def test_post_hmac_hex_endpoint_invalid_signature(): "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, - HTTPStatus.UNAUTHORIZED)) + post_task = asyncio.create_task( + assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED) + ) await asyncio.gather(plugin_task, post_task) @@ -181,9 +200,14 @@ async def test_post_hmac_hex_endpoint_invalid_signature(): async def test_post_hmac_hex_endpoint_missing_signature(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", - "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", - "hmac_format": "hex"} + args = { + "host": "127.0.0.1", + "port": 8000, + "hmac_secret": "secret", + "hmac_algo": "sha256", + "hmac_header": "x-hub-signature-256", + "hmac_format": "hex", + } plugin_task = asyncio.create_task(start_server(queue, args)) @@ -195,8 +219,9 @@ async def test_post_hmac_hex_endpoint_missing_signature(): "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, - HTTPStatus.BAD_REQUEST)) + post_task = asyncio.create_task( + assert_post(plugin_task, task_info, HTTPStatus.BAD_REQUEST) + ) await asyncio.gather(plugin_task, post_task) @@ -205,9 +230,15 @@ async def test_post_hmac_hex_endpoint_missing_signature(): async def test_post_hmac_base64_endpoint(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", - "hmac_algo": "sha256", "hmac_header": "x-custom-signature", - "hmac_format": "base64"} + args = { + "host": "127.0.0.1", + "port": 8000, + "hmac_secret": "secret", + "hmac_algo": "sha256", + "hmac_header": "x-custom-signature", + "hmac_format": "base64", + } + plugin_task = asyncio.create_task(start_server(queue, args)) task_info = { @@ -232,9 +263,14 @@ async def test_post_hmac_base64_endpoint(): async def test_post_hmac_base64_endpoint_invalid_signature(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "hmac_secret": "secret", - "hmac_algo": "sha256", "hmac_header": "x-hub-signature-256", - "hmac_format": "hex"} + args = { + "host": "127.0.0.1", + "port": 8000, + "hmac_secret": "secret", + "hmac_algo": "sha256", + "hmac_header": "x-hub-signature-256", + "hmac_format": "hex", + } plugin_task = asyncio.create_task(start_server(queue, args)) @@ -246,8 +282,9 @@ async def test_post_hmac_base64_endpoint_invalid_signature(): "host": f'{args["host"]}:{args["port"]}', } - post_task = asyncio.create_task(assert_post(plugin_task, task_info, - HTTPStatus.UNAUTHORIZED)) + post_task = asyncio.create_task( + assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED) + ) await asyncio.gather(plugin_task, post_task) @@ -256,8 +293,13 @@ async def test_post_hmac_base64_endpoint_invalid_signature(): async def test_post_token_and_hmac_hex_endpoint(): queue = asyncio.Queue() - args = {"host": "127.0.0.1", "port": 8000, "token": "secret", - "hmac_secret": "secret"} + args = { + "host": "127.0.0.1", + "port": 8000, + "token": "secret", + "hmac_secret": "secret", + } + plugin_task = asyncio.create_task(start_server(queue, args)) task_info = { @@ -283,8 +325,12 @@ async def test_post_token_and_hmac_hex_endpoint(): async def test_post_token_and_hmac_hex_endpoint_invalid_signature(): queue = asyncio.Queue() - args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", - "hmac_secret": "secret"} + args = args = { + "host": "127.0.0.1", + "port": 8000, + "token": "secret", + "hmac_secret": "secret", + } plugin_task = asyncio.create_task(start_server(queue, args)) @@ -298,9 +344,9 @@ async def test_post_token_and_hmac_hex_endpoint_invalid_signature(): } expected_text = "HMAC verification failed" - post_task = asyncio.create_task(assert_post(plugin_task, task_info, - HTTPStatus.UNAUTHORIZED, - expected_text)) + post_task = asyncio.create_task( + assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED, expected_text) + ) await asyncio.gather(plugin_task, post_task) @@ -309,8 +355,12 @@ async def test_post_token_and_hmac_hex_endpoint_invalid_signature(): async def test_post_token_and_hmac_hex_endpoint_invalid_token(): queue = asyncio.Queue() - args = args = {"host": "127.0.0.1", "port": 8000, "token": "secret", - "hmac_secret": "secret"} + args = { + "host": "127.0.0.1", + "port": 8000, + "token": "secret", + "hmac_secret": "secret", + } plugin_task = asyncio.create_task(start_server(queue, args)) @@ -324,8 +374,8 @@ async def test_post_token_and_hmac_hex_endpoint_invalid_token(): } expected_text = "Invalid authorization token" - post_task = asyncio.create_task(assert_post(plugin_task, task_info, - HTTPStatus.UNAUTHORIZED, - expected_text)) + post_task = asyncio.create_task( + assert_post(plugin_task, task_info, HTTPStatus.UNAUTHORIZED, expected_text) + ) await asyncio.gather(plugin_task, post_task) From 7d65d2de297d0698d46f8a8caff877fe94ad568b Mon Sep 17 00:00:00 2001 From: Laszlo Szomor Date: Sun, 13 Aug 2023 15:12:53 +0200 Subject: [PATCH 4/5] Fix tox issues --- .../eda/plugins/event_source/alertmanager.py | 4 +++- .../eda/plugins/event_source/aws_cloudtrail.py | 10 +++++++--- .../eda/plugins/event_source/aws_sqs_queue.py | 4 +++- .../eda/plugins/event_source/azure_service_bus.py | 4 +++- extensions/eda/plugins/event_source/file.py | 14 ++++++++------ extensions/eda/plugins/event_source/generic.py | 4 +++- extensions/eda/plugins/event_source/journald.py | 2 ++ extensions/eda/plugins/event_source/kafka.py | 4 +++- extensions/eda/plugins/event_source/range.py | 4 +++- extensions/eda/plugins/event_source/tick.py | 5 ++++- extensions/eda/plugins/event_source/url_check.py | 4 +++- extensions/eda/plugins/event_source/webhook.py | 9 ++++++--- 12 files changed, 48 insertions(+), 20 deletions(-) diff --git a/extensions/eda/plugins/event_source/alertmanager.py b/extensions/eda/plugins/event_source/alertmanager.py index 7384b347..eff6abde 100644 --- a/extensions/eda/plugins/event_source/alertmanager.py +++ b/extensions/eda/plugins/event_source/alertmanager.py @@ -32,6 +32,8 @@ """ +from __future__ import annotations + import asyncio import logging from typing import Any @@ -144,7 +146,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/aws_cloudtrail.py b/extensions/eda/plugins/event_source/aws_cloudtrail.py index 7847fcdf..56f5c48e 100644 --- a/extensions/eda/plugins/event_source/aws_cloudtrail.py +++ b/extensions/eda/plugins/event_source/aws_cloudtrail.py @@ -31,14 +31,18 @@ """ +from __future__ import annotations + import asyncio import json from datetime import datetime -from typing import Any +from typing import TYPE_CHECKING, Any -from aiobotocore.client import BaseClient from aiobotocore.session import get_session +if TYPE_CHECKING: + from aiobotocore.client import BaseClient + def _cloudtrail_event_to_dict(event: dict) -> dict: event["EventTime"] = event["EventTime"].isoformat() @@ -131,7 +135,7 @@ def connection_args(args: dict[str, Any]) -> dict[str, Any]: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/aws_sqs_queue.py b/extensions/eda/plugins/event_source/aws_sqs_queue.py index 7dd3afb5..90f26383 100644 --- a/extensions/eda/plugins/event_source/aws_sqs_queue.py +++ b/extensions/eda/plugins/event_source/aws_sqs_queue.py @@ -21,6 +21,8 @@ """ +from __future__ import annotations + import asyncio import json import logging @@ -109,7 +111,7 @@ def connection_args(args: dict[str, Any]) -> dict[str, Any]: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/azure_service_bus.py b/extensions/eda/plugins/event_source/azure_service_bus.py index 940e6ec6..3b48ea90 100644 --- a/extensions/eda/plugins/event_source/azure_service_bus.py +++ b/extensions/eda/plugins/event_source/azure_service_bus.py @@ -16,6 +16,8 @@ """ +from __future__ import annotations + import asyncio import concurrent.futures import contextlib @@ -69,7 +71,7 @@ async def main( class MockQueue: """A fake queue.""" - async def put_nowait(self: "MockQueue", event: dict) -> None: + async def put_nowait(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/file.py b/extensions/eda/plugins/event_source/file.py index 09403d98..5d845a0d 100644 --- a/extensions/eda/plugins/event_source/file.py +++ b/extensions/eda/plugins/event_source/file.py @@ -15,6 +15,8 @@ """ +from __future__ import annotations + import pathlib import yaml @@ -60,21 +62,21 @@ def _observe_files(queue, files: list[str]) -> None: # noqa: ANN001 class Handler(RegexMatchingEventHandler): """A handler for file events.""" - def __init__(self: "Handler", **kwargs) -> None: # noqa: ANN003 + def __init__(self: Handler, **kwargs) -> None: # noqa: ANN003 RegexMatchingEventHandler.__init__(self, **kwargs) - def on_created(self: "Handler", event: dict) -> None: + def on_created(self: Handler, event: dict) -> None: if event.src_path in files: send_facts(queue, event.src_path) - def on_deleted(self: "Handler", event: dict) -> None: + def on_deleted(self: Handler, event: dict) -> None: pass - def on_modified(self: "Handler", event: dict) -> None: + def on_modified(self: Handler, event: dict) -> None: if event.src_path in files: send_facts(queue, event.src_path) - def on_moved(self: "Handler", event: dict) -> None: + def on_moved(self: Handler, event: dict) -> None: pass observer = Observer() @@ -98,7 +100,7 @@ def on_moved(self: "Handler", event: dict) -> None: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/generic.py b/extensions/eda/plugins/event_source/generic.py index 3039d0a7..0b033756 100644 --- a/extensions/eda/plugins/event_source/generic.py +++ b/extensions/eda/plugins/event_source/generic.py @@ -41,6 +41,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import asyncio import random import time @@ -128,7 +130,7 @@ def _create_data( class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/journald.py b/extensions/eda/plugins/event_source/journald.py index be3b2486..fb727991 100644 --- a/extensions/eda/plugins/event_source/journald.py +++ b/extensions/eda/plugins/event_source/journald.py @@ -23,6 +23,8 @@ """ +from __future__ import annotations + import asyncio from typing import Any diff --git a/extensions/eda/plugins/event_source/kafka.py b/extensions/eda/plugins/event_source/kafka.py index d3ee5c01..b6512002 100644 --- a/extensions/eda/plugins/event_source/kafka.py +++ b/extensions/eda/plugins/event_source/kafka.py @@ -24,6 +24,8 @@ """ +from __future__ import annotations + import asyncio import json import logging @@ -103,7 +105,7 @@ async def main( # pylint: disable=R0914 class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/range.py b/extensions/eda/plugins/event_source/range.py index 01609c6d..ab884081 100644 --- a/extensions/eda/plugins/event_source/range.py +++ b/extensions/eda/plugins/event_source/range.py @@ -14,6 +14,8 @@ """ +from __future__ import annotations + import asyncio from typing import Any @@ -33,7 +35,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/tick.py b/extensions/eda/plugins/event_source/tick.py index 48632dc7..42bd3b60 100644 --- a/extensions/eda/plugins/event_source/tick.py +++ b/extensions/eda/plugins/event_source/tick.py @@ -13,6 +13,9 @@ delay: 5 """ + +from __future__ import annotations + import asyncio import itertools from typing import Any @@ -33,7 +36,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/url_check.py b/extensions/eda/plugins/event_source/url_check.py index f02ca55e..772afa05 100644 --- a/extensions/eda/plugins/event_source/url_check.py +++ b/extensions/eda/plugins/event_source/url_check.py @@ -19,6 +19,8 @@ """ +from __future__ import annotations + import asyncio from typing import Any @@ -72,7 +74,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/webhook.py b/extensions/eda/plugins/event_source/webhook.py index 8ae85259..2023fcf9 100644 --- a/extensions/eda/plugins/event_source/webhook.py +++ b/extensions/eda/plugins/event_source/webhook.py @@ -23,6 +23,7 @@ Defaults to hex """ +from __future__ import annotations import asyncio import base64 @@ -31,11 +32,13 @@ import json import logging import ssl -from collections.abc import Callable -from typing import Any +from typing import TYPE_CHECKING, Any from aiohttp import web +if TYPE_CHECKING: + from collections.abc import Callable + logger = logging.getLogger(__name__) routes = web.RouteTableDef() @@ -191,7 +194,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: "MockQueue", event: dict) -> None: + async def put(self: MockQueue, event: dict) -> None: """Print the event.""" print(event) # noqa: T201 From e0e279830288a30e22996d81f735adc3c6b103dc Mon Sep 17 00:00:00 2001 From: Laszlo Szomor Date: Mon, 21 Aug 2023 16:06:08 +0200 Subject: [PATCH 5/5] Revert - Fix tox issues This reverts commit 7d65d2de297d0698d46f8a8caff877fe94ad568b --- .../eda/plugins/event_source/alertmanager.py | 4 +--- .../eda/plugins/event_source/aws_cloudtrail.py | 10 +++------- .../eda/plugins/event_source/aws_sqs_queue.py | 4 +--- .../eda/plugins/event_source/azure_service_bus.py | 4 +--- extensions/eda/plugins/event_source/file.py | 14 ++++++-------- extensions/eda/plugins/event_source/generic.py | 4 +--- extensions/eda/plugins/event_source/journald.py | 2 -- extensions/eda/plugins/event_source/kafka.py | 4 +--- extensions/eda/plugins/event_source/range.py | 4 +--- extensions/eda/plugins/event_source/tick.py | 5 +---- extensions/eda/plugins/event_source/url_check.py | 4 +--- extensions/eda/plugins/event_source/webhook.py | 9 +++------ 12 files changed, 20 insertions(+), 48 deletions(-) diff --git a/extensions/eda/plugins/event_source/alertmanager.py b/extensions/eda/plugins/event_source/alertmanager.py index eff6abde..7384b347 100644 --- a/extensions/eda/plugins/event_source/alertmanager.py +++ b/extensions/eda/plugins/event_source/alertmanager.py @@ -32,8 +32,6 @@ """ -from __future__ import annotations - import asyncio import logging from typing import Any @@ -146,7 +144,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/aws_cloudtrail.py b/extensions/eda/plugins/event_source/aws_cloudtrail.py index 56f5c48e..7847fcdf 100644 --- a/extensions/eda/plugins/event_source/aws_cloudtrail.py +++ b/extensions/eda/plugins/event_source/aws_cloudtrail.py @@ -31,18 +31,14 @@ """ -from __future__ import annotations - import asyncio import json from datetime import datetime -from typing import TYPE_CHECKING, Any +from typing import Any +from aiobotocore.client import BaseClient from aiobotocore.session import get_session -if TYPE_CHECKING: - from aiobotocore.client import BaseClient - def _cloudtrail_event_to_dict(event: dict) -> dict: event["EventTime"] = event["EventTime"].isoformat() @@ -135,7 +131,7 @@ def connection_args(args: dict[str, Any]) -> dict[str, Any]: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/aws_sqs_queue.py b/extensions/eda/plugins/event_source/aws_sqs_queue.py index 90f26383..7dd3afb5 100644 --- a/extensions/eda/plugins/event_source/aws_sqs_queue.py +++ b/extensions/eda/plugins/event_source/aws_sqs_queue.py @@ -21,8 +21,6 @@ """ -from __future__ import annotations - import asyncio import json import logging @@ -111,7 +109,7 @@ def connection_args(args: dict[str, Any]) -> dict[str, Any]: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/azure_service_bus.py b/extensions/eda/plugins/event_source/azure_service_bus.py index 3b48ea90..940e6ec6 100644 --- a/extensions/eda/plugins/event_source/azure_service_bus.py +++ b/extensions/eda/plugins/event_source/azure_service_bus.py @@ -16,8 +16,6 @@ """ -from __future__ import annotations - import asyncio import concurrent.futures import contextlib @@ -71,7 +69,7 @@ async def main( class MockQueue: """A fake queue.""" - async def put_nowait(self: MockQueue, event: dict) -> None: + async def put_nowait(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/file.py b/extensions/eda/plugins/event_source/file.py index 5d845a0d..09403d98 100644 --- a/extensions/eda/plugins/event_source/file.py +++ b/extensions/eda/plugins/event_source/file.py @@ -15,8 +15,6 @@ """ -from __future__ import annotations - import pathlib import yaml @@ -62,21 +60,21 @@ def _observe_files(queue, files: list[str]) -> None: # noqa: ANN001 class Handler(RegexMatchingEventHandler): """A handler for file events.""" - def __init__(self: Handler, **kwargs) -> None: # noqa: ANN003 + def __init__(self: "Handler", **kwargs) -> None: # noqa: ANN003 RegexMatchingEventHandler.__init__(self, **kwargs) - def on_created(self: Handler, event: dict) -> None: + def on_created(self: "Handler", event: dict) -> None: if event.src_path in files: send_facts(queue, event.src_path) - def on_deleted(self: Handler, event: dict) -> None: + def on_deleted(self: "Handler", event: dict) -> None: pass - def on_modified(self: Handler, event: dict) -> None: + def on_modified(self: "Handler", event: dict) -> None: if event.src_path in files: send_facts(queue, event.src_path) - def on_moved(self: Handler, event: dict) -> None: + def on_moved(self: "Handler", event: dict) -> None: pass observer = Observer() @@ -100,7 +98,7 @@ def on_moved(self: Handler, event: dict) -> None: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/generic.py b/extensions/eda/plugins/event_source/generic.py index 0b033756..3039d0a7 100644 --- a/extensions/eda/plugins/event_source/generic.py +++ b/extensions/eda/plugins/event_source/generic.py @@ -41,8 +41,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import annotations - import asyncio import random import time @@ -130,7 +128,7 @@ def _create_data( class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/journald.py b/extensions/eda/plugins/event_source/journald.py index fb727991..be3b2486 100644 --- a/extensions/eda/plugins/event_source/journald.py +++ b/extensions/eda/plugins/event_source/journald.py @@ -23,8 +23,6 @@ """ -from __future__ import annotations - import asyncio from typing import Any diff --git a/extensions/eda/plugins/event_source/kafka.py b/extensions/eda/plugins/event_source/kafka.py index b6512002..d3ee5c01 100644 --- a/extensions/eda/plugins/event_source/kafka.py +++ b/extensions/eda/plugins/event_source/kafka.py @@ -24,8 +24,6 @@ """ -from __future__ import annotations - import asyncio import json import logging @@ -105,7 +103,7 @@ async def main( # pylint: disable=R0914 class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/range.py b/extensions/eda/plugins/event_source/range.py index ab884081..01609c6d 100644 --- a/extensions/eda/plugins/event_source/range.py +++ b/extensions/eda/plugins/event_source/range.py @@ -14,8 +14,6 @@ """ -from __future__ import annotations - import asyncio from typing import Any @@ -35,7 +33,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/tick.py b/extensions/eda/plugins/event_source/tick.py index 42bd3b60..48632dc7 100644 --- a/extensions/eda/plugins/event_source/tick.py +++ b/extensions/eda/plugins/event_source/tick.py @@ -13,9 +13,6 @@ delay: 5 """ - -from __future__ import annotations - import asyncio import itertools from typing import Any @@ -36,7 +33,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/url_check.py b/extensions/eda/plugins/event_source/url_check.py index 772afa05..f02ca55e 100644 --- a/extensions/eda/plugins/event_source/url_check.py +++ b/extensions/eda/plugins/event_source/url_check.py @@ -19,8 +19,6 @@ """ -from __future__ import annotations - import asyncio from typing import Any @@ -74,7 +72,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201 diff --git a/extensions/eda/plugins/event_source/webhook.py b/extensions/eda/plugins/event_source/webhook.py index 2023fcf9..8ae85259 100644 --- a/extensions/eda/plugins/event_source/webhook.py +++ b/extensions/eda/plugins/event_source/webhook.py @@ -23,7 +23,6 @@ Defaults to hex """ -from __future__ import annotations import asyncio import base64 @@ -32,13 +31,11 @@ import json import logging import ssl -from typing import TYPE_CHECKING, Any +from collections.abc import Callable +from typing import Any from aiohttp import web -if TYPE_CHECKING: - from collections.abc import Callable - logger = logging.getLogger(__name__) routes = web.RouteTableDef() @@ -194,7 +191,7 @@ async def main(queue: asyncio.Queue, args: dict[str, Any]) -> None: class MockQueue: """A fake queue.""" - async def put(self: MockQueue, event: dict) -> None: + async def put(self: "MockQueue", event: dict) -> None: """Print the event.""" print(event) # noqa: T201