From ec6a66d53ef92eaa54d24ac2b500889a470c1193 Mon Sep 17 00:00:00 2001 From: George Arama <50641385+gearama@users.noreply.github.com> Date: Wed, 31 Jul 2024 10:29:41 -0700 Subject: [PATCH] Keyvault full backup/restore support (#5744) * first build pass * move models Refactor KeyVault backup/restore client This commit represents a significant refactoring of the Azure Security KeyVault Administration client library, transitioning from a generic `KeyVaultClient` to a more specialized `BackupRestoreClient`. The refactoring includes: - Renaming `KeyVaultClient` to `BackupRestoreClient` in both `backup_restore_client.hpp` and `backup_restore_client.cpp` to better reflect the focused functionality on backup and restore operations. - Moving model definitions from `backup_restore_client.hpp` to `rest_client_models.hpp`, including structures for errors, full backup, restore, and selective key restore operations, along with their options. This change centralizes model definitions for improved maintainability and reuse across the client library. - Updating method implementations in `backup_restore_client.cpp` to align with the `BackupRestoreClient` class name and its specialized backup and restore operations. These changes aim to provide clearer separation of concerns within the library, making it more manageable and focused on backup and restore functionalities for Azure KeyVault. * Add BackupRestoreClient with tests and options This commit introduces the BackupRestoreClient for Azure Key Vault backup and restore operations, along with BackupRestoreClientOptions for configuration. Significant refactoring within the Azure::Security::KeyVault::Administration namespace simplifies namespace usage by directly using `using namespace Models;`. The BackupRestoreClient has been enhanced with a new constructor accepting `vaultUrl`, a `TokenCredential` shared pointer, and optionally `BackupRestoreClientOptions`. Additionally, a copy constructor and a default destructor have been added, alongside a private member `m_apiVersion` for API version management. The BackupRestoreClientOptions struct extends Azure::Core::_internal::ClientOptions with an `ApiVersion` member initialized to "7.5". Changes in the implementation file include additional headers for internal use and authentication, and updates to the constructor for initializing member variables and setting up the HTTP pipeline. Test infrastructure has been enhanced with modifications in `CMakeLists.txt` to include test source files, the introduction of a base test class `BackupRestoreClientTest` for setup, and a test case `CreateClient1` demonstrating client creation. These changes collectively enhance the Azure Key Vault Administration library by adding a new client for backup and restore operations, improving code organization, and setting up a testing framework. * Enhance BackupRestoreClient and tests This commit significantly updates the BackupRestoreClient and its associated models, introducing new features for backup and restore operations, and improving error handling with a new Error model. The `BackupRestoreClient` class has been marked as `final` to prevent inheritance, and several new methods such as `FullBackup`, `FullBackupStatus`, `FullRestore`, `RestoreStatus`, and `SelectiveKeyRestore` have been added, along with corresponding data models in `rest_client_models.hpp`. These changes enhance the client's functionality and API clarity. Additionally, the test infrastructure has seen substantial improvements, including refactoring in `backup_restore_client_base_test.hpp` for better encapsulation and updated utility methods for more effective testing. The practical application of these enhancements is demonstrated in `backup_restore_client_test.cpp` through the `CreateClient1` test case, showcasing the use of the `FullBackup` method. Overall, these updates bolster the Azure Key Vault Backup and Restore client library's capabilities and test support. * Enhance BackupRestoreClient for Azure backups - Updated `BackupRestoreClient` to support full backups and restores using Azure blob storage SAS tokens, including changes to method signatures for SAS token and job identifier parameters. - Introduced nullable types for `StatusDetails` and `Error` in operation models to handle optional response fields. - Removed `FullBackupOptions`, `FullRestoreOperationOptions`, and `RestoreStatusOptions` structures in favor of direct parameter usage in methods. - Refined HTTP request construction to include necessary headers and dynamically set API version. - Updated JSON parsing logic to handle nullable fields and adapt to changed response JSON structure. - Enhanced test infrastructure with methods for creating test-configured `BackupRestoreClient` and generating SAS tokens for backups. Added new tests for full backup, backup status, full restore, and restore status functionalities. - Adjusted URL path construction and query parameter setting in HTTP requests for updated API endpoints and parameters compatibility. * Refine SelectiveKeyRestore API and tests This commit overhauls the SelectiveKeyRestore function in the Azure Key Vault Backup and Restore client library. The function signature in `backup_restore_client.hpp` has been updated to replace the `SelectiveKeyRestoreOperationOptions` parameter with two distinct parameters: `keyName` (std::string) and `restoreBlobDetails` (SelectiveKeyRestoreOperationParameters). This change enhances the API's clarity and usability by explicitly requiring the key name and restore blob details for the operation. Additionally, the `SelectiveKeyRestoreOperationOptions` structure has been removed from `rest_client_models.hpp`, aligning with the shift towards using direct parameters for the selective key restore operation. Corresponding implementation adjustments in `backup_restore_client.cpp` include changes to request URL and JSON body construction, as well as improved error handling in response parsing. The test suite in `backup_restore_client_test.cpp` has also been updated. The `CreateClient1` test case was removed, and a new test case, `RestoreSelectiveStatus`, was added to demonstrate the updated API's functionality through a full backup and selective key restore operation scenario. These changes collectively improve the API's intuitiveness and demonstrate its practical application through updated test cases. * oops * fds * negative tests Enhance backup/restore tests and cleanups - Added a new test case `BackupFullErrorStatus` to the `BackupRestoreClientTest` suite to simulate a full backup operation with an invalid URI and verify the transition of backup status from "InProgress" to "Failed" with detailed error status. - Refined the `RestoreFull` test case by removing unnecessary line breaks, improving code formatting without affecting functionality. - Adjusted formatting in the `RestoreFullStatus` test by removing unnecessary braces and line breaks, streamlining the code. - Improved readability in the `RestoreSelectiveStatus` test by adding a space after a comma. - Introduced a new test case `RestoreSelectiveInvalidKeyStatus` to test the system's handling of invalid key restore requests, ensuring error reporting is accurate when keys or their versions are not found. * recordings and pipeline * linux build issues * comment * parse the error type * fix paths * ewrwe * fdss * reencode ansi * fdsfsd * Enhance samples and refine project structure - Adjusted `CMakeLists.txt` for better project clarity, moving `BUILD_SAMPLES_HSM` from `test/samples` to `samples` and adding new sample directories for backup and restore functionalities. - Updated `sample1_administration.cpp` to use `Azure::Core::_internal::Environment::GetVariable` for environment variable access and removed settings management code to focus on specific functionalities. - Added new sample applications `sample2_full_backup_restore.cpp` and `sample3_backup_selective_restore.cpp` demonstrating full and selective backup and restore operations using the Azure Key Vault administration client library, including detailed steps and error handling. - Included `CMakeLists.txt` for each new sample, specifying C++ standards and linking necessary libraries, with a focus on ease of use and configuration through the inclusion of `get-env-helper`. * restore sample 1 * attempt reformat doc * gfddgdf * attempt 2 * are you serious ? * treter * docs * typo * changelog * doc * KHlAAAAAAAAAAANg said Kirk * error check * Updates to API * clangs * test new macro use in cmake file * clang again * missing include * update1 * LROs * clang 11 * attempt2 * fhkwerjhfkj * documentation * attempt++ * separating the op in its own files * clang * docs ++ * docc +++ * clang * typo * slkjlkdsjflks * rename to backupclient * hoping it's building * PR comments * address magic param check * assets update --- sdk/keyvault/assets.json | 2 +- .../CHANGELOG.md | 2 + .../CMakeLists.txt | 8 +- .../README.md | 109 ++++++ .../inc/azure/keyvault/administration.hpp | 2 + .../keyvault/administration/backup_client.hpp | 138 ++++++++ .../administration/backup_operation.hpp | 121 +++++++ .../administration/rest_client_models.hpp | 107 ++++++ .../{test => }/samples/CMakeLists.txt | 2 + .../sample1-basic-operations/CMakeLists.txt | 0 .../sample1_administration.cpp | 3 +- .../samples/sample1_administration.md | 2 +- .../CMakeLists.txt | 16 + .../sample2_full_backup_restore.cpp | 82 +++++ .../samples/sample2_full_backup_restore.md | 91 +++++ .../CMakeLists.txt | 16 + .../sample3_backup_selective_restore.cpp | 84 +++++ .../sample3_backup_selective_restore.md | 93 +++++ .../src/backup_client.cpp | 329 ++++++++++++++++++ .../src/backup_operation.cpp | 67 ++++ .../test/ut/CMakeLists.txt | 5 +- .../ut/backup_restore_client_base_test.hpp | 115 ++++++ .../test/ut/backup_restore_client_test.cpp | 310 +++++++++++++++++ sdk/keyvault/ci.yml | 4 + 24 files changed, 1701 insertions(+), 7 deletions(-) create mode 100644 sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_client.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_operation.hpp rename sdk/keyvault/azure-security-keyvault-administration/{test => }/samples/CMakeLists.txt (61%) rename sdk/keyvault/azure-security-keyvault-administration/{test => }/samples/sample1-basic-operations/CMakeLists.txt (100%) rename sdk/keyvault/azure-security-keyvault-administration/{test => }/samples/sample1-basic-operations/sample1_administration.cpp (93%) create mode 100644 sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/CMakeLists.txt create mode 100644 sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/sample2_full_backup_restore.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-administration/samples/sample2_full_backup_restore.md create mode 100644 sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/CMakeLists.txt create mode 100644 sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/sample3_backup_selective_restore.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-administration/samples/sample3_backup_selective_restore.md create mode 100644 sdk/keyvault/azure-security-keyvault-administration/src/backup_client.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-administration/src/backup_operation.cpp create mode 100644 sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_base_test.hpp create mode 100644 sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_test.cpp diff --git a/sdk/keyvault/assets.json b/sdk/keyvault/assets.json index 59fc49329..1fcadfa3e 100644 --- a/sdk/keyvault/assets.json +++ b/sdk/keyvault/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "cpp", "TagPrefix": "cpp/keyvault", - "Tag": "cpp/keyvault_b43656c9a5" + "Tag": "cpp/keyvault_e1582c490f" } diff --git a/sdk/keyvault/azure-security-keyvault-administration/CHANGELOG.md b/sdk/keyvault/azure-security-keyvault-administration/CHANGELOG.md index 1852a1d73..2889846fd 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/CHANGELOG.md +++ b/sdk/keyvault/azure-security-keyvault-administration/CHANGELOG.md @@ -4,6 +4,8 @@ ### Features Added +- Add support for Backup/Restore operations for Key Vault HSM. + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/keyvault/azure-security-keyvault-administration/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-administration/CMakeLists.txt index 3eacddb46..5f70eb301 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-administration/CMakeLists.txt @@ -48,6 +48,8 @@ endif() set( AZURE_SECURITY_KEYVAULT_ADMINISTRATION_HEADER inc/azure/keyvault/administration.hpp + inc/azure/keyvault/administration/backup_client.hpp + inc/azure/keyvault/administration/backup_operation.hpp inc/azure/keyvault/administration/dll_import_export.hpp inc/azure/keyvault/administration/rest_client_models.hpp inc/azure/keyvault/administration/rtti.hpp @@ -57,6 +59,8 @@ set( set( AZURE_SECURITY_KEYVAULT_ADMINISTRATION_SOURCE + src/backup_client.cpp + src/backup_operation.cpp src/keyvault_settings_common_request.cpp src/private/administration_constants.hpp src/private/keyvault_settings_common_request.hpp @@ -106,8 +110,8 @@ if (BUILD_PERFORMANCE_TESTS) #add_subdirectory(test/perf) endif() -if(BUILD_SAMPLES_HSM) - add_subdirectory(test/samples) +if(BUILD_SAMPLES) + add_subdirectory(samples) endif() az_vcpkg_export( diff --git a/sdk/keyvault/azure-security-keyvault-administration/README.md b/sdk/keyvault/azure-security-keyvault-administration/README.md index 91557748e..403360922 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/README.md +++ b/sdk/keyvault/azure-security-keyvault-administration/README.md @@ -130,6 +130,115 @@ To update the value of any of the the available settings, we will call the Updat Setting updatedSetting = settingsClient.UpdateSetting(settingsList.Value[0].Name, options).Value; ``` +## Creating a BackupClient + +To create a new `BackupClient` to perform these operations, you need the endpoint to an Azure Key Vault HSM and credentials. + +Key Vault BackupClient client for C++ currently supports any `TokenCredential` for authenticating. + +```cpp + auto credential + = std::make_shared(); +``` + +Then, in the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application. + +```cpp + // create client + BackupClient client(std::getenv("AZURE_KEYVAULT_HSM_URL"), credential); +``` +## Create the SasTokenParameter + +Since these operations require a blob storage for the backup/restore operations, a SAS token is required for the connection between the services(Key Vault and Storage). + +In this sample we rely on a couple of extra environment variables. + +```cpp + SasTokenParameter sasTokenParameter; + // the backup/restore needs a SAS token to access the storage account + sasTokenParameter.Token + = Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_TOKEN"); + // the backup/restore needs a url to a blob storage resource + Azure::Core::Url blobUrl + = Azure::Core::Url(Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_URL")); +``` + +## The Backup operation + +Since this is a long running operation the service provides endpoints to determine the status while the opperation is running. + +### Starting the backup operation + +```cpp +// Create a full backup using a user-provided SAS token to an Azure blob storage container. +auto backupResponse = client.FullBackup(blobUrl, sasTokenParameter).Value; + +std::cout << "Backup Job Id: " << backupResponse.Value().JobId << std::endl + << "Backup Status: " << backupResponse.Value().Status << std::endl; +``` + +### Backup operation waiting + +In order to wait for the operation to complete we will call the polling method. + +```cpp +// Wait for the operation to complete. +auto backupStatus = backupResponse.PollUntilDone(10s); + +std::cout << "Backup Job Id: " << backupStatus.Value.JobId << std::endl + << "Backup Status: " << backupStatus.Value.Status << std::endl; +``` + +## The FullRestore operation + +Similar to the backup operation after we initialize the operation we can check the status. + +### Starting the restore operation + +the restore operation requires a folder where a backup was previously performed along side the SAS token parameter. +```cpp +// Restore the full backup using a user-provided SAS token to an Azure blob storage container. +std::cout << "Folder to restore: " << folderToRestore << std::endl; +auto restoreResponse = client.FullRestore(blobUrl, folderToRestore, sasTokenParameter).Value; +std::cout << "Restore Job Id: " << restoreResponse.Value().JobId << std::endl + << "Restore Status: " << restoreResponse.Value().Status << std::endl; +``` + +### FullRestore operation waiting + +```cpp +// Wait for the operation to complete. +auto restoreStatus = restoreResponse.PollUntilDone(10s); +std::cout << "Restore Job Id: " << restoreStatus.Value.JobId << std::endl + << "Restore Status: " << restoreStatus.Value.Status << std::endl; +``` + +## The SelectiveRestore operation + +Similar to the backup operation after we initialize the operation we can check the status. + +### Starting the restore operation + +The selective restore operation requires a folder where a backup was previously performed along side the SAS token parameter. + +```cpp +// Restore the full backup using a user-provided SAS token to an Azure blob storage container. +std::string folderToRestore = ...; +std::cout << "Folder to restore: " << restoreBlobDetails.FolderToRestore << std::endl; +auto selectiveRestore = client.SelectiveKeyRestore("keyName", blobUrl, folderToRestore, sasTokenParameter); +std::cout << "Restore Job Id: " << restoreResponse.Value.JobId << std::endl + << "Restore Status: " << restoreResponse.Value.Status << std::endl; +``` + +### Selective restore operation completion + +```cpp +// Wait for the operation to complete. +auto selectiveStatus = selectiveRestore.PollUntilDone(10s); +std::cout << "Selective Restore Job Id: " << selectiveStatus.Value.JobId << std::endl + << "Selective Restore Status: " << selectiveStatus.Value.Status << std::endl; +``` + ## Contributing For details on contributing to this repository, see the [contributing guide][azure_sdk_for_cpp_contributing]. diff --git a/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration.hpp b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration.hpp index 645cbeb55..2e1c66a35 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration.hpp +++ b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration.hpp @@ -8,6 +8,8 @@ #pragma once +#include "azure/keyvault/administration/backup_client.hpp" +#include "azure/keyvault/administration/backup_operation.hpp" #include "azure/keyvault/administration/dll_import_export.hpp" #include "azure/keyvault/administration/rest_client_models.hpp" #include "azure/keyvault/administration/rtti.hpp" diff --git a/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_client.hpp b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_client.hpp new file mode 100644 index 000000000..768c3b07c --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_client.hpp @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#pragma once +#include "azure/keyvault/administration/backup_operation.hpp" +#include "azure/keyvault/administration/rest_client_models.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Azure::Security::KeyVault::Administration::Models; + +namespace Azure { namespace Security { namespace KeyVault { namespace Administration { + /** + * @brief Backup restore client. + * + */ + class BackupClient final { + public: + /** + * @brief Destructor. + * + */ + virtual ~BackupClient() = default; + + /** + * @brief Construct a new BackupClient object + * + * @param vaultUrl The URL address where the client will send the requests to. + * @param credential The authentication method to use. + * @param options The options to customize the client behavior. + */ + explicit BackupClient( + std::string const& vaultUrl, + std::shared_ptr credential, + BackupClientOptions options = BackupClientOptions()); + + /** + * @brief Creates a full backup using a user-provided SAS token to an Azure blob storage + * container. + * + * @param blobContainerUrl The URL for the blob storage resource. + * @param sasToken Azure blob shared access signature token pointing to a + * valid Azure blob container where full backup needs to be + * stored. This token needs to be valid for at least next 24 + * hours from the time of making this call. + * @param context The context for the operation can be used for request cancellation. + * @return A backup restore operation. + */ + Response FullBackup( + Azure::Core::Url const& blobContainerUrl, + SasTokenParameter const& sasToken, + Core::Context const& context = {}); + + /** + * @brief Returns the status of full backup operation. + * + * @param jobId Identifier for the full backup operation. + * @param context The context for the operation can be used for request cancellation. + * @return Backup restore operation status. + */ + Response FullBackupStatus( + std::string const& jobId = "", + Core::Context const& context = {}); + + /** + * @brief Restores all key materials using the SAS token pointing to a previously stored Azure + * Blob storage backup folder. + * + * @param blobContainerUrl The URL for the blob storage resource, including the path to the blob + * @param folderToRestore The path to the blob container where the backup resides. + * @param sasToken Azure blob shared access signature token pointing to a valid Azure blob + * container where full backup needs to be stored. This token needs to be valid for at least + * next 24 hours from the time of making this call. + * @param context The context for the operation can be used for request cancellation. + * @return A backup restore operation. + */ + Response FullRestore( + Azure::Core::Url const& blobContainerUrl, + std::string folderToRestore, + SasTokenParameter const& sasToken, + Core::Context const& context = {}); + + /** + * @brief Returns the status of restore operation. + * + * @param jobId Identifier for the restore operation. + * @param context The context for the operation can be used for request cancellation. + * @return A backup restore operation status. + */ + Response RestoreStatus( + std::string const& jobId = "", + Core::Context const& context = {}); + + /** + * @brief Restores all key versions of a given key using user supplied SAS token pointing to a + * previously stored Azure Blob storage backup folder. + * + * @param keyName The name of the key to be restored from the user supplied backup. + * @param blobContainerUrl The URL for the blob storage resource, including the path to the blob + * @param folderToRestore The path to the blob container where the backup resides. + * @param sasToken Azure blob shared access signature token pointing to a valid Azure blob + * container where full backup needs to be stored. This token needs to be valid for at least + * next 24 hours from the time of making this call. + * @param context The context for the operation can be used for request cancellation. + * @return A backup restore operation. + */ + Response SelectiveKeyRestore( + std::string const& keyName, + Azure::Core::Url const& blobContainerUrl, + std::string folderToRestore, + SasTokenParameter const& sasToken, + Core::Context const& context = {}); + + private: + std::shared_ptr m_pipeline; + Azure::Core::Url m_vaultBaseUrl; + std::string m_apiVersion; + KeyVaultServiceError DeserializeKeyVaultServiceError( + Azure::Core::Json::_internal::json errorFragment); + BackupOperationStatus DeserializeBackupOperationStatus( + Azure::Core::Http::RawResponse const& rawResponse); + }; + +}}}} // namespace Azure::Security::KeyVault::Administration diff --git a/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_operation.hpp b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_operation.hpp new file mode 100644 index 000000000..ab0450c48 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/backup_operation.hpp @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#pragma once +#include "azure/keyvault/administration/rest_client_models.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Azure::Security::KeyVault::Administration::Models; + +namespace Azure { namespace Security { namespace KeyVault { namespace Administration { + class BackupClient; + + /** + * @brief BackupOperation : The backup / restore long running operation. + * @remark Used to handle both backup and restore operations due to the similarity in patterns + * and return values. + */ + class BackupOperation final : public Azure::Core::Operation { + private: + /* BackupOperation can be constructed only by friends classes (internal + * creation). The constructor is private and requires internal components.*/ + friend class Azure::Security::KeyVault::Administration::BackupClient; + + std::shared_ptr m_backupClient; + BackupOperationStatus m_value; + std::string m_continuationToken; + bool m_isBackupOperation = true; + + std::unique_ptr PollInternal( + Azure::Core::Context const& context) override; + + Azure::Response PollUntilDoneInternal( + std::chrono::milliseconds period, + Azure::Core::Context& context) override; + + /** + * @brief Only friend classes are permitted to construct a RecoverDeletedKeyOperation. This is + * because a KeyVaultPipelne is required and it is not exposed to customers. + * + * @param backupClient A #BackupClient that is used for getting status updates. + * @param status A BackupOperationStatus object. + * @param isBackupOperation A boolean indicating if the operation is a backup operation or a + * restore. + */ + BackupOperation( + std::shared_ptr const& backupClient, + BackupOperationStatus const& status, + bool isBackupOperation) + : m_backupClient{backupClient}, m_value{status}, m_continuationToken{status.JobId}, + m_isBackupOperation{isBackupOperation} {}; + /** + * @brief Only friend classes are permitted to construct a RecoverDeletedKeyOperation. This is + * because a KeyVaultPipelne is required and it is not exposed to customers. + * @param backupClient A BackupClient that is used for getting status updates. + * @param continuationToken A string that is used to resume the operation. + * @param isBackupOperation A boolean indicating if the operation is a backup operation or a + * restore. + */ + BackupOperation( + std::shared_ptr const& backupClient, + std::string const& continuationToken, + bool isBackupOperation) + : m_backupClient{backupClient}, m_continuationToken{continuationToken}, + m_isBackupOperation{isBackupOperation} {}; + + public: + /** + * @brief Get the BackupOperationStatus object. + * + * @remark The status contains the current progress result at the time of the call. + * + * @return A BackupOperationStatus object. + */ + BackupOperationStatus Value() const override { return m_value; } + + /** + * @brief Get the continuation token used for further status inquiries + * + * @return std::string + */ + std::string GetResumeToken() const override { return m_continuationToken; } + + /** + * @brief Create a BackupOperation from the \p resumeToken fetched from + * another `Operation`, updated to the the latest operation status. + * + * @remark After the operation is initialized, it is used to poll the last update from the + * server using the \p context. + * + * @param resumeToken A previously generated token used to resume the polling of the + * operation. + * @param client A BackupClient that is used for getting status updates. + * @param isBackupOperation A boolean indicating if the operation is a backup operation if + * false it is considered a restore operation. + * @param context A Azure::Core::Context controlling the request lifetime. + * @return BackupOperation + */ + static BackupOperation CreateFromResumeToken( + std::string const& resumeToken, + BackupClient const& client, + bool isBackupOperation, + Azure::Core::Context const& context = Azure::Core::Context()) + { + BackupOperation operation( + std::make_shared(client), resumeToken, isBackupOperation); + operation.Poll(context); + return operation; + } + }; +}}}} // namespace Azure::Security::KeyVault::Administration diff --git a/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/rest_client_models.hpp b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/rest_client_models.hpp index 52ddccbcb..2eadeebf9 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/rest_client_models.hpp +++ b/sdk/keyvault/azure-security-keyvault-administration/inc/azure/keyvault/administration/rest_client_models.hpp @@ -74,4 +74,111 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Administra std::vector Value; }; + /** + * @brief Define the options to create an SDK Keys client. + * + */ + struct BackupClientOptions final : public Azure::Core::_internal::ClientOptions + { + /** + * @brief Service Version used. + * + */ + const std::string ApiVersion{"7.5"}; + }; + + /** + * @brief KeyVault Service Error model. + * + */ + struct KeyVaultServiceError final + { + /** + * @brief The error code. + * + */ + std::string Code; + /** + * @brief The error message. + * + */ + std::string Message; + }; + + /** + * @brief The full backup operation. + * + */ + struct BackupOperationStatus final + { + /** + * @brief Status of the backup operation. + * + */ + std::string Status; + /** + * @brief The status details of backup operation. + * + */ + Azure::Nullable StatusDetails; + /** + * @brief Error encountered, if any, during the full backup operation. + * + */ + Azure::Nullable Error; + /** + * @brief The start time of the backup operation in UTC. + * + */ + DateTime StartTime; + /** + * @brief The end time of the backup operation in UTC. + * + */ + Nullable EndTime; + /** + * @brief Identifier for the full backup operation. + * + */ + std::string JobId; + /** + * @brief The Azure blob storage container Uri which contains the full backup. + * + */ + std::string AzureStorageBlobContainerUri; + }; + + /** + * @brief Sas token parameter for backup and restore operations. + * + */ + struct SasTokenParameter final + { + /** + * @brief The SAS token pointing to an Azure Blob storage container. + * + */ + Nullable Token; + /** + * @brief Indicates which authentication method should be used. If set to true, Managed HSM + * will use the configured user-assigned managed identity to authenticate with Azure Storage. + * Otherwise, a SAS token has to be specified. + * + */ + Nullable UseManagedIdentity; + }; + + /** + * @brief Full backup status options. + * + */ + struct FullBackupStatusOptions final + { + /** + * @brief Identifier for the full backup operation. + * + */ + std::string JobId; + }; + }}}}} // namespace Azure::Security::KeyVault::Administration::Models diff --git a/sdk/keyvault/azure-security-keyvault-administration/test/samples/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-administration/samples/CMakeLists.txt similarity index 61% rename from sdk/keyvault/azure-security-keyvault-administration/test/samples/CMakeLists.txt rename to sdk/keyvault/azure-security-keyvault-administration/samples/CMakeLists.txt index ce2d41316..e8d79387c 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/test/samples/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/CMakeLists.txt @@ -4,4 +4,6 @@ cmake_minimum_required (VERSION 3.13) add_subdirectory(sample1-basic-operations) +add_subdirectory(sample2-full-backup-restore) +add_subdirectory(sample3-backup-selective-restore) diff --git a/sdk/keyvault/azure-security-keyvault-administration/test/samples/sample1-basic-operations/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-administration/samples/sample1-basic-operations/CMakeLists.txt similarity index 100% rename from sdk/keyvault/azure-security-keyvault-administration/test/samples/sample1-basic-operations/CMakeLists.txt rename to sdk/keyvault/azure-security-keyvault-administration/samples/sample1-basic-operations/CMakeLists.txt diff --git a/sdk/keyvault/azure-security-keyvault-administration/test/samples/sample1-basic-operations/sample1_administration.cpp b/sdk/keyvault/azure-security-keyvault-administration/samples/sample1-basic-operations/sample1_administration.cpp similarity index 93% rename from sdk/keyvault/azure-security-keyvault-administration/test/samples/sample1-basic-operations/sample1_administration.cpp rename to sdk/keyvault/azure-security-keyvault-administration/samples/sample1-basic-operations/sample1_administration.cpp index 1ea93194a..f90757c8c 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/test/samples/sample1-basic-operations/sample1_administration.cpp +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample1-basic-operations/sample1_administration.cpp @@ -24,7 +24,8 @@ int main() auto credential = std::make_shared(); // create client - SettingsClient settingsClient(std::getenv("AZURE_KEYVAULT_HSM_URL"), credential); + SettingsClient settingsClient( + Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_HSM_URL"), credential); try { diff --git a/sdk/keyvault/azure-security-keyvault-administration/samples/sample1_administration.md b/sdk/keyvault/azure-security-keyvault-administration/samples/sample1_administration.md index 2fded41b4..4131f99ea 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/samples/sample1_administration.md +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample1_administration.md @@ -55,4 +55,4 @@ Call UpdateSetting to modify an existing setting. Create an options object and i ## Source To see the full example source, see: -[Source Code](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/keyvault/azure-security-keyvault-administration/test/samples/sample1-basic-operations) +[Source Code](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/keyvault/azure-security-keyvault-administration/samples/sample1-basic-operations) diff --git a/sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/CMakeLists.txt new file mode 100644 index 000000000..17cbaf7d7 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cmake_minimum_required (VERSION 3.13) + +project (sample2-full-backup-restore LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable ( + sample2-full-backup-restore + sample2_full_backup_restore.cpp +) +create_per_service_target_build_for_sample(keyvault sample2-full-backup-restore) + +target_link_libraries(sample2-full-backup-restore PRIVATE azure-security-keyvault-administration azure-identity get-env-helper) diff --git a/sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/sample2_full_backup_restore.cpp b/sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/sample2_full_backup_restore.cpp new file mode 100644 index 000000000..ecaee848e --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore/sample2_full_backup_restore.cpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @brief This sample provides the code implementation to use the Key Vault Settings SDK client for + * C++ to get one or more settings, and update a setting value. + * + * @remark The following environment variables must be set before running the sample. + * - AZURE_KEYVAULT_HSM_URL: To the Key Vault HSM URL. + * - AZURE_KEYVAULT_BACKUP_TOKEN : The SAS token to access the blob storage account for + * backup/restore + * - AZURE_KEYVAULT_BACKUP_URL : The URL to the blob storage account + * + */ + +#include +#include + +#include +#include +#include + +using namespace Azure::Security::KeyVault::Administration; +using namespace std::chrono_literals; + +int main() +{ + auto credential = std::make_shared(); + + // create client + BackupClient client( + Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_HSM_URL"), credential); + SasTokenParameter sasTokenParameter; + // the backup/restore needs a SAS token to access the storage account + sasTokenParameter.Token + = Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_TOKEN"); + // the backup/restore needs a url to a blob storage resource + Azure::Core::Url blobUrl = Azure::Core::Url( + Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_URL")); + + try + { + // Create a full backup using a user-provided SAS token to an Azure blob storage container. + auto backupResponse = client.FullBackup(blobUrl, sasTokenParameter).Value; + + std::cout << "Backup Job Id: " << backupResponse.Value().JobId << std::endl + << "Backup Status: " << backupResponse.Value().Status << std::endl; + // Wait for the operation to complete. + auto backupStatus = backupResponse.PollUntilDone(10s); + + std::cout << "Backup Job Id: " << backupStatus.Value.JobId << std::endl + << "Backup Status: " << backupStatus.Value.Status << std::endl; + + // Restore the full backup using a user-provided SAS token to an Azure blob storage container. + Azure::Core::Url url(backupStatus.Value.AzureStorageBlobContainerUri); + auto subPath = url.GetPath(); + std::string folderToRestore = subPath.substr(7, subPath.size() - 1); + + std::cout << "Folder to restore: " << folderToRestore << std::endl; + auto restoreResponse = client.FullRestore(blobUrl, folderToRestore, sasTokenParameter).Value; + std::cout << "Restore Job Id: " << restoreResponse.Value().JobId << std::endl + << "Restore Status: " << restoreResponse.Value().Status << std::endl; + + // Wait for the operation to complete. + auto restoreStatus = restoreResponse.PollUntilDone(10s); + std::cout << "Restore Job Id: " << restoreStatus.Value.JobId << std::endl + << "Restore Status: " << restoreStatus.Value.Status << std::endl; + } + catch (Azure::Core::Credentials::AuthenticationException const& e) + { + std::cout << "Authentication Exception happened:" << std::endl << e.what() << std::endl; + return 1; + } + catch (Azure::Core::RequestFailedException const& e) + { + std::cout << "Key Vault Settings Client Exception happened:" << std::endl + << e.Message << std::endl; + return 1; + } + + return 0; +} diff --git a/sdk/keyvault/azure-security-keyvault-administration/samples/sample2_full_backup_restore.md b/sdk/keyvault/azure-security-keyvault-administration/samples/sample2_full_backup_restore.md new file mode 100644 index 000000000..6d64f4241 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample2_full_backup_restore.md @@ -0,0 +1,91 @@ +# Getting, updating, settings + +This sample demonstrates how to perform full backup and full restore for an Azure Key Vault HSM. +To get started, you'll need a URI to an Azure Key Vault HSM. + +## Creating a BackupClient + +To create a new `BackupClient` to perform these operations, you need the endpoint to an Azure Key Vault HSM and credentials. + +Key Vault BackupClient client for C++ currently supports any `TokenCredential` for authenticating. + +```cpp + auto credential + = std::make_shared(); +``` + +Then, in the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application. + +```cpp + // create client + BackupClient client(std::getenv("AZURE_KEYVAULT_HSM_URL"), credential); +``` +## Create the SasTokenParameter + +Since these operations require a blob storage for the backup/restore operations, a SAS token is required for the connection between the services(Key Vault and Storage). + +In this sample we rely on a couple of extra environment variables. + +```cpp + SasTokenParameter sasTokenParameter; + // the backup/restore needs a SAS token to access the storage account + sasTokenParameter.Token + = Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_TOKEN"); + // the backup/restore needs a url to a blob storage resource + Azure::Core::Url blobUrl = Azure::Core::Url( + Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_URL")); +``` + +## The Backup operation + +Since this is a long running operation the service provides endpoints to determine the status while the opperation is running. + +### Starting the backup operation + +```cpp +// Create a full backup using a user-provided SAS token to an Azure blob storage container. +auto backupResponse = client.FullBackup(blobUrl, sasTokenParameter).Value; + +std::cout << "Backup Job Id: " << backupResponse.Value().JobId << std::endl + << "Backup Status: " << backupResponse.Value().Status << std::endl; +``` + +### Backup operation waiting + +In order to wait for the operation to complete we will call the polling method. + +```cpp +// Wait for the operation to complete. +auto backupStatus = backupResponse.PollUntilDone(10s); + +std::cout << "Backup Job Id: " << backupStatus.Value.JobId << std::endl + << "Backup Status: " << backupStatus.Value.Status << std::endl; +``` + +## The FullRestore operation + +Similar to the backup operation after we initialize the operation we can check the status. + +### Starting the restore operation + +the restore operation requires a folder where a backup was previously performed along side the SAS token parameter. +```cpp +// Restore the full backup using a user-provided SAS token to an Azure blob storage container. +std::cout << "Folder to restore: " << folderToRestore << std::endl; +auto restoreResponse = client.FullRestore(blobUrl, folderToRestore, sasTokenParameter).Value; +std::cout << "Restore Job Id: " << restoreResponse.Value().JobId << std::endl + << "Restore Status: " << restoreResponse.Value().Status << std::endl; +``` + +### FullRestore operation waiting + +```cpp +// Wait for the operation to complete. +auto restoreStatus = restoreResponse.PollUntilDone(10s); +std::cout << "Restore Job Id: " << restoreStatus.Value.JobId << std::endl + << "Restore Status: " << restoreStatus.Value.Status << std::endl; +``` +## Source + +To see the full example source, see: +[Source Code](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/keyvault/azure-security-keyvault-administration/samples/sample2-full-backup-restore) diff --git a/sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/CMakeLists.txt new file mode 100644 index 000000000..d14eeaddf --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cmake_minimum_required (VERSION 3.13) + +project (sample3-backup-selective-restore LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable ( + sample3-backup-selective-restore + sample3_backup_selective_restore.cpp +) +create_per_service_target_build_for_sample(keyvault sample3-backup-selective-restore) + +target_link_libraries(sample3-backup-selective-restore PRIVATE azure-security-keyvault-administration azure-identity get-env-helper) diff --git a/sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/sample3_backup_selective_restore.cpp b/sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/sample3_backup_selective_restore.cpp new file mode 100644 index 000000000..d14c5d96b --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore/sample3_backup_selective_restore.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @brief This sample provides the code implementation to use the Key Vault Settings SDK client for + * C++ to get one or more settings, and update a setting value. + * + * @remark The following environment variables must be set before running the sample. + * - AZURE_KEYVAULT_HSM_URL: To the Key Vault HSM URL. + * - AZURE_KEYVAULT_BACKUP_TOKEN : The SAS token to access the blob storage account for + * backup/restore + * - AZURE_KEYVAULT_BACKUP_URL : The URL to the blob storage account + * + */ + +#include +#include + +#include +#include +#include + +using namespace Azure::Security::KeyVault::Administration; +using namespace std::chrono_literals; + +int main() +{ + auto credential = std::make_shared(); + + // create client + BackupClient client( + Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_HSM_URL"), credential); + SasTokenParameter sasTokenParameter; + // the backup/restore needs a SAS token to access the storage account + sasTokenParameter.Token + = Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_TOKEN"); + // the backup/restore needs a url to a blob storage resource + Azure::Core::Url blobUrl = Azure::Core::Url( + Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_URL")); + // the key name to restore from backup + const std::string keyName = "trytry"; + try + { + // Create a full backup using a user-provided SAS token to an Azure blob storage container. + auto backupResponse = client.FullBackup(blobUrl, sasTokenParameter).Value; + + std::cout << "Backup Job Id: " << backupResponse.Value().JobId << std::endl + << "Backup Status: " << backupResponse.Value().Status << std::endl; + // Wait for the operation to complete. + auto backupStatus = backupResponse.PollUntilDone(10s); + + std::cout << "Backup Job Id: " << backupStatus.Value.JobId << std::endl + << "Backup Status: " << backupStatus.Value.Status << std::endl; + // Restore a selected key from the backup using a user-provided SAS token to an Azure blob + // storage container. + Azure::Core::Url url(backupStatus.Value.AzureStorageBlobContainerUri); + auto subPath = url.GetPath(); + std::string folderToRestore = subPath.substr(7, subPath.size() - 1); + + std::cout << "Folder to restore: " << folderToRestore << std::endl; + auto selectiveRestore + = client.SelectiveKeyRestore("trytry", blobUrl, folderToRestore, sasTokenParameter).Value; + std::cout << "Selective Restore Job Id: " << selectiveRestore.Value().JobId << std::endl + << "Selective Restore Status: " << selectiveRestore.Value().Status << std::endl; + + // Wait for the operation to complete. + auto selectiveStatus = selectiveRestore.PollUntilDone(10s); + std::cout << "Selective Restore Job Id: " << selectiveStatus.Value.JobId << std::endl + << "Selective Restore Status: " << selectiveStatus.Value.Status << std::endl; + } + catch (Azure::Core::Credentials::AuthenticationException const& e) + { + std::cout << "Authentication Exception happened:" << std::endl << e.what() << std::endl; + return 1; + } + catch (Azure::Core::RequestFailedException const& e) + { + std::cout << "Key Vault Settings Client Exception happened:" << std::endl + << e.Message << std::endl; + return 1; + } + + return 0; +} diff --git a/sdk/keyvault/azure-security-keyvault-administration/samples/sample3_backup_selective_restore.md b/sdk/keyvault/azure-security-keyvault-administration/samples/sample3_backup_selective_restore.md new file mode 100644 index 000000000..f6eea22c9 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/samples/sample3_backup_selective_restore.md @@ -0,0 +1,93 @@ +# Getting, updating, settings + +This sample demonstrates how to perform full backup and full restore for an Azure Key Vault HSM. +To get started, you'll need a URI to an Azure Key Vault HSM. + +## Creating a BackupClient + +To create a new `BackupClient` to perform these operations, you need the endpoint to an Azure Key Vault HSM and credentials. + +Key Vault BackupClient client for C++ currently supports any `TokenCredential` for authenticating. + +```cpp + auto credential + = std::make_shared(); +``` + +Then, in the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application. + +```cpp + // create client + BackupClient client(std::getenv("AZURE_KEYVAULT_HSM_URL"), credential); +``` +## Create the SasTokenParameter + +Since these operations require a blob storage for the backup/restore operations, a SAS token is required for the connection between the services(Key Vault and Storage). + +In this sample we rely on a couple of extra environment variables. + +```cpp + SasTokenParameter sasTokenParameter; + // the backup/restore needs a SAS token to access the storage account + sasTokenParameter.Token + = Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_TOKEN"); + // the backup/restore needs a url to a blob storage resource + Azure::Core::Url blobUrl + = Azure::Core::Url(Azure::Core::_internal::Environment::GetVariable("AZURE_KEYVAULT_BACKUP_URL")); +``` + +## The Backup operation + +Since this is a long running operation the service provides endpoints to determine the status while the opperation is running. + +### Starting the backup operation + +```cpp +// Create a full backup using a user-provided SAS token to an Azure blob storage container. +auto backupResponse = client.FullBackup(blobUrl, sasTokenParameter).Value; + +std::cout << "Backup Job Id: " << backupResponse.Value().JobId << std::endl + << "Backup Status: " << backupResponse.Value().Status << std::endl; +``` + +### Backup operation waiting + +In order to wait for the operation to complete we will call the polling method. + +```cpp +// Wait for the operation to complete. +auto backupStatus = backupResponse.PollUntilDone(10s); + +std::cout << "Backup Job Id: " << backupStatus.Value.JobId << std::endl + << "Backup Status: " << backupStatus.Value.Status << std::endl; +``` + +## The SelectiveRestore operation + +Similar to the backup operation after we initialize the operation we can check the status. + +### Starting the restore operation + +The selective restore operation requires a folder where a backup was previously performed along side the SAS token parameter. + +```cpp +// Restore the full backup using a user-provided SAS token to an Azure blob storage container. +std::string folderToRestore = ...; +std::cout << "Folder to restore: " << restoreBlobDetails.FolderToRestore << std::endl; +auto selectiveRestore = client.SelectiveKeyRestore("keyName", blobUrl, folderToRestore, sasTokenParameter); +std::cout << "Restore Job Id: " << restoreResponse.Value.JobId << std::endl + << "Restore Status: " << restoreResponse.Value.Status << std::endl; +``` + +### Selective restore operation completion + +```cpp +// Wait for the operation to complete. +auto selectiveStatus = selectiveRestore.PollUntilDone(10s); +std::cout << "Selective Restore Job Id: " << selectiveStatus.Value.JobId << std::endl + << "Selective Restore Status: " << selectiveStatus.Value.Status << std::endl; +``` +## Source + +To see the full example source, see: +[Source Code](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/keyvault/azure-security-keyvault-administration/samples/sample3-backup-selective-restore) diff --git a/sdk/keyvault/azure-security-keyvault-administration/src/backup_client.cpp b/sdk/keyvault/azure-security-keyvault-administration/src/backup_client.cpp new file mode 100644 index 000000000..37f1dd1e9 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/src/backup_client.cpp @@ -0,0 +1,329 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#include "azure/keyvault/administration/backup_client.hpp" + +#include "private/administration_constants.hpp" +#include "private/package_version.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Azure::Security::KeyVault::Administration; +using namespace Azure::Core::Http; +using namespace Azure::Core::Http::Policies; +using namespace Azure::Core::Http::Policies::_internal; +using namespace Azure::Core::Http::_internal; + +BackupClient::BackupClient( + std::string const& vaultUrl, + std::shared_ptr credential, + BackupClientOptions options) + : m_vaultBaseUrl(vaultUrl), m_apiVersion(options.ApiVersion) +{ + std::vector> perRetrypolicies; + { + Azure::Core::Credentials::TokenRequestContext tokenContext; + tokenContext.Scopes = {_internal::UrlScope::GetScopeFromUrl(m_vaultBaseUrl)}; + + perRetrypolicies.emplace_back( + std::make_unique<_internal::KeyVaultChallengeBasedAuthenticationPolicy>( + credential, std::move(tokenContext))); + } + std::vector> perCallpolicies; + + m_pipeline = std::make_shared( + options, + _detail::KeyVaultServicePackageName, + _detail::PackageVersion::ToString(), + std::move(perRetrypolicies), + std::move(perCallpolicies)); +} + +Azure::Response BackupClient::FullBackup( + Azure::Core::Url const& blobContainerUrl, + SasTokenParameter const& sasToken, + Core::Context const& context) +{ + auto url = m_vaultBaseUrl; + url.AppendPath("backup"); + + url.SetQueryParameters({{"api-version", m_apiVersion}}); + + std::string jsonBody; + { + auto jsonRoot = Core::Json::_internal::json::object(); + + jsonRoot["storageResourceUri"] = blobContainerUrl.GetAbsoluteUrl(); + + if (sasToken.Token.HasValue()) + { + jsonRoot["token"] = sasToken.Token.Value(); + } + + if (sasToken.UseManagedIdentity.HasValue()) + { + jsonRoot["useManagedIdentity"] = sasToken.UseManagedIdentity.Value(); + } + + jsonBody = jsonRoot.dump(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(jsonBody.data()), jsonBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Post, url, &requestBody); + + request.SetHeader(HttpShared::ContentType, HttpShared::ApplicationJson); + request.SetHeader(HttpShared::Accept, HttpShared::ApplicationJson); + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + auto const httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) + { + throw Core::RequestFailedException(rawResponse); + } + + BackupOperationStatus response = DeserializeBackupOperationStatus(*rawResponse); + BackupOperation operation(std::make_shared(*this), std::move(response), true); + return Response(std::move(operation), std::move(rawResponse)); +} + +Azure::Response BackupClient::FullBackupStatus( + std::string const& jobId, + Core::Context const& context) +{ + auto url = m_vaultBaseUrl; + url.AppendPath("backup"); + url.AppendPath(Core::Url::Encode(jobId)); + url.AppendPath("pending"); + + url.SetQueryParameters({{"api-version", m_apiVersion}}); + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + request.SetHeader(HttpShared::ContentType, HttpShared::ApplicationJson); + request.SetHeader(HttpShared::Accept, HttpShared::ApplicationJson); + + auto rawResponse = m_pipeline->Send(request, context); + auto const httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + BackupOperationStatus response + + = DeserializeBackupOperationStatus(*rawResponse); + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response BackupClient::FullRestore( + Azure::Core::Url const& blobContainerUrl, + std::string folderToRestore, + SasTokenParameter const& sasToken, + Core::Context const& context) +{ + auto url = m_vaultBaseUrl; + url.AppendPath("restore"); + + url.SetQueryParameters({{"api-version", m_apiVersion}}); + + std::string jsonBody; + { + auto jsonRoot = Core::Json::_internal::json::object(); + + jsonRoot["sasTokenParameters"]["storageResourceUri"] = blobContainerUrl.GetAbsoluteUrl(); + + if (sasToken.Token.HasValue()) + { + jsonRoot["sasTokenParameters"]["token"] = sasToken.Token.Value(); + } + + if (sasToken.UseManagedIdentity.HasValue()) + { + jsonRoot["sasTokenParameters"]["useManagedIdentity"] = sasToken.UseManagedIdentity.Value(); + } + + jsonRoot["folderToRestore"] = folderToRestore; + + jsonBody = jsonRoot.dump(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(jsonBody.data()), jsonBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody); + + request.SetHeader("Content-Type", "application/json"); + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + auto const httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) + { + throw Core::RequestFailedException(rawResponse); + } + + BackupOperationStatus response = DeserializeBackupOperationStatus(*rawResponse); + BackupOperation operation(std::make_shared(*this), std::move(response), false); + return Response(std::move(operation), std::move(rawResponse)); +} + +Azure::Response BackupClient::RestoreStatus( + std::string const& jobId, + Core::Context const& context) +{ + auto url = m_vaultBaseUrl; + url.AppendPath("restore"); + url.AppendPath(Core::Url::Encode(jobId)); + url.AppendPath("pending"); + + url.SetQueryParameters({{"api-version", "7.5"}}); + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + auto rawResponse = m_pipeline->Send(request, context); + auto const httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + BackupOperationStatus response = DeserializeBackupOperationStatus(*rawResponse); + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response BackupClient::SelectiveKeyRestore( + std::string const& keyName, + Azure::Core::Url const& blobContainerUrl, + std::string folderToRestore, + SasTokenParameter const& sasToken, + Core::Context const& context) +{ + auto url = m_vaultBaseUrl; + url.AppendPath("keys"); + url.AppendPath(Core::Url::Encode(keyName)); + url.AppendPath("restore"); + + url.SetQueryParameters({{"api-version", "7.5"}}); + + std::string jsonBody; + { + auto jsonRoot = Core::Json::_internal::json::object(); + + jsonRoot["sasTokenParameters"]["storageResourceUri"] = blobContainerUrl.GetAbsoluteUrl(); + + if (sasToken.Token.HasValue()) + { + jsonRoot["sasTokenParameters"]["token"] = sasToken.Token.Value(); + } + + if (sasToken.UseManagedIdentity.HasValue()) + { + jsonRoot["sasTokenParameters"]["useManagedIdentity"] = sasToken.UseManagedIdentity.Value(); + } + + jsonRoot["folder"] = folderToRestore; + + jsonBody = jsonRoot.dump(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(jsonBody.data()), jsonBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody); + + request.SetHeader("Content-Type", "application/json"); + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + auto const httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) + { + throw Core::RequestFailedException(rawResponse); + } + + BackupOperationStatus response = DeserializeBackupOperationStatus(*rawResponse); + BackupOperation operation(std::make_shared(*this), std::move(response), false); + return Response(std::move(operation), std::move(rawResponse)); +} + +BackupOperationStatus BackupClient::DeserializeBackupOperationStatus( + Azure::Core::Http::RawResponse const& rawResponse) +{ + BackupOperationStatus response{}; + auto const& responseBody = rawResponse.GetBody(); + if (responseBody.size() > 0) + { + auto const jsonRoot + = Core::Json::_internal::json::parse(responseBody.begin(), responseBody.end()); + + response.Status = jsonRoot["status"].get(); + + if (jsonRoot.contains("statusDetails") && !jsonRoot["statusDetails"].is_null()) + { + response.StatusDetails = jsonRoot["statusDetails"].get(); + } + + response.StartTime = Core::_internal::PosixTimeConverter::PosixTimeToDateTime( + jsonRoot["startTime"].is_string() ? std::stoll(jsonRoot["startTime"].get()) + : jsonRoot["startTime"].get()); + + if (jsonRoot.contains("endTime") && !jsonRoot["endTime"].is_null()) + { + response.EndTime = Core::_internal::PosixTimeConverter::PosixTimeToDateTime( + jsonRoot["endTime"].is_string() ? std::stoll(jsonRoot["endTime"].get()) + : jsonRoot["endTime"].get()); + } + + response.JobId = jsonRoot["jobId"].get(); + + if (jsonRoot.contains("azureStorageBlobContainerUri") + && !jsonRoot["azureStorageBlobContainerUri"].is_null()) + { + response.AzureStorageBlobContainerUri + = jsonRoot["azureStorageBlobContainerUri"].get(); + } + + if (jsonRoot.contains("error") && !jsonRoot["error"].is_null()) + { + response.Error = DeserializeKeyVaultServiceError(jsonRoot["error"]); + } + } + return response; +} + +KeyVaultServiceError BackupClient::DeserializeKeyVaultServiceError( + Azure::Core::Json::_internal::json errorFragment) +{ + KeyVaultServiceError result; + if (errorFragment.contains("code")) + { + result.Code = errorFragment["code"].get(); + } + if (errorFragment.contains("message")) + { + result.Message = errorFragment["message"].get(); + } + return result; +} diff --git a/sdk/keyvault/azure-security-keyvault-administration/src/backup_operation.cpp b/sdk/keyvault/azure-security-keyvault-administration/src/backup_operation.cpp new file mode 100644 index 000000000..043c960a7 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/src/backup_operation.cpp @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#include "azure/keyvault/administration/backup_operation.hpp" + +#include "azure/keyvault/administration/backup_client.hpp" + +using namespace Azure::Security::KeyVault::Administration; + +std::unique_ptr BackupOperation::PollInternal( + Azure::Core::Context const& context) +{ + std::unique_ptr rawResponse; + try + { + Azure::Response response = m_isBackupOperation + ? m_backupClient->FullBackupStatus(m_continuationToken, context) + : m_backupClient->RestoreStatus(m_continuationToken, context); + + m_value = response.Value; + m_continuationToken = response.Value.JobId; + rawResponse = std::move(response.RawResponse); + if (response.Value.Status == "InProgress") + { + m_status = Azure::Core::OperationStatus::Running; + } + else if (response.Value.Status == "Succeeded") + { + m_status = Azure::Core::OperationStatus::Succeeded; + } + else if (response.Value.Status == "Failed") + { + m_status = Azure::Core::OperationStatus::Failed; + } + else + { + throw Azure::Core::RequestFailedException(response.RawResponse); + } + } + catch (Azure::Core::RequestFailedException& error) + { + rawResponse = std::move(error.RawResponse); + } + + return rawResponse; +} + +Azure::Response BackupOperation::PollUntilDoneInternal( + std::chrono::milliseconds period, + Azure::Core::Context& context) +{ + while (true) + { + // Poll will update the raw response. + Poll(context); + if (IsDone()) + { + break; + } + std::this_thread::sleep_for(period); + } + + return Azure::Response( + m_value, std::make_unique(*m_rawResponse)); +} diff --git a/sdk/keyvault/azure-security-keyvault-administration/test/ut/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-administration/test/ut/CMakeLists.txt index 5ccf473e8..6410db2ba 100644 --- a/sdk/keyvault/azure-security-keyvault-administration/test/ut/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-administration/test/ut/CMakeLists.txt @@ -17,10 +17,11 @@ add_compile_definitions(AZURE_TEST_RECORDING_DIR="${CMAKE_CURRENT_LIST_DIR}") add_executable ( azure-security-keyvault-administration-test + backup_restore_client_base_test.hpp + backup_restore_client_test.cpp macro_guard.cpp settings_client_base_test.hpp - settings_client_test.cpp -) + settings_client_test.cpp) target_compile_definitions(azure-security-keyvault-administration-test PRIVATE _azure_BUILDING_TESTS) diff --git a/sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_base_test.hpp b/sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_base_test.hpp new file mode 100644 index 000000000..848a68e34 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_base_test.hpp @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @file + * @brief The base class to construct and init a Key Vault client. + * + */ + +#include +#include +#include + +#include +#include + +#include + +namespace Azure { + namespace Security { + namespace KeyVault { + namespace Administration { + namespace Test { + + class BackupRestoreClientTest : public Azure::Core::Test::TestBase, + public ::testing::WithParamInterface { + + public: + BackupRestoreClientTest() { TestBase::SetUpTestSuiteLocal(AZURE_TEST_ASSETS_DIR); } + void CreateHSMClientForTest(std::string hsmUrl = "") + { + BackupClientOptions options; + m_client = InitTestClient< + Azure::Security::KeyVault::Administration::BackupClient, + Azure::Security::KeyVault::Administration::Models::BackupClientOptions>( + hsmUrl.length() == 0 ? m_keyVaultHsmUrl : hsmUrl, m_credential, options); + } + + SasTokenParameter GetSasTokenBackup(bool managedIdentity = false) + { + SasTokenParameter sasTokenParameter; + // the backup/restore needs a SAS token to access the storage account + sasTokenParameter.Token = GetEnv("AZURE_KEYVAULT_BACKUP_TOKEN"); + // the backup/restore needs a url to a blob storage resource + m_blobUrl = Azure::Core::Url(GetEnv("AZURE_KEYVAULT_BACKUP_URL")); + + sasTokenParameter.UseManagedIdentity = managedIdentity; + return sasTokenParameter; + } + + private: + std::unique_ptr m_client; + + protected: + std::shared_ptr m_credential; + std::string m_keyVaultUrl; + std::string m_keyVaultHsmUrl; + Azure::Core::Url m_blobUrl; + std::chrono::milliseconds m_testPollingIntervalMs = std::chrono::seconds(1); + + // Reads the current test instance name. + // Name gets also sanitized (special chars are removed) to avoid issues when recording or + // creating. This also return the name with suffix if the "AZURE_LIVE_TEST_SUFFIX" exists. + std::string GetTestName(bool sanitize = true) + { + auto output = m_keyVaultUrl.compare(m_keyVaultHsmUrl) == 0 ? "Same" : "NotSame"; + std::cout << "\n Keyvault and HSM are" << output; + return Azure::Core::Test::TestBase::GetTestNameSuffix(sanitize); + } + + Azure::Security::KeyVault::Administration::BackupClient& GetClientForTest( + std::string const& testName) + { + // set the interceptor for the current test + m_testContext.RenameTest(testName); + return *m_client; + } + + // Create + virtual void SetUp() override + { + Azure::Core::Test::TestBase::SetUpTestBase(AZURE_TEST_RECORDING_DIR); + m_keyVaultUrl = GetEnv("AZURE_KEYVAULT_URL"); + m_keyVaultHsmUrl = GetEnv("AZURE_KEYVAULT_HSM_URL"); + + // Options and credential for the client + BackupClientOptions options; + m_credential = GetTestCredential(); + + // `InitTestClient` takes care of setting up Record&Playback. + m_client = InitTestClient< + Azure::Security::KeyVault::Administration::BackupClient, + Azure::Security::KeyVault::Administration::Models::BackupClientOptions>( + m_keyVaultUrl, m_credential, options); + + UpdateWaitingTime(m_testPollingIntervalMs); + } + + public: + template + static inline void CheckValidResponse( + Azure::Response& response, + Azure::Core::Http::HttpStatusCode expectedCode = Azure::Core::Http::HttpStatusCode::Ok) + { + auto const& rawResponse = response.RawResponse; + EXPECT_EQ( + static_cast::type>( + rawResponse->GetStatusCode()), + static_cast::type>( + expectedCode)); + } + + static inline std::string GetUniqueName() { return Azure::Core::Uuid::CreateUuid().ToString(); } + }; +}}}}} // namespace Azure::Security::KeyVault::Administration::Test diff --git a/sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_test.cpp b/sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_test.cpp new file mode 100644 index 000000000..6fc130b26 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-administration/test/ut/backup_restore_client_test.cpp @@ -0,0 +1,310 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "azure/keyvault/administration/backup_client.hpp" +#include "backup_restore_client_base_test.hpp" + +#include +#include +#include + +#include +#include +#include + +#include + +using namespace Azure::Security::KeyVault::Administration; +using namespace Azure::Security::KeyVault::Administration::Models; +using namespace Azure::Security::KeyVault::Administration::Test; + +using namespace std::chrono_literals; + +TEST_F(BackupRestoreClientTest, BackupFull_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "BackupFull"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + auto response = client.FullBackup(m_blobUrl, sasTokenParameter).Value; + EXPECT_EQ(response.Value().Status, "InProgress"); + EXPECT_TRUE(response.Value().StartTime > response.Value().StartTime.min()); + EXPECT_FALSE(response.Value().EndTime.HasValue()); + EXPECT_FALSE(response.Value().Error.HasValue()); + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, BackupFullStatus_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "BackupFullStatus"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + auto response = client.FullBackup(m_blobUrl, sasTokenParameter).Value; + + EXPECT_EQ(response.Value().Status, "InProgress"); + EXPECT_TRUE(response.Value().StartTime > response.Value().StartTime.min()); + EXPECT_FALSE(response.Value().EndTime.HasValue()); + EXPECT_FALSE(response.Value().Error.HasValue()); + auto response2 = response.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response2.Value.Status, "Succeeded"); + EXPECT_TRUE(response2.Value.EndTime.Value() > response2.Value.StartTime); + EXPECT_FALSE(response2.Value.Error.HasValue()); + EXPECT_EQ(response.Value().JobId, response2.Value.JobId); + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, BackupFullStatusEmptyJobId_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "BackupFullStatusEmptyJobId"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + try + { + auto response2 = client.FullBackupStatus().Value; + } + catch (Azure::Core::RequestFailedException& e) + { + EXPECT_EQ(e.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NotFound); + } + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, RestoreStatusEmptyJobId_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "RestoreStatusEmptyJobId"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + try + { + auto response2 = client.RestoreStatus().Value; + } + catch (Azure::Core::RequestFailedException& e) + { + EXPECT_EQ(e.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::NotFound); + } + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, BackupFullErrorStatus_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "BackupFullErrorStatus"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + Azure::Core::Url defectiveUrl( + m_blobUrl.GetScheme() + "://" + m_blobUrl.GetHost()); // invalid uri + auto response = client.FullBackup(defectiveUrl, sasTokenParameter).Value; + + EXPECT_EQ(response.Value().Status, "InProgress"); + EXPECT_TRUE(response.Value().StartTime > response.Value().StartTime.min()); + EXPECT_FALSE(response.Value().EndTime.HasValue()); + EXPECT_FALSE(response.Value().Error.HasValue()); + + response.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response.Value().Status, "Failed"); + EXPECT_TRUE(response.Value().EndTime.Value() > response.Value().StartTime); + EXPECT_EQ(response.Value().StatusDetails.Value(), "InvalidQueryParameterValue"); + EXPECT_EQ(response.Value().Error.Value().Code, "InvalidQueryParameterValue"); + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, RestoreFull_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "RestoreFull"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + auto response = client.FullBackup(m_blobUrl, sasTokenParameter).Value; + + EXPECT_EQ(response.Value().Status, "InProgress"); + EXPECT_TRUE(response.Value().StartTime > response.Value().StartTime.min()); + EXPECT_FALSE(response.Value().EndTime.HasValue()); + EXPECT_FALSE(response.Value().Error.HasValue()); + auto response2 = response.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response2.Value.Status, "Succeeded"); + EXPECT_TRUE(response2.Value.EndTime.Value() > response2.Value.StartTime); + EXPECT_FALSE(response2.Value.Error.HasValue()); + EXPECT_EQ(response.Value().JobId, response2.Value.JobId); + + Azure::Core::Url url(response2.Value.AzureStorageBlobContainerUri); + auto subPath = url.GetPath(); + std::string folderToRestore = subPath.substr(7, subPath.size() - 1); + + auto response3 = client.FullRestore(m_blobUrl, folderToRestore, sasTokenParameter).Value; + EXPECT_EQ(response3.Value().Status, "InProgress"); + EXPECT_TRUE(response3.Value().StartTime > response3.Value().StartTime.min()); + EXPECT_FALSE(response3.Value().EndTime.HasValue()); + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, RestoreFullStatus_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "RestoreFullStatus"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + auto response = client.FullBackup(m_blobUrl, sasTokenParameter).Value; + + EXPECT_EQ(response.Value().Status, "InProgress"); + EXPECT_TRUE(response.Value().StartTime > response.Value().StartTime.min()); + EXPECT_FALSE(response.Value().EndTime.HasValue()); + EXPECT_FALSE(response.Value().Error.HasValue()); + auto response2 = response.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response2.Value.Status, "Succeeded"); + EXPECT_TRUE(response2.Value.EndTime.Value() > response2.Value.StartTime); + EXPECT_FALSE(response2.Value.Error.HasValue()); + EXPECT_EQ(response.Value().JobId, response2.Value.JobId); + + Azure::Core::Url url(response2.Value.AzureStorageBlobContainerUri); + auto subPath = url.GetPath(); + std::string folderToRestore = subPath.substr(7, subPath.size() - 1); + + auto response3 = client.FullRestore(m_blobUrl, folderToRestore, sasTokenParameter).Value; + EXPECT_EQ(response3.Value().Status, "InProgress"); + EXPECT_TRUE(response3.Value().StartTime > response3.Value().StartTime.min()); + EXPECT_FALSE(response3.Value().EndTime.HasValue()); + auto response4 = response3.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response4.Value.Status, "Succeeded"); + EXPECT_TRUE(response4.Value.EndTime.Value() > response4.Value.StartTime); + EXPECT_FALSE(response4.Value.Error.HasValue()); + EXPECT_EQ(response3.Value().JobId, response4.Value.JobId); + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, RestoreSelectiveStatus_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "RestoreSelectiveStatus"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + auto response = client.FullBackup(m_blobUrl, sasTokenParameter).Value; + + EXPECT_EQ(response.Value().Status, "InProgress"); + EXPECT_TRUE(response.Value().StartTime > response.Value().StartTime.min()); + EXPECT_FALSE(response.Value().EndTime.HasValue()); + EXPECT_FALSE(response.Value().Error.HasValue()); + auto response2 = response.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response2.Value.Status, "Succeeded"); + EXPECT_TRUE(response2.Value.EndTime.Value() > response2.Value.StartTime); + EXPECT_FALSE(response2.Value.Error.HasValue()); + EXPECT_EQ(response.Value().JobId, response2.Value.JobId); + + Azure::Core::Url url(response2.Value.AzureStorageBlobContainerUri); + auto subPath = url.GetPath(); + std::string folderToRestore = subPath.substr(7, subPath.size() - 1); + + auto response3 + = client.SelectiveKeyRestore("trytry", m_blobUrl, folderToRestore, sasTokenParameter).Value; + EXPECT_EQ(response3.Value().Status, "InProgress"); + EXPECT_TRUE(response3.Value().StartTime > response3.Value().StartTime.min()); + EXPECT_FALSE(response3.Value().EndTime.HasValue()); + auto response4 = response3.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response4.Value.Status, "Succeeded"); + EXPECT_TRUE(response4.Value.EndTime.Value() > response4.Value.StartTime); + EXPECT_FALSE(response4.Value.Error.HasValue()); + EXPECT_EQ(response3.Value().JobId, response4.Value.JobId); + } + else + { + SkipTest(); + } +} + +TEST_F(BackupRestoreClientTest, RestoreSelectiveInvalidKeyStatus_RECORDEDONLY_) +{ + if (m_keyVaultHsmUrl != m_keyVaultUrl) + { + auto testName = "RestoreSelectiveInvalidKeyStatus"; + CreateHSMClientForTest(); + auto& client = GetClientForTest(testName); + SasTokenParameter sasTokenParameter = GetSasTokenBackup(); + + auto response = client.FullBackup(m_blobUrl, sasTokenParameter).Value; + + EXPECT_EQ(response.Value().Status, "InProgress"); + EXPECT_TRUE(response.Value().StartTime > response.Value().StartTime.min()); + EXPECT_FALSE(response.Value().EndTime.HasValue()); + EXPECT_FALSE(response.Value().Error.HasValue()); + auto response2 = response.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response2.Value.Status, "Succeeded"); + EXPECT_TRUE(response2.Value.EndTime.Value() > response2.Value.StartTime); + EXPECT_FALSE(response2.Value.Error.HasValue()); + EXPECT_EQ(response.Value().JobId, response2.Value.JobId); + + Azure::Core::Url url(response2.Value.AzureStorageBlobContainerUri); + auto subPath = url.GetPath(); + std::string folderToRestore = subPath.substr(7, subPath.size() - 1); + + auto response3 + = client.SelectiveKeyRestore("trytry2", m_blobUrl, folderToRestore, sasTokenParameter) + .Value; + EXPECT_EQ(response3.Value().Status, "InProgress"); + EXPECT_TRUE(response3.Value().StartTime > response3.Value().StartTime.min()); + EXPECT_FALSE(response3.Value().EndTime.HasValue()); + auto response4 = response3.PollUntilDone(m_testPollingIntervalMs); + EXPECT_EQ(response4.Value.Status, "Failed"); + EXPECT_TRUE(response4.Value.EndTime.Value() > response4.Value.StartTime); + EXPECT_EQ(response4.Value.StatusDetails.Value(), "The given key or its versions NOT found"); + EXPECT_EQ(response3.Value().JobId, response4.Value.JobId); + EXPECT_EQ(response4.Value.Error.Value().Message, "The given key or its versions NOT found"); + EXPECT_EQ(response4.Value.Error.Value().Code, "No key versions are updated"); + } + else + { + SkipTest(); + } +} diff --git a/sdk/keyvault/ci.yml b/sdk/keyvault/ci.yml index c491e25ee..baf9eeb6a 100644 --- a/sdk/keyvault/ci.yml +++ b/sdk/keyvault/ci.yml @@ -77,6 +77,10 @@ extends: Value: "debug" - Name: LOGGING__LOGLEVEL__MICROSOFT Value: "debug" + - Name: AZURE_KEYVAULT_BACKUP_TOKEN + Value: "Sanitized" + - Name: AZURE_KEYVAULT_BACKUP_URL + Value: "https://non-real-account.blob.core.windows.net/backup" CMakeTestOptions: - Name: Default Value: ''