|
| 1 | +import base64 |
1 | 2 | import os |
2 | 3 | import pickle |
3 | 4 | import unittest |
|
8 | 9 | import google.protobuf.any_pb2 |
9 | 10 | import google.protobuf.wrappers_pb2 |
10 | 11 | import httpx |
| 12 | +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey |
11 | 13 | from fastapi.testclient import TestClient |
12 | 14 |
|
13 | 15 | from dispatch.experimental.durable.registry import clear_functions |
14 | | -from dispatch.fastapi import Dispatch |
| 16 | +from dispatch.fastapi import Dispatch, parse_verification_key |
15 | 17 | from dispatch.function import Arguments, Error, Function, Input, Output |
16 | 18 | from dispatch.proto import _any_unpickle as any_unpickle |
17 | 19 | from dispatch.sdk.v1 import call_pb2 as call_pb |
18 | 20 | from dispatch.sdk.v1 import function_pb2 as function_pb |
| 21 | +from dispatch.signature import public_key_from_pem |
19 | 22 | from dispatch.status import Status |
20 | 23 | from dispatch.test import EndpointClient |
21 | 24 |
|
| 25 | +public_key_pem = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=\n-----END PUBLIC KEY-----" |
| 26 | +public_key_pem2 = "-----BEGIN PUBLIC KEY-----\\nMCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=\\n-----END PUBLIC KEY-----" |
| 27 | +public_key = public_key_from_pem(public_key_pem) |
| 28 | +public_key_bytes = public_key.public_bytes_raw() |
| 29 | +public_key_b64 = base64.b64encode(public_key_bytes) |
| 30 | + |
22 | 31 |
|
23 | 32 | def create_dispatch_instance(app, endpoint): |
24 | 33 | return Dispatch( |
@@ -98,6 +107,71 @@ def my_function(input: Input) -> Output: |
98 | 107 |
|
99 | 108 | self.assertEqual(output, "You told me: 'Hello World!' (12 characters)") |
100 | 109 |
|
| 110 | + @mock.patch.dict(os.environ, {"DISPATCH_VERIFICATION_KEY": public_key_pem}) |
| 111 | + def test_parse_verification_key_env_pem_str(self): |
| 112 | + verification_key = parse_verification_key(None) |
| 113 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 114 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 115 | + |
| 116 | + @mock.patch.dict(os.environ, {"DISPATCH_VERIFICATION_KEY": public_key_pem2}) |
| 117 | + def test_parse_verification_key_env_pem_escaped_newline_str(self): |
| 118 | + verification_key = parse_verification_key(None) |
| 119 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 120 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 121 | + |
| 122 | + @mock.patch.dict(os.environ, {"DISPATCH_VERIFICATION_KEY": public_key_b64.decode()}) |
| 123 | + def test_parse_verification_key_env_b64_str(self): |
| 124 | + verification_key = parse_verification_key(None) |
| 125 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 126 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 127 | + |
| 128 | + def test_parse_verification_key_none(self): |
| 129 | + # The verification key is optional. Both Dispatch(verification_key=...) and |
| 130 | + # DISPATCH_VERIFICATION_KEY may be omitted/None. |
| 131 | + verification_key = parse_verification_key(None) |
| 132 | + self.assertIsNone(verification_key) |
| 133 | + |
| 134 | + def test_parse_verification_key_ed25519publickey(self): |
| 135 | + verification_key = parse_verification_key(public_key) |
| 136 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 137 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 138 | + |
| 139 | + def test_parse_verification_key_pem_str(self): |
| 140 | + verification_key = parse_verification_key(public_key_pem) |
| 141 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 142 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 143 | + |
| 144 | + def test_parse_verification_key_pem_escaped_newline_str(self): |
| 145 | + verification_key = parse_verification_key(public_key_pem2) |
| 146 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 147 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 148 | + |
| 149 | + def test_parse_verification_key_pem_bytes(self): |
| 150 | + verification_key = parse_verification_key(public_key_pem.encode()) |
| 151 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 152 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 153 | + |
| 154 | + def test_parse_verification_key_b64_str(self): |
| 155 | + verification_key = parse_verification_key(public_key_b64.decode()) |
| 156 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 157 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 158 | + |
| 159 | + def test_parse_verification_key_b64_bytes(self): |
| 160 | + verification_key = parse_verification_key(public_key_b64) |
| 161 | + self.assertIsInstance(verification_key, Ed25519PublicKey) |
| 162 | + self.assertEqual(verification_key.public_bytes_raw(), public_key_bytes) |
| 163 | + |
| 164 | + def test_parse_verification_key_invalid(self): |
| 165 | + with self.assertRaisesRegex(ValueError, "invalid verification key 'foo'"): |
| 166 | + parse_verification_key("foo") |
| 167 | + |
| 168 | + @mock.patch.dict(os.environ, {"DISPATCH_VERIFICATION_KEY": "foo"}) |
| 169 | + def test_parse_verification_key_invalid_env(self): |
| 170 | + with self.assertRaisesRegex( |
| 171 | + ValueError, "invalid DISPATCH_VERIFICATION_KEY 'foo'" |
| 172 | + ): |
| 173 | + parse_verification_key(None) |
| 174 | + |
101 | 175 |
|
102 | 176 | def response_output(resp: function_pb.RunResponse) -> Any: |
103 | 177 | return any_unpickle(resp.exit.result.output) |
|
0 commit comments