Add modular credential chain for AWS identity resolution#749
Conversation
|
|
||
|
|
||
| @dataclass(frozen=True, kw_only=True) | ||
| class NamedResolver: |
There was a problem hiding this comment.
This will also eventually associate an identity resolver with other provider metadata like a feature id set, not just its name. I'm open to renaming this resolver wrapper.
| self._terminal = True | ||
|
|
||
|
|
||
| class ChainIdentityProvider(Protocol): |
There was a problem hiding this comment.
I just realized that this class will eventually need to expose a feature_ids property and may need to expose new things in the future. This would break anybody that initializes their provider without creating a subclass from this protocol, even if we provide a default.
We can probably switch this to an ABC to allow us to add these properties with defaults that automatically propagate to third-party implementations. We could also implement a more specific protocol like AwsChainIdentityProvider that we control while directing third party implementations to use the base ChainIdentityProvider. Curious what reviewers think.
Description
This PR adds modular credential resolution to Smithy Python via a new
IdentityChain. Today users must set a singleaws_credentials_identity_resolveron the client config; there is no fallback chain to automatically detect and resolve credentials from the environment, shared config files, etc. The newIdentityChainfills that gap.IdentityChainimplements the existingIdentityResolverinterface, so it drops into the current config slot unchanged. A client cannot tell whether it holds a single resolver or a chain.The chain follows a two-phase model:
Assembly (once, at construction).
IdentityChain.create():smithy_aws_core.identity.chain_providersentry point group. Installing a package that exposes a provider to that group is all that is needed to add a provider to the chain.Standard/Before/Afterordering constraints against a fixed set ofStandardProviderslots.setup()with a sharedChainSetupcontext. A provider inspects the environment and active profile, and if it detects its source, registers a resolver. Registering a terminal resolver (a source that is authoritative once detected, e.g. complete environment credentials) stops further assembly. When assembly finishes,ChainSetupis discarded and only the ordered resolvers survive.Resolution (per request).
IdentityChain.get_identity()walks the assembled resolvers in order, returning the first identity that resolves. A resolver "misses" by raisingSmithyIdentityError, and the chain moves on. If every resolver misses, it raisesIdentityChainErrorwith per-provider diagnostics. When a known credential source is detected on the system but no provider claims it, the error also suggests the package to install.This PR initially ships four standard providers (
Environment,SharedConfig,ProfileSessionKeys, andProfileStaticKeys) all of which detect their source locally with no network calls. Network-backed providers (STS, SSO, ECS, IMDS) are designed to ship as separate optional packages that register under the same entry point group.Note
Network providers and integration into SDK client construction will be added in follow-up PRs. This PR adds standalone support for now.
Usage
Create a chain from installed providers:
Construct a chain with explicit resolvers (useful for tests or an explicit, non-discovered chain). Here
StaticCredentialsResolveris seeded with a fixedAWSCredentialsIdentity, so the chain falls back to those credentials if the environment resolver misses:Custom providers implement
ChainIdentityProviderand position themselves relative to a standard slot:Register a custom provider so
IdentityChain.create()can discover it:Testing
make test-pyBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.