Skip to content

Commit 7e853e1

Browse files
Move event idenity to sign function
1 parent 3d0819b commit 7e853e1

File tree

6 files changed

+70
-31
lines changed

6 files changed

+70
-31
lines changed

packages/aws-sdk-signers/src/aws_sdk_signers/signers.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -803,27 +803,25 @@ class AsyncEventSigner:
803803
def __init__(
804804
self,
805805
*,
806-
properties: SigV4SigningProperties,
807-
identity: _AWSCredentialsIdentity,
808806
initial_signature: bytes,
809807
event_encoder_cls: type["EventHeaderEncoder"],
810808
):
811809
self._prior_signature = initial_signature
812810
self._signing_lock = asyncio.Lock()
813811
self._event_encoder_cls = event_encoder_cls
814-
self._properties = properties
815-
self._identity = identity
816812

817813
async def sign(
818814
self,
819815
*,
820816
event: "EventMessage",
817+
identity: _AWSCredentialsIdentity,
818+
properties: SigV4SigningProperties,
821819
) -> "EventMessage":
822820
async with self._signing_lock:
823821
# Copy and prepopulate any missing values in the
824822
# signing properties.
825823
new_signing_properties = SigV4SigningProperties( # type: ignore
826-
**self._properties
824+
**properties
827825
)
828826
# TODO: If date is in properties, parse a datetime from it.
829827
date_obj = datetime.datetime.now(datetime.UTC)
@@ -848,7 +846,7 @@ async def sign(
848846
prior_signature=self._prior_signature,
849847
)
850848
event_signature = await self._sign_event(
851-
identity=self._identity,
849+
identity=identity,
852850
timestamp=timestamp,
853851
string_to_sign=string_to_sign,
854852
properties=new_signing_properties,

packages/smithy-aws-core/src/smithy_aws_core/auth/sigv4.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,15 @@ def signer(self) -> SigV4Signer:
100100
return self._signer
101101

102102
def event_signer(
103-
self,
104-
request: HTTPRequest,
105-
identity: AWSCredentialsIdentity,
106-
properties: SigV4SigningProperties,
107-
) -> EventSigner | None:
103+
self, request: HTTPRequest
104+
) -> EventSigner[AWSCredentialsIdentity, SigV4SigningProperties] | None:
108105
if not HAS_EVENT_STREAM:
109106
return None
110107

111108
auth_value = request.fields["Authorization"].as_string()
112109
signature: str = re.split("Signature=", auth_value)[-1]
113110

114111
return AsyncEventSigner(
115-
identity=identity,
116-
properties=properties,
117112
initial_signature=signature.encode("utf-8"),
118113
event_encoder_cls=EventHeaderEncoder,
119114
)

packages/smithy-aws-event-stream/src/smithy_aws_event_stream/aio/__init__.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import asyncio
44
import logging
55
from collections.abc import Callable
6+
from dataclasses import dataclass
7+
from typing import TYPE_CHECKING
68

79
from smithy_core.aio.interfaces import AsyncByteStream, AsyncWriter
810
from smithy_core.aio.interfaces.auth import EventSigner
911
from smithy_core.aio.interfaces.eventstream import EventPublisher, EventReceiver
12+
from smithy_core.aio.interfaces.identity import IdentityResolver
1013
from smithy_core.codecs import Codec
1114
from smithy_core.deserializers import DeserializeableShape, ShapeDeserializer
1215
from smithy_core.exceptions import ExpectationNotMetException
@@ -20,16 +23,29 @@
2023
logger = logging.getLogger(__name__)
2124

2225

26+
if TYPE_CHECKING:
27+
from aws_sdk_signers import SigV4SigningProperties
28+
from smithy_aws_core.identity import AWSCredentialsIdentity, AWSIdentityProperties
29+
30+
31+
@dataclass
32+
class SigningConfig:
33+
signer: "EventSigner[AWSCredentialsIdentity, SigV4SigningProperties]"
34+
signing_properties: "SigV4SigningProperties"
35+
identity_resolver: "IdentityResolver[AWSCredentialsIdentity, AWSIdentityProperties]"
36+
identity_properties: "AWSIdentityProperties"
37+
38+
2339
class AWSEventPublisher[E: SerializeableShape](EventPublisher[E]):
2440
def __init__(
2541
self,
2642
payload_codec: Codec,
2743
async_writer: AsyncWriter,
28-
signer: EventSigner | None = None,
44+
signing_config: SigningConfig | None = None,
2945
is_client_mode: bool = True,
3046
):
3147
self._writer = async_writer
32-
self._signer = signer
48+
self._signing_config = signing_config
3349
self._serializer = _EventSerializer(
3450
payload_codec=payload_codec, is_client_mode=is_client_mode
3551
)
@@ -45,8 +61,16 @@ async def send(self, event: E) -> None:
4561
raise ExpectationNotMetException(
4662
"Expected an event message to be serialized, but was None."
4763
)
48-
if self._signer is not None:
49-
result = await self._signer.sign(event=result)
64+
65+
if self._signing_config is not None:
66+
identity = await self._signing_config.identity_resolver.get_identity(
67+
properties=self._signing_config.identity_properties
68+
)
69+
result = await self._signing_config.signer.sign(
70+
event=event,
71+
identity=identity,
72+
properties=self._signing_config.signing_properties,
73+
)
5074

5175
encoded_result = result.encode()
5276
try:

packages/smithy-core/src/smithy_core/aio/identity.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33
import logging
4-
from collections.abc import Sequence
5-
from typing import Final
4+
from collections.abc import Mapping, Sequence
5+
from typing import Any, Final
66

77
from ..exceptions import SmithyIdentityException
8+
from ..interfaces.identity import Identity
89
from .interfaces.identity import IdentityResolver
910

1011
logger: Final = logging.getLogger(__name__)
1112

1213

13-
class ChainedIdentityResolver[I, IP](IdentityResolver[I, IP]):
14+
# TODO: turn this into a decorator
15+
class CachingIdentityResolver[I: Identity, IP: Mapping[str, Any]](
16+
IdentityResolver[I, IP]
17+
):
18+
def __init__(self) -> None:
19+
self._cached: I | None = None
20+
21+
async def get_identity(self, *, properties: IP) -> I:
22+
if self._cached is None or self._cached.is_expired:
23+
self._cached = await self._get_identity(properties=properties)
24+
return self._cached
25+
26+
async def _get_identity(self, *, properties: IP) -> I:
27+
raise NotImplementedError
28+
29+
30+
class ChainedIdentityResolver[I: Identity, IP: Mapping[str, Any]](
31+
CachingIdentityResolver[I, IP]
32+
):
1433
"""Attempts to resolve an identity by checking a sequence of sub-resolvers.
1534
1635
If a nested resolver raises a :py:class:`SmithyIdentityException`, the next
@@ -22,9 +41,10 @@ def __init__(self, resolvers: Sequence[IdentityResolver[I, IP]]) -> None:
2241
2342
:param resolvers: The sequence of resolvers to resolve identity from.
2443
"""
44+
super().__init__()
2545
self._resolvers = resolvers
2646

27-
async def get_identity(self, *, properties: IP) -> I:
47+
async def _get_identity(self, *, properties: IP) -> I:
2848
logger.debug("Attempting to resolve identity from resolver chain.")
2949
for resolver in self._resolvers:
3050
try:

packages/smithy-core/src/smithy_core/aio/interfaces/auth.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Any, Protocol
55

66
from ...interfaces import TypedProperties as _TypedProperties
7+
from ...interfaces.identity import Identity
78
from ...shapes import ShapeID
89
from . import Request
910
from .identity import IdentityResolver
@@ -22,19 +23,21 @@ async def sign(self, *, request: R, identity: I, properties: SP) -> R:
2223
...
2324

2425

25-
class EventSigner(Protocol):
26+
class EventSigner[I, SP: Mapping[str, Any]](Protocol):
2627
"""A class that signs requests before they are sent."""
2728

2829
# TODO: add a protocol type for events
29-
async def sign(self, *, event: Any) -> Any:
30+
async def sign(self, *, event: Any, identity: I, properties: SP) -> Any:
3031
"""Get a signed version of the event.
3132
3233
:param event: The event to be signed.
3334
"""
3435
...
3536

3637

37-
class AuthScheme[R: Request, I, IP: Mapping[str, Any], SP: Mapping[str, Any]](Protocol):
38+
class AuthScheme[R: Request, I: Identity, IP: Mapping[str, Any], SP: Mapping[str, Any]](
39+
Protocol
40+
):
3841
"""A class that coordinates identity and auth."""
3942

4043
scheme_id: ShapeID
@@ -74,15 +77,11 @@ def signer(self) -> Signer[R, I, SP]:
7477
"""Get a signer for the request."""
7578
...
7679

77-
def event_signer(
78-
self, request: R, identity: I, properties: SP
79-
) -> EventSigner | None:
80+
def event_signer(self, request: R) -> EventSigner[I, SP] | None:
8081
"""Construct a signer for event stream events.
8182
8283
:param request: The request that will initiate the event stream. The request
8384
will not have been sent when this method is called.
84-
:param identity: The identity to use to sign events.
85-
:param properties: Additional properties used to sign events.
8685
:returns: An event signer if the scheme supports signing events, otherwise None.
8786
"""
8887
return None

packages/smithy-core/src/smithy_core/aio/interfaces/identity.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
3-
from typing import Protocol
3+
from collections.abc import Mapping
4+
from typing import Any, Protocol
45

6+
from ...interfaces.identity import Identity
57

6-
class IdentityResolver[I, IP](Protocol):
8+
9+
class IdentityResolver[I: Identity, IP: Mapping[str, Any]](Protocol):
710
"""Used to load a user's `Identity` from a given source.
811
912
Each `Identity` may have one or more resolver implementations.

0 commit comments

Comments
 (0)