Adding GetDeletedKey (#1907)

* Adding GetDeletedKey
This commit is contained in:
Victor Vazquez 2021-03-17 11:15:35 -07:00 committed by GitHub
parent 5210e8eb0d
commit a5bc8fe0ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 186 additions and 19 deletions

View File

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Define a convenience layer on top of Json for setting optional fields.
*
*/
#pragma once
#include "azure/core/internal/json/json.hpp"
#include "azure/core/nullable.hpp"
#include <functional>
#include <string>
namespace Azure { namespace Core { namespace Json { namespace _internal {
/**
* @brief Define a wrapper for working with Json containing optional fields.
*
*/
struct JsonOptional
{
/**
* @brief If the optional key \p key is present in the json node \p jsonKey set the value of \p
* destination.
*
* @remark If the key is not in the json node, the \p destination is not modified.
*
* @param jsonKey The json node to review.
* @param key The key name for the optional property.
* @param destination The value to update if the key name property is in the json node.
*/
template <class T>
static inline void SetIfExists(
Azure::Nullable<T>& destination,
Azure::Core::Json::_internal::json const& jsonKey,
std::string const& key) noexcept
{
if (jsonKey.contains(key))
{
destination = jsonKey[key].get<T>();
}
}
/**
* @brief If the optional key \p key is present in the json node \p jsonKey set the value of \p
* destination.
*
* @remark If the key is not in the json node, the \p destination is not modified.
*
* @param jsonKey The json node to review.
* @param key The key name for the optional property.
* @param destination The value to update if the key name property is in the json node.
* @param decorator A optional function to update the json value before updating the \p
* destination.
*/
template <class T, class V>
static inline void SetIfExists(
Azure::Nullable<V>& destination,
Azure::Core::Json::_internal::json const& jsonKey,
std::string const& key,
std::function<V(T value)> decorator) noexcept
{
if (jsonKey.contains(key))
{
destination = decorator(jsonKey[key].get<T>());
}
}
};
}}}} // namespace Azure::Core::Json::_internal

View File

@ -163,5 +163,20 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
Azure::Security::KeyVault::Keys::DeleteKeyOperation StartDeleteKey(
std::string const& name,
Azure::Core::Context const& context = Azure::Core::Context()) const;
/**
* @brief Gets the public part of a deleted key.
*
* @remark The Get Deleted Key operation is applicable for soft-delete enabled vaults. While the
* operation can be invoked on any vault, it will return an error if invoked on a non
* soft-delete enabled vault. This operation requires the keys/get permission.
*
* @param name The name of the key.
* @param context A #Azure::Core::Context controlling the request lifetime.
* @return Azure::Response<DeletedKey>
*/
Azure::Response<DeletedKey> GetDeletedKey(
std::string const& name,
Azure::Core::Context const& context = Azure::Core::Context()) const;
};
}}}} // namespace Azure::Security::KeyVault::Keys

View File

@ -36,6 +36,10 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation::PollInternal(Azure::Core::C
rawResponse = m_pipeline->GetResponse(
context, Azure::Core::Http::HttpMethod::Get, {_detail::DeletedKeysPath, m_value.Name()});
m_status = CheckCompleted(*rawResponse);
if (m_status == Azure::Core::OperationStatus::Succeeded)
{
m_value = _detail::DeletedKeyDeserialize(m_value.Name(), *rawResponse);
}
}
// To ensure the success of calling Poll multiple times, even after operation is completed, a

View File

@ -129,3 +129,16 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation KeyClient::StartDeleteKey(
},
{_detail::KeysPath, name}));
}
Azure::Response<DeletedKey> KeyClient::GetDeletedKey(
std::string const& name,
Azure::Core::Context const& context) const
{
return m_pipeline->SendRequest<DeletedKey>(
context,
Azure::Core::Http::HttpMethod::Get,
[&name](Azure::Core::Http::RawResponse const& rawResponse) {
return _detail::DeletedKeyDeserialize(name, rawResponse);
},
{_detail::DeletedKeysPath, name});
}

View File

@ -8,8 +8,11 @@
#include <azure/keyvault/common/internal/unix_time_helper.hpp>
#include <azure/core/internal/json/json.hpp>
#include <azure/core/internal/json/json_optional.hpp>
#include <azure/core/internal/json/json_serializable.hpp>
using namespace Azure::Security::KeyVault::Keys;
using namespace Azure::Core::Json::_internal;
using Azure::Security::KeyVault::Common::_internal::UnixTimeConverter;
namespace {
@ -38,39 +41,61 @@ void _detail::KeyVaultKeyDeserialize(
Azure::Core::Http::RawResponse const& rawResponse)
{
auto body = rawResponse.GetBody();
auto jsonParser = Azure::Core::Json::_internal::json::parse(body);
auto jsonParser = json::parse(body);
// "Key"
auto const& jsonKey = jsonParser[_detail::KeyPropertyName];
if (jsonParser.contains(_detail::KeyPropertyName))
{
// key_ops
auto keyOperationVector = jsonKey[_detail::KeyOpsPropertyName].get<std::vector<std::string>>();
std::vector<KeyOperation> keyOperations;
ParseStringOperationsToKeyOperations(keyOperations, keyOperationVector);
key.Key.SetKeyOperations(keyOperations);
}
key.Key.Id = jsonKey[_detail::KeyIdPropertyName].get<std::string>();
key.Key.KeyType
= _detail::KeyTypeFromString(jsonKey[_detail::KeyTypePropertyName].get<std::string>());
auto const& jsonKey = jsonParser[_detail::KeyPropertyName];
{
// key_ops
auto keyOperationVector
= jsonKey[_detail::KeyOpsPropertyName].get<std::vector<std::string>>();
std::vector<KeyOperation> keyOperations;
ParseStringOperationsToKeyOperations(keyOperations, keyOperationVector);
key.Key.SetKeyOperations(keyOperations);
}
key.Key.Id = jsonKey[_detail::KeyIdPropertyName].get<std::string>();
key.Key.KeyType
= _detail::KeyTypeFromString(jsonKey[_detail::KeyTypePropertyName].get<std::string>());
if (jsonKey.contains(_detail::CurveNamePropertyName))
{
key.Key.CurveName = KeyCurveName(jsonKey[_detail::CurveNamePropertyName].get<std::string>());
JsonOptional::SetIfExists<std::string, KeyCurveName>(
key.Key.CurveName, jsonKey, _detail::CurveNamePropertyName, [](std::string const& keyName) {
return KeyCurveName(keyName);
});
}
// "Attributes"
if (jsonParser.contains(_detail::AttributesPropertyName))
{
auto attributes = jsonParser[_detail::AttributesPropertyName];
key.Properties.CreatedOn
= UnixTimeConverter::UnixTimeToDatetime(attributes["created"].get<uint64_t>());
JsonOptional::SetIfExists(key.Properties.Enabled, attributes, "enabled");
JsonOptional::SetIfExists<uint64_t, Azure::DateTime>(
key.Properties.NotBefore, attributes, "nbf", UnixTimeConverter::UnixTimeToDatetime);
JsonOptional::SetIfExists<uint64_t, Azure::DateTime>(
key.Properties.ExpiresOn, attributes, "exp", UnixTimeConverter::UnixTimeToDatetime);
JsonOptional::SetIfExists<uint64_t, Azure::DateTime>(
key.Properties.CreatedOn, attributes, "created", UnixTimeConverter::UnixTimeToDatetime);
JsonOptional::SetIfExists<uint64_t, Azure::DateTime>(
key.Properties.UpdatedOn, attributes, "updated", UnixTimeConverter::UnixTimeToDatetime);
}
// "Tags"
auto const& tags = jsonParser[_detail::TagsPropertyName];
if (jsonParser.contains(_detail::TagsPropertyName))
{
for (auto tag = tags.begin(); tag != tags.end(); ++tag)
auto const& tags = jsonParser[_detail::TagsPropertyName];
{
key.Properties.Tags.emplace(tag.key(), tag.value().get<std::string>());
for (auto tag = tags.begin(); tag != tags.end(); ++tag)
{
key.Properties.Tags.emplace(tag.key(), tag.value().get<std::string>());
}
}
}
// managed
if (jsonParser.contains(_detail::ManagedPropertyName))
{
key.Properties.Managed = jsonParser[_detail::ManagedPropertyName].get<bool>();
}
}

View File

@ -132,6 +132,7 @@ TEST_F(KeyVaultClientTest, CreateEcKeyWithCurve)
CheckValidResponse(keyResponse);
auto keyVaultKey = keyResponse.ExtractValue();
EXPECT_EQ(keyVaultKey.Name(), keyName);
EXPECT_EQ(ecKey.CurveName->ToString(), keyVaultKey.Key.CurveName->ToString());
}
{
// Now get the key

View File

@ -271,3 +271,38 @@ TEST_F(KeyVaultClientTest, CreateDeletedKeyBeforePollComplete)
}
EXPECT_TRUE(wasThrown);
}
// Get Delete Key
TEST_F(KeyVaultClientTest, GetDeletedKey)
{
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
std::string keyName("deleteThisKeyAndGetItFromDeleteKeys1wwxx3x4");
{
auto keyResponse
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::JsonWebKeyType::Ec);
CheckValidResponse(keyResponse);
auto keyVaultKey = keyResponse.ExtractValue();
EXPECT_EQ(keyVaultKey.Name(), keyName);
}
{
// Wait until key is deleted
auto duration = std::chrono::system_clock::now() + std::chrono::minutes(3);
auto cancelToken = Azure::Core::Context::GetApplicationContext().WithDeadline(duration);
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
auto expectedStatusToken = m_keyVaultUrl + "/"
+ std::string(Azure::Security::KeyVault::Keys::_detail::DeletedKeysPath) + "/" + keyName;
auto keyResponse = keyResponseLRO.PollUntilDone(std::chrono::milliseconds(1000), cancelToken);
}
{
// Get the deleted key
auto deletedKey = keyClient.GetDeletedKey(keyName).ExtractValue();
EXPECT_FALSE(deletedKey.RecoveryId.empty());
EXPECT_EQ(deletedKey.Name(), keyName);
auto expectedType = Azure::Security::KeyVault::Keys::JsonWebKeyType::Ec;
EXPECT_EQ(
Azure::Security::KeyVault::Keys::_detail::KeyTypeToString(expectedType),
Azure::Security::KeyVault::Keys::_detail::KeyTypeToString(deletedKey.Key.KeyType));
}
}