diff --git a/sdk/keyvault/azure-security-keyvault-secrets/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-secrets/CMakeLists.txt index 73e6a1d91..e8c5a026b 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-secrets/CMakeLists.txt @@ -34,6 +34,7 @@ set( inc/azure/keyvault/secrets/secret_client.hpp inc/azure/keyvault/keyvault_secrets.hpp inc/azure/keyvault/secrets/keyvault_deleted_secret.hpp + inc/azure/keyvault/secrets/keyvault_backup_secret.hpp ) set( diff --git a/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/keyvault_secrets.hpp b/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/keyvault_secrets.hpp index 0e63d7e15..eeaa9ece3 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/keyvault_secrets.hpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/keyvault_secrets.hpp @@ -9,6 +9,7 @@ #pragma once #include "azure/keyvault/secrets/dll_import_export.hpp" +#include "azure/keyvault/secrets/keyvault_backup_secret.hpp" #include "azure/keyvault/secrets/keyvault_deleted_secret.hpp" #include "azure/keyvault/secrets/keyvault_secret.hpp" #include "azure/keyvault/secrets/keyvault_secret_properties.hpp" diff --git a/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/secrets/keyvault_backup_secret.hpp b/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/secrets/keyvault_backup_secret.hpp new file mode 100644 index 000000000..89fed1cb3 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/secrets/keyvault_backup_secret.hpp @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Keyvault BackupSecretData definition + */ +#pragma once +#include +#include + +namespace Azure { namespace Security { namespace KeyVault { namespace Secrets { + /** + * @brief Represents a backed up secret. + * + */ + struct BackupSecretResult final + { + /** + * @brief The backed up secret. + * + */ + std::vector Secret; + }; +}}}} // namespace Azure::Security::KeyVault::Secrets diff --git a/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/secrets/secret_client.hpp b/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/secrets/secret_client.hpp index 6197d5eaa..0a083753e 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/secrets/secret_client.hpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/inc/azure/keyvault/secrets/secret_client.hpp @@ -8,14 +8,15 @@ #pragma once -#include "dll_import_export.hpp" - +#include "azure/keyvault/secrets/keyvault_backup_secret.hpp" #include "azure/keyvault/secrets/keyvault_deleted_secret.hpp" #include "azure/keyvault/secrets/keyvault_secret.hpp" +#include "dll_import_export.hpp" #include #include #include +#include #include namespace Azure { namespace Security { namespace KeyVault { namespace _detail { @@ -235,6 +236,35 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Secrets { std::string const& version, KeyvaultSecretProperties const& properties, Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Back up the specified secret. + * Requests that a backup of the specified secret be downloaded to the client. + * All versions of the secret will be downloaded. + * This operation requires the secrets/backup permission. + * + * @param name The name of the secret. + * @param context The context for the operation can be used for request cancellation. + * + * @return The The backup blob containing the backed up secret. + */ + Azure::Response BackupSecret( + std::string const& name, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Restore a backed up secret to a vault. + * Restores a backed up secret, and all its versions, to a vault. + * This operation requires the secrets/restore permission. + * + * @param backup The backup payload as encoded vector of bytes. + * @param context The context for the operation can be used for request cancellation. + * + * @return The Secret wrapped in the Response. + */ + Azure::Response RestoreSecretBackup( + std::vector const& backup, + Azure::Core::Context const& context = Azure::Core::Context()) const; }; }}}} // namespace Azure::Security::KeyVault::Secrets diff --git a/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_constants.hpp b/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_constants.hpp index e9e7d51eb..88d8467d5 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_constants.hpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_constants.hpp @@ -14,6 +14,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Secrets { /***************** KeyVault Secret *****************/ constexpr static const char SecretPath[] = "secrets"; static constexpr char const DeletedSecretPath[] = "deletedsecrets"; + static constexpr char const BackupSecretPath[] = "backup"; + static constexpr char const RestoreSecretPath[] = "restore"; /******************* Secret property names ***********/ diff --git a/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_serializers.hpp b/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_serializers.hpp index 8aba3bde9..0669ba4ec 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_serializers.hpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/src/private/secret_serializers.hpp @@ -8,10 +8,13 @@ */ #pragma once +#include "azure/keyvault/secrets/keyvault_backup_secret.hpp" #include "azure/keyvault/secrets/keyvault_deleted_secret.hpp" #include "azure/keyvault/secrets/keyvault_secret.hpp" #include #include +#include +#include using namespace Azure::Security::KeyVault::Secrets; @@ -103,4 +106,15 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Secrets { static std::string KeyVaultSecretPropertiesSerialize( KeyvaultSecretProperties const& properties); }; + + struct KeyvaultBackupSecretSerializer final + { + static BackupSecretResult KeyvaultBackupSecretDeserialize( + Azure::Core::Http::RawResponse const& rawResponse); + }; + + struct KeyvaultRestoreSecretSerializer final + { + static std::string KeyvaultRestoreSecretSerialize(std::vector const& backup); + }; }}}}} // namespace Azure::Security::KeyVault::Secrets::_detail diff --git a/sdk/keyvault/azure-security-keyvault-secrets/src/secret_client.cpp b/sdk/keyvault/azure-security-keyvault-secrets/src/secret_client.cpp index 43098c968..777ce2838 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/src/secret_client.cpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/src/secret_client.cpp @@ -27,6 +27,8 @@ namespace { constexpr static const char TelemetryName[] = "keyvault-secrets"; } +const ServiceVersion ServiceVersion::V7_2("7.2"); + SecretClient::SecretClient( std::string const& vaultUrl, std::shared_ptr credential, @@ -133,4 +135,32 @@ Azure::Response SecretClient::UpdateSecretProperties( return UpdateSecretProperties(name, options, properties, context); } -const ServiceVersion ServiceVersion::V7_2("7.2"); +Azure::Response SecretClient::BackupSecret( + std::string const& name, + Azure::Core::Context const& context) const +{ + return m_protocolClient->SendRequest( + context, + Azure::Core::Http::HttpMethod::Post, + [](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyvaultBackupSecretSerializer::KeyvaultBackupSecretDeserialize( + rawResponse); + }, + {_detail::SecretPath, name, _detail::BackupSecretPath}); +} + +Azure::Response SecretClient::RestoreSecretBackup( + std::vector const& backup, + Azure::Core::Context const& context) const +{ + return m_protocolClient->SendRequest( + context, + Azure::Core::Http::HttpMethod::Post, + [&backup]() { + return _detail::KeyvaultRestoreSecretSerializer::KeyvaultRestoreSecretSerialize(backup); + }, + [](Azure::Core::Http::RawResponse const& rawResponse) { + return _detail::KeyVaultSecretSerializer::KeyVaultSecretDeserialize(rawResponse); + }, + {_detail::SecretPath, _detail::RestoreSecretPath}); +} diff --git a/sdk/keyvault/azure-security-keyvault-secrets/src/secret_serializers.cpp b/sdk/keyvault/azure-security-keyvault-secrets/src/secret_serializers.cpp index 5652246b5..3f476011e 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/src/secret_serializers.cpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/src/secret_serializers.cpp @@ -8,11 +8,12 @@ #include "private/secret_serializers.hpp" #include "private/secret_constants.hpp" - +#include #include #include #include +using namespace Azure::Core::_internal; using namespace Azure::Core::Json::_internal; using Azure::Core::_internal::PosixTimeConverter; using namespace Azure::Security::KeyVault::Secrets; @@ -157,41 +158,41 @@ void KeyVaultDeletedSecretSerializer::KeyVaultDeletedSecretDeserialize( std::string KeyVaultSecretSerializer::KeyVaultSecretSerialize(KeyVaultSecret const& parameters) { json payload; - using namespace Azure::Security::KeyVault::Secrets::_detail; // value is required payload[ValuePropertyName] = parameters.Value; // all else is optional JsonOptional::SetFromNullable( - parameters.Properties.ContentType, payload, ContentTypePropertyName); + parameters.Properties.ContentType, payload, _detail::ContentTypePropertyName); json attributes; JsonOptional::SetFromNullable( parameters.Properties.CreatedOn, attributes, - CreatedPropertyName, + _detail::CreatedPropertyName, PosixTimeConverter::DateTimeToPosixTime); - JsonOptional::SetFromNullable(parameters.Properties.Enabled, attributes, EnabledPropertyName); + JsonOptional::SetFromNullable( + parameters.Properties.Enabled, attributes, _detail::EnabledPropertyName); JsonOptional::SetFromNullable( parameters.Properties.ExpiresOn, attributes, - ExpPropertyName, + _detail::ExpPropertyName, PosixTimeConverter::DateTimeToPosixTime); JsonOptional::SetFromNullable( parameters.Properties.NotBefore, attributes, - NbfPropertyName, + _detail::NbfPropertyName, PosixTimeConverter::DateTimeToPosixTime); JsonOptional::SetFromNullable( - parameters.Properties.RecoverableDays, attributes, RecoverableDaysPropertyName); + parameters.Properties.RecoverableDays, attributes, _detail::RecoverableDaysPropertyName); JsonOptional::SetFromNullable( - parameters.Properties.RecoveryLevel, attributes, RecoveryLevelPropertyName); + parameters.Properties.RecoveryLevel, attributes, _detail::RecoveryLevelPropertyName); JsonOptional::SetFromNullable( parameters.Properties.UpdatedOn, attributes, - UpdatedPropertyName, + _detail::UpdatedPropertyName, PosixTimeConverter::DateTimeToPosixTime); // optional tags @@ -236,3 +237,23 @@ std::string KeyVaultSecretPropertiesSerializer::KeyVaultSecretPropertiesSerializ return payload.dump(); } + +BackupSecretResult KeyvaultBackupSecretSerializer::KeyvaultBackupSecretDeserialize( + Azure::Core::Http::RawResponse const& rawResponse) +{ + auto const& body = rawResponse.GetBody(); + auto jsonParser = json::parse(body); + auto encodedResult = jsonParser[_detail::ValuePropertyName].get(); + BackupSecretResult data; + data.Secret = Base64Url::Base64UrlDecode(encodedResult); + + return data; +} + +std::string KeyvaultRestoreSecretSerializer::KeyvaultRestoreSecretSerialize( + std::vector const& backup) +{ + json payload; + payload[_detail::ValuePropertyName] = Base64Url::Base64UrlEncode(backup); + return payload.dump(); +} diff --git a/sdk/keyvault/azure-security-keyvault-secrets/test/sample/test_app.cpp b/sdk/keyvault/azure-security-keyvault-secrets/test/sample/test_app.cpp index 43a2f841f..2028d9037 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/test/sample/test_app.cpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/test/sample/test_app.cpp @@ -19,14 +19,17 @@ int main() SecretClient secretClient(std::getenv("AZURE_KEYVAULT_URL"), credential); // just a response, with a secret - auto response = secretClient.GetSecret("testSecret"); - response.Value.Properties.ContentType = "weqeq"; - GetSecretOptions options; + // auto response = secretClient.GetSecret("testSecret"); + // response.Value.Properties.ContentType = "weqeq"; + // GetSecretOptions options; - response = secretClient.UpdateSecretProperties( - response.Value.Name, response.Value.Properties.Version, response.Value.Properties); + // response = secretClient.UpdateSecretProperties( + // response.Value.Name, response.Value.Properties.Version, response.Value.Properties); // just a response, with a secret - auto response3 = secretClient.GetDeletedSecret("someSecret"); + // auto response3 = secretClient.GetDeletedSecret("someSecret"); + + auto response4 = secretClient.BackupSecret("someSecret2"); + auto response5 = secretClient.RestoreSecretBackup(response4.Value.Secret); return 0; } diff --git a/sdk/keyvault/azure-security-keyvault-secrets/test/ut/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/CMakeLists.txt index 8f5120786..aee5b592b 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/test/ut/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/CMakeLists.txt @@ -17,7 +17,9 @@ add_executable ( secret_get_client_deserialize_test.cpp secret_set_parameters_serializer_test.cpp secret_update_properties_test.cpp - ) + secret_backup_deserialize_test.cpp + secret_backup_deserialize_test.hpp +) if (MSVC) target_compile_options(azure-security-keyvault-secrets-test PUBLIC /wd6326 /wd26495 /wd26812) diff --git a/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_backup_deserialize_test.cpp b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_backup_deserialize_test.cpp new file mode 100644 index 000000000..3f913ee1a --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_backup_deserialize_test.cpp @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "secret_backup_deserialize_test.hpp" +#include "../src/private/secret_serializers.hpp" +#include "azure/keyvault/secrets/secret_client.hpp" + +using namespace Azure::Security::KeyVault::Secrets; +using namespace Azure::Security::KeyVault::Secrets::_test; +using namespace Azure::Security::KeyVault::Secrets::_detail; +using namespace Azure::Core::Json::_internal; +TEST(KeyvaultBackupSecretSerializer, EmptyValue) +{ + auto response = BackupHelpers::GetEmptyResponse(); + + auto secret = _detail::KeyvaultBackupSecretSerializer::KeyvaultBackupSecretDeserialize(response); + + EXPECT_EQ(secret.Secret.size(), size_t(0)); +} + +TEST(KeyvaultBackupSecretSerializer, FullValue) +{ + auto response = BackupHelpers::GetFullResponse(); + + auto secret = _detail::KeyvaultBackupSecretSerializer::KeyvaultBackupSecretDeserialize(response); + + EXPECT_EQ(secret.Secret.size(), size_t(10)); + std::string str(secret.Secret.begin(), secret.Secret.end()); + EXPECT_EQ(str, "my name is"); +} + +TEST(KeyvaultRestoreSecretSerializer, EmptyValue) +{ + std::string str = ""; + auto data = std::vector(str.begin(), str.end()); + auto secret = _detail::KeyvaultRestoreSecretSerializer::KeyvaultRestoreSecretSerialize(data); + auto jsonParser = json::parse(secret); + + EXPECT_EQ(secret.size(), size_t(12)); + EXPECT_EQ(jsonParser["value"].get().empty(), true); +} + +TEST(KeyvaultRestoreSecretSerializer, SomeValue) +{ + std::string str = "my name is"; + + auto data = std::vector(str.begin(), str.end()); + auto secret = _detail::KeyvaultRestoreSecretSerializer::KeyvaultRestoreSecretSerialize(data); + auto jsonParser = json::parse(secret); + + EXPECT_EQ(secret.size(), size_t(26)); + EXPECT_EQ(jsonParser["value"], "bXkgbmFtZSBpcw"); +} diff --git a/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_backup_deserialize_test.hpp b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_backup_deserialize_test.hpp new file mode 100644 index 000000000..7bd7d40fc --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_backup_deserialize_test.hpp @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "azure/keyvault/secrets/keyvault_deleted_secret.hpp" +#include "azure/keyvault/secrets/keyvault_secret.hpp" +#include +#include +#include +#include + +using namespace Azure::Security::KeyVault::Secrets; +using namespace Azure::Core::Http::_internal; + +namespace Azure { namespace Security { namespace KeyVault { namespace Secrets { namespace _test { + struct BackupHelpers + { + static Azure::Core::Http::RawResponse GetEmptyResponse() + { + auto response + = Azure::Core::Http::RawResponse(1, 1, Azure::Core::Http::HttpStatusCode::Ok, "OK"); + + constexpr static const uint8_t responseBody[] = R"json({ + "value": "" + } +)json"; + + response.SetHeader(HttpShared::ContentType, "application/json"); + response.SetHeader(HttpShared::MsRequestId, "1"); + response.SetHeader(HttpShared::MsClientRequestId, "2"); + response.SetBody(std::vector(responseBody, responseBody + sizeof(responseBody))); + response.SetBodyStream(std::make_unique( + responseBody, sizeof(responseBody) - 1)); + + return response; + } + + static Azure::Core::Http::RawResponse GetFullResponse() + { + auto response + = Azure::Core::Http::RawResponse(1, 1, Azure::Core::Http::HttpStatusCode::Ok, "OK"); + + constexpr static const uint8_t responseBody[] = R"json({ + "value": "bXkgbmFtZSBpcw==" + } +)json"; + + response.SetHeader(HttpShared::ContentType, "application/json"); + response.SetHeader(HttpShared::MsRequestId, "1"); + response.SetHeader(HttpShared::MsClientRequestId, "2"); + response.SetBody(std::vector(responseBody, responseBody + sizeof(responseBody))); + response.SetBodyStream(std::make_unique( + responseBody, sizeof(responseBody) - 1)); + + return response; + } + + static Azure::Core::Http::RawResponse GetIncorrectResponse() + { + auto response + = Azure::Core::Http::RawResponse(1, 1, Azure::Core::Http::HttpStatusCode::Ok, "OK"); + + constexpr static const uint8_t responseBody[] = R"json({ + "value": "my name is" + } +)json"; + + response.SetHeader(HttpShared::ContentType, "application/json"); + response.SetHeader(HttpShared::MsRequestId, "1"); + response.SetHeader(HttpShared::MsClientRequestId, "2"); + response.SetBody(std::vector(responseBody, responseBody + sizeof(responseBody))); + response.SetBodyStream(std::make_unique( + responseBody, sizeof(responseBody) - 1)); + + return response; + } + }; +}}}}} // namespace Azure::Security::KeyVault::Secrets::_test diff --git a/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_get_client_deserialize_test.hpp b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_get_client_deserialize_test.hpp index 5e11773d2..8f4b18f4a 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_get_client_deserialize_test.hpp +++ b/sdk/keyvault/azure-security-keyvault-secrets/test/ut/secret_get_client_deserialize_test.hpp @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // SPDX-License-Identifier: MIT -#include "azure/core/http/http.hpp" -#include "azure/core/http/policies/policy.hpp" #include "azure/keyvault/secrets/keyvault_deleted_secret.hpp" #include "azure/keyvault/secrets/keyvault_secret.hpp" +#include +#include #include #include