diff --git a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md index 8834bd492fb0..7fcb0514b51b 100644 --- a/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-keys/CHANGELOG.md @@ -3,6 +3,10 @@ ## 4.5.0b4 (Unreleased) ### Features Added +- Added support for automated and on-demand key rotation in Azure Key Vault + ([#19840](https://github.com/Azure/azure-sdk-for-python/issues/19840)) + - Added `KeyClient.rotate_key` to rotate a key on-demand + - Added `KeyClient.update_key_rotation_policy` to update a key's automated rotation policy ### Breaking Changes diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/__init__.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/__init__.py index db89d14689a8..ffd2301ad26a 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/__init__.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/__init__.py @@ -2,13 +2,15 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------- -from ._enums import KeyCurveName, KeyExportEncryptionAlgorithm, KeyOperation, KeyType +from ._enums import KeyCurveName, KeyExportEncryptionAlgorithm, KeyOperation, KeyRotationPolicyAction, KeyType from ._shared.client_base import ApiVersion from ._models import ( DeletedKey, JsonWebKey, KeyProperties, KeyReleasePolicy, + KeyRotationLifetimeAction, + KeyRotationPolicy, KeyVaultKey, KeyVaultKeyIdentifier, RandomBytes, @@ -25,10 +27,13 @@ "KeyCurveName", "KeyExportEncryptionAlgorithm", "KeyOperation", + "KeyRotationPolicyAction", "KeyType", "DeletedKey", "KeyProperties", "KeyReleasePolicy", + "KeyRotationLifetimeAction", + "KeyRotationPolicy", "RandomBytes", "ReleaseKeyResult", ] diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py index 1e561321b9c2..d9ba659d69e5 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py @@ -8,7 +8,7 @@ from ._shared import KeyVaultClientBase from ._shared.exceptions import error_map as _error_map from ._shared._polling import DeleteRecoverPollingMethod, KeyVaultOperationPoller -from ._models import DeletedKey, KeyVaultKey, KeyProperties, RandomBytes, ReleaseKeyResult +from ._models import DeletedKey, KeyVaultKey, KeyProperties, KeyRotationPolicy, RandomBytes, ReleaseKeyResult try: from typing import TYPE_CHECKING @@ -17,7 +17,7 @@ if TYPE_CHECKING: # pylint:disable=unused-import - from typing import Any, Optional, Union + from typing import Any, Iterable, Optional, Union from azure.core.paging import ItemPaged from azure.core.polling import LROPoller from ._models import JsonWebKey @@ -45,7 +45,7 @@ class KeyClient(KeyVaultClientBase): :dedent: 4 """ - # pylint:disable=protected-access + # pylint:disable=protected-access, too-many-public-methods def _get_attributes(self, enabled, not_before, expires_on, exportable=None): """Return a KeyAttributes object if none-None attributes are provided, or None otherwise""" @@ -727,3 +727,70 @@ def get_random_bytes(self, count, **kwargs): parameters = self._models.GetRandomBytesRequest(count=count) result = self._client.get_random_bytes(vault_base_url=self._vault_url, parameters=parameters, **kwargs) return RandomBytes(value=result.value) + + @distributed_trace + def get_key_rotation_policy(self, name, **kwargs): + # type: (str, **Any) -> KeyRotationPolicy + """Get the rotation policy of a Key Vault key. + + :param str name: The name of the key. + + :return: The key rotation policy. + :rtype: ~azure.keyvault.keys.KeyRotationPolicy + :raises: :class: `~azure.core.exceptions.HttpResponseError` + """ + policy = self._client.get_key_rotation_policy(vault_base_url=self._vault_url, key_name=name, **kwargs) + return KeyRotationPolicy._from_generated(policy) + + @distributed_trace + def rotate_key(self, name, **kwargs): + # type: (str, **Any) -> KeyVaultKey + """Rotate the key based on the key policy by generating a new version of the key. + + This operation requires the keys/rotate permission. + + :param str name: The name of the key to rotate. + + :return: The new version of the rotated key. + :rtype: ~azure.keyvault.keys.KeyVaultKey + :raises: :class:`~azure.core.exceptions.HttpResponseError` + """ + bundle = self._client.rotate_key(vault_base_url=self._vault_url, key_name=name, **kwargs) + return KeyVaultKey._from_key_bundle(bundle) + + @distributed_trace + def update_key_rotation_policy(self, name, **kwargs): + # type: (str, **Any) -> KeyRotationPolicy + """Updates the rotation policy of a Key Vault key. + + This operation requires the keys/update permission. + + :param str name: The name of the key in the given vault. + + :keyword lifetime_actions: Actions that will be performed by Key Vault over the lifetime of a key. + :paramtype lifetime_actions: Iterable[~azure.keyvault.keys.KeyRotationLifetimeAction] + :keyword str expires_in: The expiry time of the policy that will be applied on new key versions, defined as an + ISO 8601 duration. For example: 90 days is "P90D", 3 months is "P3M", and 48 hours is "PT48H". + + :return: The updated rotation policy. + :rtype: ~azure.keyvault.keys.KeyRotationPolicy + :raises: :class:`~azure.core.exceptions.HttpResponseError` + """ + lifetime_actions = kwargs.pop("lifetime_actions", None) + if lifetime_actions: + lifetime_actions = [ + self._models.LifetimeActions( + action=self._models.LifetimeActionsType(type=action.action), + trigger=self._models.LifetimeActionsTrigger( + time_after_create=action.time_after_create, time_before_expiry=action.time_before_expiry + ), + ) + for action in lifetime_actions + ] + + attributes = self._models.KeyRotationPolicyAttributes(expiry_time=kwargs.pop("expires_in", None)) + policy = self._models.KeyRotationPolicy(lifetime_actions=lifetime_actions, attributes=attributes) + result = self._client.update_key_rotation_policy( + vault_base_url=self._vault_url, key_name=name, key_rotation_policy=policy + ) + return KeyRotationPolicy._from_generated(result) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py index b75eaaa99eb6..ac2667ae0482 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_enums.py @@ -39,6 +39,13 @@ class KeyOperation(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): export = "export" +class KeyRotationPolicyAction(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """The action that will be executed in a key rotation policy""" + + ROTATE = "Rotate" #: Rotate the key based on the key policy. + NOTIFY = "Notify" #: Trigger Event Grid events. + + class KeyType(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): """Supported key types""" diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_models.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_models.py index 6a127ced8283..c2aa78c6fe29 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_models.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_models.py @@ -16,7 +16,7 @@ from typing import Any, Dict, Optional, List from datetime import datetime from ._generated.v7_0 import models as _models - from ._enums import KeyOperation, KeyType + from ._enums import KeyOperation, KeyRotationPolicyAction, KeyType KeyOperationResult = namedtuple("KeyOperationResult", ["id", "value"]) @@ -279,6 +279,71 @@ def __init__(self, value): self.value = value +class KeyRotationLifetimeAction(object): + """An action and its corresponding trigger that will be performed by Key Vault over the lifetime of a key. + + :param action: The action that will be executed. + :type action: ~azure.keyvault.keys.KeyRotationPolicyAction or str + + :keyword str time_after_create: Time after creation to attempt the specified action, as an ISO 8601 duration. + For example, 90 days is "P90D". + :keyword str time_before_expiry: Time before expiry to attempt the specified action, as an ISO 8601 duration. + For example, 90 days is "P90D". + """ + + def __init__(self, action, **kwargs): + # type: (KeyRotationPolicyAction, **Any) -> None + self.action = action + self.time_after_create = kwargs.get("time_after_create", None) + self.time_before_expiry = kwargs.get("time_before_expiry", None) + + @classmethod + def _from_generated(cls, lifetime_action): + if lifetime_action.trigger: + return cls( + action=lifetime_action.action.type, + time_after_create=lifetime_action.trigger.time_after_create, + time_before_expiry=lifetime_action.trigger.time_before_expiry, + ) + return cls(action=lifetime_action.action) + + +class KeyRotationPolicy(object): + """The key rotation policy that belongs to a key. + + :ivar str id: The identifier of the key rotation policy. + :ivar lifetime_actions: Actions that will be performed by Key Vault over the lifetime of a key. + :type lifetime_actions: list[~azure.keyvault.keys.KeyRotationLifetimeAction] + :ivar str expires_in: The expiry time of the policy that will be applied on new key versions, defined as an ISO + 8601 duration. For example, 90 days is "P90D". + :ivar created_on: When the policy was created, in UTC + :type created_on: ~datetime.datetime + :ivar updated_on: When the policy was last updated, in UTC + :type updated_on: ~datetime.datetime + """ + + def __init__(self, policy_id, **kwargs): + # type: (str, **Any) -> None + self.id = policy_id + self.lifetime_actions = kwargs.get("lifetime_actions", None) + self.expires_in = kwargs.get("expires_in", None) + self.created_on = kwargs.get("created_on", None) + self.updated_on = kwargs.get("updated_on", None) + + @classmethod + def _from_generated(cls, policy): + lifetime_actions = [KeyRotationLifetimeAction._from_generated(action) for action in policy.lifetime_actions] # pylint:disable=protected-access + if policy.attributes: + return cls( + policy_id=policy.id, + lifetime_actions=lifetime_actions, + expires_on=policy.attributes.expiry_time, + created_on=policy.attributes.created, + updated_on=policy.attributes.updated, + ) + return cls(policy_id=policy.id, lifetime_actions=lifetime_actions) + + class KeyVaultKey(object): """A key's attributes and cryptographic material. diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py index 496844d1aa33..b1e835df7699 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py @@ -11,12 +11,20 @@ from .._shared._polling_async import AsyncDeleteRecoverPollingMethod from .._shared import AsyncKeyVaultClientBase from .._shared.exceptions import error_map as _error_map -from .. import DeletedKey, JsonWebKey, KeyProperties, KeyVaultKey, RandomBytes, ReleaseKeyResult +from .. import ( + DeletedKey, + JsonWebKey, + KeyProperties, + KeyRotationPolicy, + KeyVaultKey, + RandomBytes, + ReleaseKeyResult, +) if TYPE_CHECKING: # pylint:disable=ungrouped-imports from azure.core.async_paging import AsyncItemPaged - from typing import Any, Optional, Union + from typing import Any, Iterable, Optional, Union from .. import KeyType @@ -42,7 +50,7 @@ class KeyClient(AsyncKeyVaultClientBase): :dedent: 4 """ - # pylint:disable=protected-access + # pylint:disable=protected-access, too-many-public-methods def _get_attributes(self, enabled, not_before, expires_on, exportable=None): """Return a KeyAttributes object if none-None attributes are provided, or None otherwise""" @@ -702,3 +710,67 @@ async def get_random_bytes(self, count: int, **kwargs: "Any") -> RandomBytes: parameters = self._models.GetRandomBytesRequest(count=count) result = await self._client.get_random_bytes(vault_base_url=self._vault_url, parameters=parameters, **kwargs) return RandomBytes(value=result.value) + + @distributed_trace_async + async def get_key_rotation_policy(self, name: str, **kwargs: "Any") -> "KeyRotationPolicy": + """Get the rotation policy of a Key Vault key. + + :param str name: The name of the key. + + :return: The key rotation policy. + :rtype: ~azure.keyvault.keys.KeyRotationPolicy + :raises: :class:`~azure.core.exceptions.HttpResponseError` + """ + policy = await self._client.get_key_rotation_policy(vault_base_url=self._vault_url, key_name=name, **kwargs) + return KeyRotationPolicy._from_generated(policy) + + @distributed_trace_async + async def rotate_key(self, name: str, **kwargs: "Any") -> KeyVaultKey: + """Rotate the key based on the key policy by generating a new version of the key. + + This operation requires the keys/rotate permission. + + :param str name: The name of the key to rotate. + + :return: The new version of the rotated key. + :rtype: ~azure.keyvault.keys.KeyVaultKey + :raises: :class:`~azure.core.exceptions.HttpResponseError` + """ + bundle = await self._client.rotate_key(vault_base_url=self._vault_url, key_name=name, **kwargs) + return KeyVaultKey._from_key_bundle(bundle) + + @distributed_trace_async + async def update_key_rotation_policy(self, name: str, **kwargs: "Any") -> KeyRotationPolicy: + """Updates the rotation policy of a Key Vault key. + + This operation requires the keys/update permission. + + :param str name: The name of the key in the given vault. + + :keyword lifetime_actions: Actions that will be performed by Key Vault over the lifetime of a key. + :paramtype lifetime_actions: Iterable[~azure.keyvault.keys.KeyRotationLifetimeAction] + :keyword str expires_in: The expiry time of the policy that will be applied on new key versions, defined as an + ISO 8601 duration. For example: 90 days is "P90D", 3 months is "P3M", and 48 hours is "PT48H". + + :return: The updated rotation policy. + :rtype: ~azure.keyvault.keys.KeyRotationPolicy + :raises: :class:`~azure.core.exceptions.HttpResponseError` + """ + lifetime_actions = kwargs.pop("lifetime_actions", None) + if lifetime_actions: + lifetime_actions = [ + self._models.LifetimeActions( + action=self._models.LifetimeActionsType(type=action.action), + trigger=self._models.LifetimeActionsTrigger( + time_after_create=action.time_after_create, time_before_expiry=action.time_before_expiry + ), + ) + for action in lifetime_actions + ] + + attributes = self._models.KeyRotationPolicyAttributes(expiry_time=kwargs.pop("expires_in", None)) + policy = self._models.KeyRotationPolicy(lifetime_actions=lifetime_actions, attributes=attributes) + result = await self._client.update_key_rotation_policy( + vault_base_url=self._vault_url, key_name=name, key_rotation_policy=policy + ) + return KeyRotationPolicy._from_generated(result) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_key_client.test_key_rotation_7_3_preview_vault.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_key_client.test_key_rotation_7_3_preview_vault.yaml new file mode 100644 index 000000000000..bfb85abbeb9a --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_key_client.test_key_rotation_7_3_preview_vault.yaml @@ -0,0 +1,148 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-key1cff14c0/create?api-version=7.3-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"AKV10000: Request is missing + a Bearer or PoP token."}}' + headers: + cache-control: + - no-cache + content-length: + - '97' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 00:38:10 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + www-authenticate: + - Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 401 + message: Unauthorized +- request: + body: '{"kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '14' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-key1cff14c0/create?api-version=7.3-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/livekvtestrotation-key1cff14c0/8d038a3884a24dc997ef9d529debf31a","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"1aJXI-PznJu_7RfGmgUSvqRptbKTYSWOpEDJQShmUHOy8ri9TlC0r6vZ0Ek21dj6gpWpnxu91-0-IVOaLn4RRBl74OlcRiSGzzbXdVaRwTS9-GAbq7DfXKVP0ZSm37fexkPtrqk94uOqsybGbjUgJ2CF645b7yJAfcpqiQmx4i7NTriie1rH4CzXq5YaecOPw9fwZEh4Wqup6gl2mdAGAjZW72LCWsIvAccJLp2DQ4ZBJ-qMltWwKnMcDPMDRtOemo6FaZW41BKqCQy-e0fdHIWhyWp0GkqylJ3OumWAg50P3PjevWT_rltPovwcvOy4upvS4IEkKDJHVJFujE32fQ","e":"AQAB"},"attributes":{"enabled":true,"created":1632789491,"updated":1632789491,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '698' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 00:38:10 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-key1cff14c0/rotate?api-version=7.3-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/livekvtestrotation-key1cff14c0/7d2cb7c5012d41bd87ff36c5fa845cfc","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"wB5MHXYWo2l-8yOrk4bjheXi95VicMvJpSDInALbtMd6V9IzanKUKHoFD7sUrx5vBeApR07Vww9WPPWr-wAMQ8d6z7-qby7xF5SCWCZNWCXmKotUI9WH-ExRmZGzWCkbvsDE4fEh-uDLqh55-tGZSJMJRkc9DDl97_kdJyPILbwoQjQrAHghQJSC9JonWOS70GfTBKoIdgJrxKimHi61OnGDEIqnCuUbQmv9C3P_bNYMpV53F8M4Yxtcjmv_epiEh33rWtPXm-0S_kLbO3Ln0EoM09CaW1h5d3OdR9qy7r1YycpIaBHJHoF9LAbCUUTO8zUEDCKJglkfCmtRrZnL9Q","e":"AQAB"},"attributes":{"enabled":true,"created":1632789491,"updated":1632789491,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '698' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 00:38:11 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_key_client.test_key_rotation_policy_7_3_preview_vault.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_key_client.test_key_rotation_policy_7_3_preview_vault.yaml new file mode 100644 index 000000000000..5c04af45b520 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_key_client.test_key_rotation_policy_7_3_preview_vault.yaml @@ -0,0 +1,288 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/create?api-version=7.3-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"AKV10000: Request is missing + a Bearer or PoP token."}}' + headers: + cache-control: + - no-cache + content-length: + - '97' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 20:28:54 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + www-authenticate: + - Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 401 + message: Unauthorized +- request: + body: '{"kty": "RSA"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '14' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/create?api-version=7.3-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/1f1dcc214e0649298fc9d8431f133d70","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"xuqdP_vk8J3YrTpuVGWdaqKyYur6DxjrNIf8ytP5gbe5FNZ6gWdR3Owy8DhQoR699a9XLLzuqa-h7DasW_n-HXoZ_yRcJq4SHi-FG4PkZdJB9rh54l8dbzFn-8anGdTcVEPqFn9B1xOdf8aSZ1QIyW2io9gpLtpWY9rvQjtK8N332KWLxDHDxgOIq7jbexsa9DDWgKzcA_g37PEwpRYSb4NtJkvk4ztMa_OhEEwL7HIzUJjCQjKZRRGIVZxNWhakMQrdb4qhxQ8PNPmMFcuY9TVJ8hzVCZHQrsristcBcE1IyhvQoHYnCnDX3dD5FLyhPAoLVqmP6Z0_2w68-k-nfQ","e":"AQAB"},"attributes":{"enabled":true,"exp":1640636935,"created":1632860935,"updated":1632860935,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: + - no-cache + content-length: + - '715' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 20:28:55 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"lifetimeActions": [{"trigger": {"timeBeforeExpiry": "P30D"}, "action": + {"type": "Rotate"}}], "attributes": {"expiryTime": "P90D"}}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '132' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy","lifetimeActions":[{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Rotate"}},{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Notify"}}],"attributes":{"expiryTime":"P90D","created":1632795088,"updated":1632795088}}' + headers: + cache-control: + - no-cache + content-length: + - '327' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 20:28:55 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy","lifetimeActions":[{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Rotate"}},{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Notify"}}],"attributes":{"expiryTime":"P90D","created":1632795088,"updated":1632795088}}' + headers: + cache-control: + - no-cache + content-length: + - '327' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 20:28:55 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: '{"lifetimeActions": [{"trigger": {"timeAfterCreate": "P2M"}, "action": + {"type": "Notify"}}], "attributes": {}}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '110' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy","lifetimeActions":[{"trigger":{"timeAfterCreate":"P2M"},"action":{"type":"Notify"}}],"attributes":{"created":1632795088,"updated":1632860936}}' + headers: + cache-control: + - no-cache + content-length: + - '238' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 20:28:55 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keybd0f17af/rotationpolicy","lifetimeActions":[{"trigger":{"timeAfterCreate":"P2M"},"action":{"type":"Notify"}}],"attributes":{"created":1632795088,"updated":1632860936}}' + headers: + cache-control: + - no-cache + content-length: + - '238' + content-type: + - application/json; charset=utf-8 + date: + - Tue, 28 Sep 2021 20:28:56 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000;includeSubDomains + x-content-type-options: + - nosniff + x-ms-keyvault-network-info: + - conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: + - westus + x-ms-keyvault-service-version: + - 1.9.79.2 + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_keys_async.test_key_rotation_7_3_preview_vault.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_keys_async.test_key_rotation_7_3_preview_vault.yaml new file mode 100644 index 000000000000..5c5d154bff36 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_keys_async.test_key_rotation_7_3_preview_vault.yaml @@ -0,0 +1,100 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-key201714d2/create?api-version=7.3-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"AKV10000: Request is missing + a Bearer or PoP token."}}' + headers: + cache-control: no-cache + content-length: '97' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 02:39:57 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + www-authenticate: Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 401 + message: Unauthorized + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-key201714d2/create?api-version=7.3-preview +- request: + body: '{"kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '14' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-key201714d2/create?api-version=7.3-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/livekvtestrotation-key201714d2/efb9c8e922a647e0bac7c713c3266428","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"1vbwKr3d5P_CSOJfLHp0VWLlqeptK5NIh7raFt4aCuG2npczGgNKznjdFeUt3MborSWYyEY4DlZWGJsvA7TOAQ13k9-u9xe2g1wYfgNw5X8WAju2BO69ylBQ7aPEZeoEfO12ub0a3uvA0Ooct8OC-nyalyk7qZON8xJoj0Todagwq-njgwuZask3XJVAx-yGobwFcFQgynrq5ScRcHTJSKGnTp7FniJqAg6tD6nTUWq_RlmgQ6f5cok0AAvChauDzes4T-3glcMucqSJohAGyqXkcETiBOfZH5keVmFdi5n-9C6dRFq3OjJltl-JhVAI6nL18chVrZxF6C0qApcBVQ","e":"AQAB"},"attributes":{"enabled":true,"created":1632796798,"updated":1632796798,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '698' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 02:39:58 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-key201714d2/create?api-version=7.3-preview +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-key201714d2/rotate?api-version=7.3-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/livekvtestrotation-key201714d2/d91392d890cb485fa99f84391f7d845b","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"4SWEheIFMLmxTdx92NaXipleHUHoTuvqarTdQ4fhtAPzHtRdGKTjbCFjT-c21T7x0gkDhPJsCMzO9VjuLKvdBiRziZarxEU_1Sv9NGoWBRFdLbFGh5ZV1eDklQxXQuIxgIsz177fzI--ErbIhA1zVfpOuUbart1Kj99VZsqv8PhkwN065SfNrOrVtGmW1rhBZUA6-l7wVY10iTCgCfPww36LrTE66soNNt6rRk03_Xmi_08va3jzmg89MQ3UZL94W-dDXVU25vmNV4MIhclPxB15WwSEstLrJqvlxNzAbTwt9akzaWHaihVuK_Ug3sTl-ye232jdnpqdWIt8VgojEQ","e":"AQAB"},"attributes":{"enabled":true,"created":1632796798,"updated":1632796798,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '698' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 02:39:58 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-key201714d2/rotate?api-version=7.3-preview +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_keys_async.test_key_rotation_policy_7_3_preview_vault.yaml b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_keys_async.test_key_rotation_policy_7_3_preview_vault.yaml new file mode 100644 index 000000000000..343b4a0cc575 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-keys/tests/recordings/test_keys_async.test_key_rotation_policy_7_3_preview_vault.yaml @@ -0,0 +1,197 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Content-Length: + - '0' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/create?api-version=7.3-preview + response: + body: + string: '{"error":{"code":"Unauthorized","message":"AKV10000: Request is missing + a Bearer or PoP token."}}' + headers: + cache-control: no-cache + content-length: '97' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 20:30:13 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + www-authenticate: Bearer authorization="https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47", + resource="https://vault.azure.net" + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 401 + message: Unauthorized + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/create?api-version=7.3-preview +- request: + body: '{"kty": "RSA"}' + headers: + Accept: + - application/json + Content-Length: + - '14' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: POST + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/create?api-version=7.3-preview + response: + body: + string: '{"key":{"kid":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/d576940c117f4f3cb9ddb667c1042a8d","kty":"RSA","key_ops":["encrypt","decrypt","sign","verify","wrapKey","unwrapKey"],"n":"tLipS2sqtYKME4ffnq8-tGTxE8vCKp0zfIWclCIrorV-I1MzWsZzNUyyVfjoKvk2h1CKTVBar8d00eCvvPRHeSd-YalDTobAfmLM58xej3MT27f5p7t_sfkK3ZP5BoGssbKHXR_7RI2vvNb_VirqzF1leJKRu-FMs0xdjBfqaiqmyWQZFSLXEPoAkE3BQ42BsIrXhQFQVixoNMaiY9_LOb2zl2_MHhvKRw2ZO9DEIJ33IWFcH5crr2UklJxkAOmCVNKfRbZkxjPeGJKUxypoyVer7kWUNdleDPhupk6RWnGta9npIxSsB0DjQSrXf8G_McNB2Q4SBymfXrJRO8RREQ","e":"AQAB"},"attributes":{"enabled":true,"exp":1640637014,"created":1632861014,"updated":1632861014,"recoveryLevel":"Recoverable+Purgeable","recoverableDays":90}}' + headers: + cache-control: no-cache + content-length: '715' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 20:30:14 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/create?api-version=7.3-preview +- request: + body: '{"lifetimeActions": [{"trigger": {"timeBeforeExpiry": "P30D"}, "action": + {"type": "Rotate"}}], "attributes": {"expiryTime": "P90D"}}' + headers: + Accept: + - application/json + Content-Length: + - '132' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy","lifetimeActions":[{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Rotate"}},{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Notify"}}],"attributes":{"expiryTime":"P90D","created":1632796802,"updated":1632796802}}' + headers: + cache-control: no-cache + content-length: '327' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 20:30:14 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy","lifetimeActions":[{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Rotate"}},{"trigger":{"timeBeforeExpiry":"P30D"},"action":{"type":"Notify"}}],"attributes":{"expiryTime":"P90D","created":1632796802,"updated":1632796802}}' + headers: + cache-control: no-cache + content-length: '327' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 20:30:14 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview +- request: + body: '{"lifetimeActions": [{"trigger": {"timeAfterCreate": "P2M"}, "action": + {"type": "Notify"}}], "attributes": {}}' + headers: + Accept: + - application/json + Content-Length: + - '110' + Content-Type: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: PUT + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy","lifetimeActions":[{"trigger":{"timeAfterCreate":"P2M"},"action":{"type":"Notify"}}],"attributes":{"created":1632796802,"updated":1632861014}}' + headers: + cache-control: no-cache + content-length: '238' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 20:30:14 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-keyvault-keys/4.5.0b4 Python/3.9.0 (Windows-10-10.0.19041-SP0) + method: GET + uri: https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview + response: + body: + string: '{"id":"https://vaultname.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy","lifetimeActions":[{"trigger":{"timeAfterCreate":"P2M"},"action":{"type":"Notify"}}],"attributes":{"created":1632796802,"updated":1632861014}}' + headers: + cache-control: no-cache + content-length: '238' + content-type: application/json; charset=utf-8 + date: Tue, 28 Sep 2021 20:30:14 GMT + expires: '-1' + pragma: no-cache + strict-transport-security: max-age=31536000;includeSubDomains + x-content-type-options: nosniff + x-ms-keyvault-network-info: conn_type=Ipv4;addr=172.92.159.124;act_addr_fam=InterNetwork; + x-ms-keyvault-region: westus + x-ms-keyvault-service-version: 1.9.79.2 + x-powered-by: ASP.NET + status: + code: 200 + message: OK + url: https://mcpatinotest.vault.azure.net/keys/livekvtestrotation-keyc0a517c1/rotationpolicy?api-version=7.3-preview +version: 1 diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_key_client.py b/sdk/keyvault/azure-keyvault-keys/tests/test_key_client.py index 1609ededad21..e3ba694fd382 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_key_client.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_key_client.py @@ -11,7 +11,14 @@ from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError from azure.core.pipeline.policies import SansIOHTTPPolicy -from azure.keyvault.keys import ApiVersion, JsonWebKey, KeyClient, KeyReleasePolicy +from azure.keyvault.keys import ( + ApiVersion, + JsonWebKey, + KeyClient, + KeyReleasePolicy, + KeyRotationLifetimeAction, + KeyRotationPolicyAction, +) import pytest from six import byte2int @@ -22,10 +29,24 @@ all_api_versions = get_decorator() only_hsm = get_decorator(only_hsm=True) only_hsm_7_3_preview = get_decorator(only_hsm=True, api_versions=[ApiVersion.V7_3_PREVIEW]) +only_vault_7_3_preview = get_decorator(only_vault=True, api_versions=[ApiVersion.V7_3_PREVIEW]) logging_enabled = get_decorator(logging_enable=True) logging_disabled = get_decorator(logging_enable=False) +def _assert_rotation_policies_equal(p1, p2): + assert p1.id == p2.id + assert p1.expires_in == p2.expires_in + assert p1.created_on == p2.created_on + assert p1.updated_on == p2.updated_on + assert len(p1.lifetime_actions) == len(p2.lifetime_actions) + +def _assert_lifetime_actions_equal(a1, a2): + assert a1.action == a2.action + assert a1.time_after_create == a2.time_after_create + assert a1.time_before_expiry == a2.time_before_expiry + + # used for logging tests class MockHandler(logging.Handler): def __init__(self): @@ -508,6 +529,48 @@ def test_update_release_policy(self, client, **kwargs): self._update_key_properties(client, key, new_release_policy) + @only_vault_7_3_preview() + @client_setup + def test_key_rotation(self, client, **kwargs): + key_name = self.get_resource_name("rotation-key") + key = self._create_rsa_key(client, key_name) + rotated_key = client.rotate_key(key_name) + + # the rotated key should have a new ID, version, and key material (for RSA, n and e fields) + assert key.id != rotated_key.id + assert key.properties.version != rotated_key.properties.version + assert key.key.n != rotated_key.key.n + + @only_vault_7_3_preview() + @client_setup + def test_key_rotation_policy(self, client, **kwargs): + key_name = self.get_resource_name("rotation-key") + self._create_rsa_key(client, key_name) + + actions = [KeyRotationLifetimeAction(KeyRotationPolicyAction.ROTATE, time_before_expiry="P30D")] + updated_policy = client.update_key_rotation_policy(key_name, expires_in="P90D", lifetime_actions=actions) + fetched_policy = client.get_key_rotation_policy(key_name) + _assert_rotation_policies_equal(updated_policy, fetched_policy) + + updated_policy_actions = updated_policy.lifetime_actions[0] + fetched_policy_actions = fetched_policy.lifetime_actions[0] + assert updated_policy_actions.action == KeyRotationPolicyAction.ROTATE + assert updated_policy_actions.time_after_create is None + assert updated_policy_actions.time_before_expiry == "P30D" + _assert_lifetime_actions_equal(updated_policy_actions, fetched_policy_actions) + + new_actions = [KeyRotationLifetimeAction(KeyRotationPolicyAction.NOTIFY, time_after_create="P2M")] + new_policy = client.update_key_rotation_policy(key_name, lifetime_actions=new_actions) + new_fetched_policy = client.get_key_rotation_policy(key_name) + _assert_rotation_policies_equal(new_policy, new_fetched_policy) + + new_policy_actions = new_policy.lifetime_actions[0] + new_fetched_policy_actions = new_fetched_policy.lifetime_actions[0] + assert new_policy_actions.action == KeyRotationPolicyAction.NOTIFY + assert new_policy_actions.time_after_create == "P2M" + assert new_policy_actions.time_before_expiry is None + _assert_lifetime_actions_equal(new_policy_actions, new_fetched_policy_actions) + def test_positive_bytes_count_required(): client = KeyClient("...", object()) diff --git a/sdk/keyvault/azure-keyvault-keys/tests/test_keys_async.py b/sdk/keyvault/azure-keyvault-keys/tests/test_keys_async.py index 17a2604bd223..524ebb062c81 100644 --- a/sdk/keyvault/azure-keyvault-keys/tests/test_keys_async.py +++ b/sdk/keyvault/azure-keyvault-keys/tests/test_keys_async.py @@ -11,18 +11,26 @@ from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError from azure.core.pipeline.policies import SansIOHTTPPolicy -from azure.keyvault.keys import ApiVersion, JsonWebKey, KeyReleasePolicy +from azure.keyvault.keys import ( + ApiVersion, + JsonWebKey, + KeyReleasePolicy, + KeyRotationLifetimeAction, + KeyRotationPolicyAction, +) from azure.keyvault.keys.aio import KeyClient import pytest from six import byte2int from _shared.test_case_async import KeyVaultTestCase from _test_case import client_setup, get_attestation_token, get_decorator, get_release_policy, KeysTestCase +from test_key_client import _assert_lifetime_actions_equal, _assert_rotation_policies_equal all_api_versions = get_decorator(is_async=True) only_hsm = get_decorator(only_hsm=True, is_async=True) only_hsm_7_3_preview = get_decorator(only_hsm=True, is_async=True, api_versions=[ApiVersion.V7_3_PREVIEW]) +only_vault_7_3_preview = get_decorator(only_vault=True, is_async=True, api_versions=[ApiVersion.V7_3_PREVIEW]) logging_enabled = get_decorator(is_async=True, logging_enable=True) logging_disabled = get_decorator(is_async=True, logging_enable=False) @@ -538,6 +546,48 @@ async def test_update_release_policy(self, client, **kwargs): await self._update_key_properties(client, key, new_release_policy) + @only_vault_7_3_preview() + @client_setup + async def test_key_rotation(self, client, **kwargs): + key_name = self.get_resource_name("rotation-key") + key = await self._create_rsa_key(client, key_name) + rotated_key = await client.rotate_key(key_name) + + # the rotated key should have a new ID, version, and key material (for RSA, n and e fields) + assert key.id != rotated_key.id + assert key.properties.version != rotated_key.properties.version + assert key.key.n != rotated_key.key.n + + @only_vault_7_3_preview() + @client_setup + async def test_key_rotation_policy(self, client, **kwargs): + key_name = self.get_resource_name("rotation-key") + await self._create_rsa_key(client, key_name) + + actions = [KeyRotationLifetimeAction(KeyRotationPolicyAction.ROTATE, time_before_expiry="P30D")] + updated_policy = await client.update_key_rotation_policy(key_name, expires_in="P90D", lifetime_actions=actions) + fetched_policy = await client.get_key_rotation_policy(key_name) + _assert_rotation_policies_equal(updated_policy, fetched_policy) + + updated_policy_actions = updated_policy.lifetime_actions[0] + fetched_policy_actions = fetched_policy.lifetime_actions[0] + assert updated_policy_actions.action == KeyRotationPolicyAction.ROTATE + assert updated_policy_actions.time_after_create is None + assert updated_policy_actions.time_before_expiry == "P30D" + _assert_lifetime_actions_equal(updated_policy_actions, fetched_policy_actions) + + new_actions = [KeyRotationLifetimeAction(KeyRotationPolicyAction.NOTIFY, time_after_create="P2M")] + new_policy = await client.update_key_rotation_policy(key_name, lifetime_actions=new_actions) + new_fetched_policy = await client.get_key_rotation_policy(key_name) + _assert_rotation_policies_equal(new_policy, new_fetched_policy) + + new_policy_actions = new_policy.lifetime_actions[0] + new_fetched_policy_actions = new_fetched_policy.lifetime_actions[0] + assert new_policy_actions.action == KeyRotationPolicyAction.NOTIFY + assert new_policy_actions.time_after_create == "P2M" + assert new_policy_actions.time_before_expiry is None + _assert_lifetime_actions_equal(new_policy_actions, new_fetched_policy_actions) + @pytest.mark.asyncio async def test_positive_bytes_count_required(): diff --git a/sdk/keyvault/test-resources.json b/sdk/keyvault/test-resources.json index cadd04d8b6d5..9268e5237874 100644 --- a/sdk/keyvault/test-resources.json +++ b/sdk/keyvault/test-resources.json @@ -89,6 +89,7 @@ } }, "variables": { + "kvApiVersion": "2019-09-01", "azureKeyVaultUrl": "[format('https://{0}{1}', parameters('baseName'), parameters('keyVaultDomainSuffix'))]", "hsmApiVersion": "2021-04-01-preview", "hsmName": "[concat(parameters('baseName'), 'hsm')]", @@ -113,7 +114,7 @@ "resources": [ { "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2016-10-01", + "apiVersion": "[variables('kvApiVersion')]", "name": "[parameters('baseName')]", "location": "[parameters('location')]", "properties": { @@ -143,7 +144,8 @@ "wrapKey", "verify", "sign", - "purge" + "purge", + "rotate" ], "secrets": [ "get",