Conversation
0e39eb8 to
246f957
Compare
|
|
||
| def __init__( | ||
| self, | ||
| command: list[str], |
There was a problem hiding this comment.
The PR description says we will accept a command string:
Improves command handling so callers can provide either explicit argument lists or a shell-style command string.
Looking at the commit history, it looks like you intentionally removed this capability. I'm alright with this; we can always add support later. What was the reason for the revert?
|
|
||
| if process.returncode != 0: | ||
| raise SmithyIdentityError( | ||
| f"Credential process failed with non-zero exit code: {stderr.decode('utf-8')}" |
There was a problem hiding this comment.
Should we include the returncode in the exception message in case it is useful in debugging?
There was a problem hiding this comment.
Second comment here - we are assuming that this will always be parse-able as valid UTF-8.
While any other response encoding would be considered malformed output, a user would see a UnicodeDecodeError instead of the SmithyIdentityError. We should be guarding against other encodings or binary garbage being returned by the unknown process.
| @@ -0,0 +1,115 @@ | |||
| # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | |||
| # SPDX-License-Identifier: Apache-2.0 | |||
| import asyncio | |||
There was a problem hiding this comment.
High level: I'm starting to get a little concerned with all of the things we are missing since we don't have support for them yet and how we will track them. In this case, it's feature id tracking and adding this to the default chain (which will need to look in the config file).
Should we add some TODOs here?
| creds = json.loads(stdout.decode("utf-8")) | ||
| except json.JSONDecodeError as e: | ||
| raise SmithyIdentityError( | ||
| f"Failed to parse credential process output: {e}" |
There was a problem hiding this comment.
[Non-blocking]
JSONDecodeError contains the json document being parsed. The string representation of this generally does not contain the JSON document, but any downstream error parser may wind up leaking any secrets that may have been contained in this json error.
The alternative here is to not raise this error using from e, but just raise it alone.
Since this scenario seems far-fetched, and SDK users should not be sharing raw error info with untrusted users, I'd say this approach is sufficiently secure and helpful for users looking to debug their credentials process. Just wanted to flag the potential concern, but I'm not requesting changes.
| ) from e | ||
|
|
||
| version = creds.get("Version") | ||
| if version is None or version != 1: |
There was a problem hiding this comment.
| if version is None or version != 1: | |
| if version != 1: |
Nit: the None check is redundant here since None != 1 returns True.
|
|
||
|
|
||
| @pytest.mark.parametrize("command", [[], None]) | ||
| def test_resolver_invalid_command(command: object): |
There was a problem hiding this comment.
Did you mean to add more test cases here, like a tuple or a space separated string?
I'm not sure why you chose to have a generic name with pymark.test.parametrize, but only one test case
| return process | ||
|
|
||
|
|
||
| @pytest.mark.asyncio |
There was a problem hiding this comment.
Nit: we could make a test class and just have this annotation once (or a few times as it makes sense to separate out the classes) instead of 19 times
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_missing_expiration(): |
There was a problem hiding this comment.
How is this different than test_valid_credentials_with_session_token?
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_missing_expiration_and_session_token(): |
There was a problem hiding this comment.
Same as above, this is similar to test_valid_credentials_without_session_token
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_credentials_with_expiration(): |
There was a problem hiding this comment.
Couldn't this be renamed to test_credentials_with_optional_fields and consolidated with test_credentials_with_account_id and also add session token in here?
| async def test_process_timeout(): | ||
| process = AsyncMock() | ||
| process.returncode = None | ||
| process.communicate = AsyncMock(side_effect=TimeoutError) |
There was a problem hiding this comment.
We are mocking process.communicate, but this error gets raised during wait_for.
Can we get this updated to reflect the expected code path for this error?
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_credentials_with_non_utc_expiration(): |
There was a problem hiding this comment.
Can we add a negative test case for invalid string expirations and another for non-string expirations?
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_missing_access_key_id(): |
There was a problem hiding this comment.
Nit: Should we parametrize this and the next test? Either way is fine, it's just duplicated code.
| account_id = creds.get("AccountId") | ||
|
|
||
| if isinstance(expiration, str): | ||
| dt = datetime.fromisoformat(expiration) |
There was a problem hiding this comment.
What does this look like when we have an invalid string (for instance foo)?
That will raise ValueError: Invalid isoformat string, so maybe we need another try/except here.
| async def test_non_zero_exit_code(): | ||
| process = mock_subprocess(1, b"", b"Process error message") | ||
|
|
||
| with patch("asyncio.create_subprocess_exec", return_value=process): |
There was a problem hiding this comment.
Can we add an assertion that "Process error message" is in the error value?
Important
This PR adds a standalone AWS process credential resolver. We do not currently support reading from the shared AWS config file. However, this will unblock anyone from using a process based credential provider.
Overview
This PR adds an AWS process-based credentials resolver to
smithy-aws-coreso credential providers can source credentials from an external command, while handling the common failure modes and caching behavior expected for both long-lived and temporary credentials.Summary
ProcessCredentialsResolverandProcessCredentialsConfigin the AWS identity package and exports them as part of the public identity surface.AWSCredentialsIdentity.Testing
Note
This was tested with internal AWS process tools for sourcing credentials. TODO: Test with popular external tools.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.