From 27eead3b1ba2ccf1a26d27a09c6ff1a054c58651 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Fri, 4 Sep 2020 15:24:39 -0700 Subject: [PATCH] Implement full vault backup and restore (#13525) --- .../CHANGELOG.md | 6 +- .../azure/keyvault/administration/__init__.py | 13 ++- .../keyvault/administration/_backup_client.py | 95 ++++++++++++++++ .../administration/_internal/polling.py | 13 +++ .../azure/keyvault/administration/_models.py | 58 ++++++++++ .../azure/keyvault/administration/_version.py | 2 +- .../keyvault/administration/aio/__init__.py | 3 +- .../administration/aio/_backup_client.py | 104 ++++++++++++++++++ 8 files changed, 290 insertions(+), 4 deletions(-) create mode 100644 sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py create mode 100644 sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py create mode 100644 sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py diff --git a/sdk/keyvault/azure-keyvault-administration/CHANGELOG.md b/sdk/keyvault/azure-keyvault-administration/CHANGELOG.md index 332564950c28..0e966c4e71db 100644 --- a/sdk/keyvault/azure-keyvault-administration/CHANGELOG.md +++ b/sdk/keyvault/azure-keyvault-administration/CHANGELOG.md @@ -1,3 +1,7 @@ # Release History -## 1.0.0b1 (Unreleased) +## 4.0.0b1 (2020-09-08) +### Added +- `KeyVaultAccessControlClient` performs role-based access control operations +- `KeyVaultBackupClient` performs full vault backup and full and selective + restore operations diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/__init__.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/__init__.py index f512df41adc1..1db879dd4265 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/__init__.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/__init__.py @@ -3,17 +3,28 @@ # Licensed under the MIT License. # ------------------------------------ from ._access_control_client import KeyVaultAccessControlClient +from ._backup_client import KeyVaultBackupClient from ._internal.client_base import ApiVersion from ._models import ( - KeyVaultPermission, KeyVaultRoleAssignment, KeyVaultRoleDefinition, KeyVaultRoleScope + BackupOperation, + KeyVaultPermission, + KeyVaultRoleAssignment, + KeyVaultRoleDefinition, + KeyVaultRoleScope, + RestoreOperation, + SelectiveKeyRestoreOperation, ) __all__ = [ "ApiVersion", + "BackupOperation", "KeyVaultAccessControlClient", + "KeyVaultBackupClient", "KeyVaultPermission", "KeyVaultRoleAssignment", "KeyVaultRoleDefinition", "KeyVaultRoleScope", + "RestoreOperation", + "SelectiveKeyRestoreOperation", ] diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py new file mode 100644 index 000000000000..39666d43dc6b --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_backup_client.py @@ -0,0 +1,95 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import TYPE_CHECKING + +from azure.core.polling.base_polling import LROBasePolling + +from ._models import BackupOperation, RestoreOperation, SelectiveKeyRestoreOperation +from ._internal import KeyVaultClientBase +from ._internal.polling import KeyVaultBackupClientPolling + +if TYPE_CHECKING: + # pylint:disable=unused-import + from typing import Any + from azure.core.polling import LROPoller + + +class KeyVaultBackupClient(KeyVaultClientBase): + """Performs Key Vault backup and restore operations. + + :param str vault_url: URL of the vault on which the client will operate. This is also called the vault's "DNS Name". + :param credential: an object which can provide an access token for the vault, such as a credential from + :mod:`azure.identity` + """ + + # pylint:disable=protected-access + def begin_full_backup(self, blob_storage_uri, sas_token, **kwargs): + # type: (str, str, **Any) -> LROPoller[BackupOperation] + """Begin a full backup of the Key Vault. + + :param str blob_storage_uri: URI of the blob storage resource in which the backup will be stored + :param str sas_token: a Shared Access Signature (SAS) token authorizing access to the blob storage resource + :keyword str continuation_token: a continuation token to restart polling from a saved state + :returns: An instance of an LROPoller. Call `result()` on the poller object to get a :class:`BackupOperation`. + :rtype: ~azure.core.polling.LROPoller[BackupOperation] + """ + polling_interval = kwargs.pop("_polling_interval", 5) + sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_uri, token=sas_token) + return self._client.begin_full_backup( + vault_base_url=self._vault_url, + azure_storage_blob_container_uri=sas_parameter, + cls=BackupOperation._wrap_generated, + continuation_token=kwargs.pop("continuation_token", None), + polling=LROBasePolling(lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs), + **kwargs + ) + + def begin_full_restore(self, blob_storage_uri, sas_token, folder_name, **kwargs): + # type: (str, str, str, **Any) -> LROPoller[RestoreOperation] + """Restore a full backup of a Key Vault. + + :param str blob_storage_uri: URI of the blob storage resource in which the backup is stored + :param str sas_token: a Shared Access Signature (SAS) token authorizing access to the blob storage resource + :param str folder_name: name of the blob container which contains the backup + :rtype: ~azure.core.polling.LROPoller[RestoreOperation] + """ + polling_interval = kwargs.pop("_polling_interval", 5) + sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_uri, token=sas_token) + restore_details = self._models.RestoreOperationParameters( + sas_token_parameters=sas_parameter, folder_to_restore=folder_name, + ) + return self._client.begin_full_restore_operation( + vault_base_url=self._vault_url, + restore_blob_details=restore_details, + cls=RestoreOperation._wrap_generated, + continuation_token=kwargs.pop("continuation_token", None), + polling=LROBasePolling(lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs), + **kwargs + ) + + def begin_selective_restore(self, blob_storage_uri, sas_token, folder_name, key_name, **kwargs): + # type: (str, str, str, str, **Any) -> LROPoller[SelectiveKeyRestoreOperation] + """Restore a single key from a full Key Vault backup. + + :param str blob_storage_uri: URI of the blob storage resource in which the backup is stored + :param str sas_token: a Shared Access Signature (SAS) token authorizing access to the blob storage resource + :param str folder_name: name of the blob container which contains the backup + :param str key_name: name of the key to restore from the backup + :rtype: ~azure.core.polling.LROPoller[RestoreOperation] + """ + polling_interval = kwargs.pop("_polling_interval", 5) + sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_uri, token=sas_token) + restore_details = self._models.SelectiveKeyRestoreOperationParameters( + sas_token_parameters=sas_parameter, folder=folder_name, + ) + return self._client.begin_selective_key_restore_operation( + vault_base_url=self._vault_url, + key_name=key_name, + restore_blob_details=restore_details, + cls=SelectiveKeyRestoreOperation._wrap_generated, + continuation_token=kwargs.pop("continuation_token", None), + polling=LROBasePolling(lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs), + **kwargs + ) diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py new file mode 100644 index 000000000000..b4fe58cead15 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_internal/polling.py @@ -0,0 +1,13 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from azure.core.polling.base_polling import OperationResourcePolling + + +class KeyVaultBackupClientPolling(OperationResourcePolling): + def __init__(self): + super(KeyVaultBackupClientPolling, self).__init__(operation_location_header="azure-asyncoperation") + + def get_final_get_url(self, pipeline_response): + return None diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_models.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_models.py index 28b35b68930e..93f4c1bc99f6 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_models.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_models.py @@ -174,3 +174,61 @@ def _from_generated(cls, definition): role_type=definition.role_type, type=definition.type, ) + + +class _Operation(object): + def __init__(self, **kwargs): + self.status = kwargs.get("status", None) + self.status_details = kwargs.get("status_details", None) + self.error = kwargs.get("error", None) + self.start_time = kwargs.get("start_time", None) + self.end_time = kwargs.get("end_time", None) + self.id = kwargs.get("job_id", None) + + @classmethod + def _wrap_generated(cls, response, deserialized_operation, response_headers): # pylint:disable=unused-argument + return cls(**deserialized_operation.__dict__) + + +class BackupOperation(_Operation): + """A Key Vault full backup operation. + + :ivar str status: status of the backup operation + :ivar str status_details: more details of the operation's status + :ivar error: Error encountered, if any, during the operation + :type error: ~key_vault_client.models.Error + :ivar datetime.datetime start_time: UTC start time of the operation + :ivar datetime.datetime end_time: UTC end time of the operation + :ivar str job_id: identifier for the operation + :ivar str azure_storage_blob_container_uri: URI of the Azure blob storage container which contains the backup + """ + + def __init__(self, **kwargs): + self.azure_storage_blob_container_uri = kwargs.pop("azure_storage_blob_container_uri", None) + super(BackupOperation, self).__init__(**kwargs) + + +class RestoreOperation(_Operation): + """A Key Vault restore operation. + + :ivar str status: status of the operation + :ivar str status_details: more details of the operation's status + :ivar error: Error encountered, if any, during the operation + :type error: ~key_vault_client.models.Error + :ivar datetime.datetime start_time: UTC start time of the operation + :ivar datetime.datetime end_time: UTC end time of the operation + :ivar str job_id: identifier for the operation + """ + + +class SelectiveKeyRestoreOperation(_Operation): + """A Key Vault operation restoring a single key. + + :ivar str status: status of the operation + :ivar str status_details: more details of the operation's status + :ivar error: Error encountered, if any, during the operation + :type error: ~key_vault_client.models.Error + :ivar datetime.datetime start_time: UTC start time of the operation + :ivar datetime.datetime end_time: UTC end time of the operation + :ivar str job_id: identifier for the operation + """ diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_version.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_version.py index ac9f392f513e..ccaa7193d0b4 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_version.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/_version.py @@ -3,4 +3,4 @@ # Licensed under the MIT License. # ------------------------------------ -VERSION = "1.0.0b1" +VERSION = "4.0.0b1" diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/__init__.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/__init__.py index 45ea36c883e7..af5affe57e5a 100644 --- a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/__init__.py +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/__init__.py @@ -3,5 +3,6 @@ # Licensed under the MIT License. # ------------------------------------ from ._access_control_client import KeyVaultAccessControlClient +from ._backup_client import KeyVaultBackupClient -__all__ = ["KeyVaultAccessControlClient"] +__all__ = ["KeyVaultAccessControlClient", "KeyVaultBackupClient"] diff --git a/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py new file mode 100644 index 000000000000..b04faa321b21 --- /dev/null +++ b/sdk/keyvault/azure-keyvault-administration/azure/keyvault/administration/aio/_backup_client.py @@ -0,0 +1,104 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import TYPE_CHECKING + +from azure.core.polling.async_base_polling import AsyncLROBasePolling + +from .._internal import AsyncKeyVaultClientBase +from .._internal.polling import KeyVaultBackupClientPolling +from .._models import BackupOperation, RestoreOperation, SelectiveKeyRestoreOperation + +if TYPE_CHECKING: + # pylint:disable=unused-import + from typing import Any + from azure.core.polling import AsyncLROPoller + + +class KeyVaultBackupClient(AsyncKeyVaultClientBase): + """Performs Key Vault backup and restore operations. + + :param str vault_url: URL of the vault on which the client will operate. This is also called the vault's "DNS Name". + :param credential: an object which can provide an access token for the vault, such as a credential from + :mod:`azure.identity.aio` + """ + + # pylint:disable=protected-access + async def begin_full_backup( + self, blob_storage_uri: str, sas_token: str, **kwargs: "Any" + ) -> "AsyncLROPoller[BackupOperation]": + """Begin a full backup of the Key Vault. + + :param str blob_storage_uri: URI of the blob storage resource in which the backup will be stored + :param str sas_token: a Shared Access Signature (SAS) token authorizing access to the blob storage resource + :keyword str continuation_token: a continuation token to restart polling from a saved state + :returns: An AsyncLROPoller. Call `result()` on this object to get a :class:`BackupOperation`. + :rtype: ~azure.core.polling.AsyncLROPoller[BackupOperation] + """ + polling_interval = kwargs.pop("_polling_interval", 5) + sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_uri, token=sas_token) + return await self._client.begin_full_backup( + vault_base_url=self._vault_url, + azure_storage_blob_container_uri=sas_parameter, + cls=BackupOperation._wrap_generated, + continuation_token=kwargs.pop("continuation_token", None), + polling=AsyncLROBasePolling( + lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs + ), + **kwargs + ) + + async def begin_full_restore( + self, blob_storage_uri: str, sas_token: str, folder_name: str, **kwargs: "Any" + ) -> "AsyncLROPoller[RestoreOperation]": + """Restore a full backup of a Key Vault. + + :param str blob_storage_uri: URI of the blob storage resource in which the backup is stored + :param str sas_token: a Shared Access Signature (SAS) token authorizing access to the blob storage resource + :param str folder_name: name of the blob container which contains the backup + :rtype: ~azure.core.polling.AsyncLROPoller[RestoreOperation] + """ + polling_interval = kwargs.pop("_polling_interval", 5) + sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_uri, token=sas_token) + restore_details = self._models.RestoreOperationParameters( + sas_token_parameters=sas_parameter, folder_to_restore=folder_name, + ) + return await self._client.begin_full_restore_operation( + vault_base_url=self._vault_url, + restore_blob_details=restore_details, + cls=RestoreOperation._wrap_generated, + continuation_token=kwargs.pop("continuation_token", None), + polling=AsyncLROBasePolling( + lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs + ), + **kwargs + ) + + async def begin_selective_restore( + self, blob_storage_uri: str, sas_token: str, folder_name: str, key_name: str, **kwargs: "Any" + ) -> "AsyncLROPoller[SelectiveKeyRestoreOperation]": + """Restore a single key from a full Key Vault backup. + + :param str blob_storage_uri: URI of the blob storage resource in which the backup is stored + :param str sas_token: a Shared Access Signature (SAS) token authorizing access to the blob storage resource + :param str folder_name: name of the blob container which contains the backup + :param str key_name: name of the key to restore from the backup + :rtype: ~azure.core.polling.AsyncLROPoller[RestoreOperation] + """ + polling_interval = kwargs.pop("_polling_interval", 5) + sas_parameter = self._models.SASTokenParameter(storage_resource_uri=blob_storage_uri, token=sas_token) + restore_details = self._models.SelectiveKeyRestoreOperationParameters( + sas_token_parameters=sas_parameter, folder=folder_name, + ) + return await self._client.begin_selective_key_restore_operation( + vault_base_url=self._vault_url, + key_name=key_name, + restore_blob_details=restore_details, + cls=SelectiveKeyRestoreOperation._wrap_generated, + continuation_token=kwargs.pop("continuation_token", None), + polling=AsyncLROBasePolling( + lro_algorithms=[KeyVaultBackupClientPolling()], timeout=polling_interval, **kwargs + ), + **kwargs + )