Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Key Vault] Add local-only mode to CryptographyClient #16565

Merged
merged 18 commits into from
Mar 10, 2021

Conversation

mccoyp
Copy link
Member

@mccoyp mccoyp commented Feb 5, 2021

Resolves #16619.

Aligns Python's CryptographyClient with other languages, in providing a local-only mode by means of a factory method.

@mccoyp mccoyp added KeyVault Client This issue points to a problem in the data-plane of the library. labels Feb 5, 2021
@mccoyp mccoyp force-pushed the feature/localclient-pr branch 3 times, most recently from f3ef95b to 22e32c4 Compare February 25, 2021 00:28
@mccoyp mccoyp marked this pull request as ready for review February 25, 2021 23:42
@mccoyp mccoyp added this to the [2021] March milestone Feb 25, 2021
@mccoyp mccoyp requested a review from chlowell March 4, 2021 00:23

mock_client = mock.Mock()
jwk = mock.Mock(spec=JsonWebKey)
client = CryptographyClient.from_jwk(jwk=jwk)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We expect no request, so I would use a mock transport that raises when the client tries to send e.g.

mock.Mock(send=mock.Mock(side_effect=Exception("client shouldn't send a request"))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(That is, if it would be a bug for the client to send a request in local-only mode.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one that I've made a note of to revisit -- I'm not too familiar with Mock and wasn't able to randomly turn enough dials to get a better understanding. I figure that asserting that the underlying client doesn't call get_key is sufficient for the time being

@@ -106,16 +107,20 @@ def __init__(self, key, credential, **kwargs):
self._key = None
self._key_id = parse_key_vault_id(key)
self._keys_get_forbidden = None # type: Optional[bool]
elif self._jwk:
self._key_id = key.get("kid", "")
self._key = KeyVaultKey(None, jwk=key, _local_only=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything we need for local crypto is in the JWK. Do we really need to jam that into KeyVaultKey?

Copy link
Member Author

@mccoyp mccoyp Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the local crypto providers and operations assume that they're working with a KeyVaultKey, so we'd have to do so unless we refactor them to accept a JsonWebKey (unless I'm mistaken)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's unreasonable by any means to make local providers accept JWKs, but it seems like a design question of whether we do that (and preserve the current definition of a KeyVaultKey) or make a smaller tweak to accept KeyVaultKeys intended for local-only use

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that's what I'm getting at. Their expectation of KeyVaultKey appears to be driving additional complexity here but that expectation isn't a requirement. Actually they only need the JWK. If they took that up front, maybe it wouldn't be necessary to complicate KeyVaultKey?

@mccoyp mccoyp requested a review from chlowell March 5, 2021 23:34
@mccoyp mccoyp requested a review from chlowell March 8, 2021 19:39
"""The full identifier of the provider's key.

:rtype: str
"""
return self._key.id
return self._key_id
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be either a valid Key Vault identifier or whatever the user gave us:

Suggested change
return self._key_id
return self._key.get("kid")

Does that remove the need to ask a caller to give us _key_id separately?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking here is that a KeyVaultKey provided to CryptographyClient could have a valid Key Vault identifier for its id, but that the JsonWebKey could have a different kid. I assumed it made more sense to return the Key Vault identifier in the operation result if possible, which is why I added the _key_id keyword argument

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would happen only when someone does KeyVaultKey(key_id, jwk=jwk) with key_id != jwk["kid"]. Granted, that is possible, but seems unlikely and given KeyVaultKey's docs ask for a Key Vault identifier for key_id, it seems reasonable not to special case it. My $0.02 🤑

"""The full identifier of the provider's key.

:rtype: str
"""
return self._key.id
return self._key_id
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would happen only when someone does KeyVaultKey(key_id, jwk=jwk) with key_id != jwk["kid"]. Granted, that is possible, but seems unlikely and given KeyVaultKey's docs ask for a Key Vault identifier for key_id, it seems reasonable not to special case it. My $0.02 🤑

@@ -276,6 +276,23 @@ def test_encrypt_local(self, azure_keyvault_url, **kwargs):
result = crypto_client.decrypt(result.algorithm, result.ciphertext)
self.assertEqual(result.plaintext, self.plaintext)

@KeyVaultPreparer()
def test_encrypt_local_from_jwk(self, azure_keyvault_url, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also possible to test private operations locally, using an imported key for public operations (the neighboring keys.py has suitable keys). Doesn't need to be in this PR but it's a test gap to fill before the next stable release. Also, need to cover EC keys as well.

status:
code: 401
message: Unauthorized
url: https://mcpatinokv.vault.azure.net/keys/livekvtestwrap-localfe02144b/create?api-version=7.2-preview
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why we've stopped replacing the vault name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vault names aren't being replaced in responses with the PowerShellPreparer -- this is another item on my list of things to investigate 🕵️‍♀️

@mccoyp mccoyp merged commit 95035c7 into Azure:master Mar 10, 2021
@mccoyp mccoyp deleted the feature/localclient-pr branch March 10, 2021 00:54
openapi-sdkautomation bot pushed a commit to AzureSDKAutomation/azure-sdk-for-python that referenced this pull request Nov 9, 2021
Fix Swagger for SecurityInsights - Add teamInformation to IncidentProperties (Azure#16565)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Client This issue points to a problem in the data-plane of the library. KeyVault
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove LocalCryptographyClient in favor of CryptographyClient local mode
2 participants