parent
5f523f7030
commit
cea5e03a17
@ -634,6 +634,24 @@ namespace Azure { namespace Core { namespace Http {
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a raw response to construct a new one.
|
||||
*
|
||||
* @remark The body stream won't be copied.
|
||||
*
|
||||
* @param response A reference for copying the raw response.
|
||||
*/
|
||||
RawResponse(RawResponse const& response)
|
||||
: RawResponse(
|
||||
response.m_majorVersion,
|
||||
response.m_minorVersion,
|
||||
response.m_statusCode,
|
||||
response.m_reasonPhrase)
|
||||
{
|
||||
// Copy body
|
||||
m_body = response.GetBody();
|
||||
}
|
||||
|
||||
// ===== Methods used to build HTTP response =====
|
||||
|
||||
/**
|
||||
|
||||
@ -29,6 +29,7 @@ endif()
|
||||
set(
|
||||
AZURE_KEYVAULT_COMMON_HEADER
|
||||
inc/azure/keyvault/common/internal/keyvault_pipeline.hpp
|
||||
inc/azure/keyvault/common/internal/unix_time_helper.hpp
|
||||
inc/azure/keyvault/common/keyvault_constants.hpp
|
||||
inc/azure/keyvault/common/keyvault_exception.hpp
|
||||
inc/azure/keyvault/common/version.hpp
|
||||
|
||||
@ -130,5 +130,31 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Common { n
|
||||
auto response = SendRequest(context, request);
|
||||
return Azure::Core::Response<T>(factoryFn(*response), std::move(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a key vault request and send it using the Azure Core pipeline directly to avoid
|
||||
* checking the respone code.
|
||||
*
|
||||
* @param context A context for cancellation.
|
||||
* @param method The Http method for the request.
|
||||
* @param path The path for the request.
|
||||
* @return A unique ptr to an Http raw response.
|
||||
*/
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> GetResponse(
|
||||
Azure::Core::Context const& context,
|
||||
Azure::Core::Http::HttpMethod method,
|
||||
std::vector<std::string> const& path)
|
||||
{
|
||||
auto request = CreateRequest(method, path);
|
||||
// Use the core pipeline directly to avoid checking the response code.
|
||||
return m_pipeline.Send(context, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the Vault Url which was used to create the #KeyVaultPipeline.
|
||||
*
|
||||
* @return The vault Url as string.
|
||||
*/
|
||||
std::string GetVaultUrl() const { return m_vaultUrl.GetAbsoluteUrl(); }
|
||||
};
|
||||
}}}}} // namespace Azure::Security::KeyVault::Common::Internal
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @brief Provides helper method for using unix time.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/datetime.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Azure { namespace Security { namespace KeyVault { namespace Common { namespace Internal {
|
||||
|
||||
/**
|
||||
* @brief Provides convertion methods for unix time to Azure Core Datetime.
|
||||
*/
|
||||
class UnixTimeConverter {
|
||||
public:
|
||||
/**
|
||||
* @brief Converts unix time to a #Azure::Core::Datetime.
|
||||
*
|
||||
* @param unixTime The number of seconds since 1970.
|
||||
* @return Calculated Datetime.
|
||||
*/
|
||||
static inline Azure::Core::DateTime UnixTimeToDatetime(uint64_t unixTime)
|
||||
{
|
||||
return Azure::Core::DateTime(1970) + std::chrono::seconds(unixTime);
|
||||
}
|
||||
};
|
||||
}}}}} // namespace Azure::Security::KeyVault::Common::Internal
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
namespace Azure { namespace Security { namespace KeyVault { namespace Common { namespace Details {
|
||||
/***************** KeyVault headers *****************/
|
||||
static constexpr char const ContentType[] = "Content-Type";
|
||||
static constexpr char const ContentType[] = "content-type";
|
||||
static constexpr char const ApplicationJson[] = "application/json";
|
||||
static constexpr char const Accept[] = "Accept";
|
||||
static constexpr char const Accept[] = "accept";
|
||||
static constexpr char const MsRequestId[] = "x-ms-request-id";
|
||||
static constexpr char const MsClientRequestId[] = "x-ms-client-request-id";
|
||||
|
||||
|
||||
@ -81,5 +81,14 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Common {
|
||||
*/
|
||||
static KeyVaultException CreateFromResponse(
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> response);
|
||||
|
||||
/**
|
||||
* @brief Create #Azure::Security::KeyVault::Common::KeyVaultException by parsing the \p
|
||||
* response reference.
|
||||
*
|
||||
* @param response The Http raw response from the network.
|
||||
* @return KeyVaultException.
|
||||
*/
|
||||
static KeyVaultException CreateFromResponse(Azure::Core::Http::RawResponse const& response);
|
||||
};
|
||||
}}}} // namespace Azure::Security::KeyVault::Common
|
||||
|
||||
@ -30,11 +30,17 @@ inline std::string GetHeaderOrEmptyString(
|
||||
KeyVaultException KeyVaultException::CreateFromResponse(
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> response)
|
||||
{
|
||||
std::vector<uint8_t> bodyBuffer = std::move(response->GetBody());
|
||||
return CreateFromResponse(*response);
|
||||
}
|
||||
|
||||
auto httpStatusCode = response->GetStatusCode();
|
||||
std::string reasonPhrase = response->GetReasonPhrase();
|
||||
auto& headers = response->GetHeaders();
|
||||
KeyVaultException KeyVaultException::CreateFromResponse(
|
||||
Azure::Core::Http::RawResponse const& response)
|
||||
{
|
||||
std::vector<uint8_t> bodyBuffer = std::move(response.GetBody());
|
||||
|
||||
auto httpStatusCode = response.GetStatusCode();
|
||||
std::string reasonPhrase = response.GetReasonPhrase();
|
||||
auto& headers = response.GetHeaders();
|
||||
std::string requestId = GetHeaderOrEmptyString(headers, Details::MsRequestId);
|
||||
std::string clientRequestId = GetHeaderOrEmptyString(headers, Details::MsClientRequestId);
|
||||
std::string contentType = GetHeaderOrEmptyString(headers, Details::ContentType);
|
||||
@ -62,6 +68,6 @@ KeyVaultException KeyVaultException::CreateFromResponse(
|
||||
result.RequestId = std::move(requestId);
|
||||
result.ErrorCode = std::move(errorCode);
|
||||
result.Message = std::move(message);
|
||||
result.RawResponse = std::move(response);
|
||||
result.RawResponse = std::make_unique<Azure::Core::Http::RawResponse>(response);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -28,6 +28,8 @@ endif()
|
||||
|
||||
set(
|
||||
AZURE_KEYVAULT_KEYS_HEADER
|
||||
inc/azure/keyvault/keys/delete_key_operation.hpp
|
||||
inc/azure/keyvault/keys/deleted_key.hpp
|
||||
inc/azure/keyvault/keys/json_web_key.hpp
|
||||
inc/azure/keyvault/keys/key_client.hpp
|
||||
inc/azure/keyvault/keys/key_constants.hpp
|
||||
@ -44,6 +46,8 @@ set(
|
||||
|
||||
set(
|
||||
AZURE_KEYVAULT_KEYS_SOURCE
|
||||
src/delete_key_operation.cpp
|
||||
src/deleted_key.cpp
|
||||
src/key_client.cpp
|
||||
src/key_request_parameters.cpp
|
||||
src/key_type.cpp
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "azure/keyvault/keys/delete_key_operation.hpp"
|
||||
#include "azure/keyvault/keys/deleted_key.hpp"
|
||||
#include "azure/keyvault/keys/dll_import_export.hpp"
|
||||
#include "azure/keyvault/keys/json_web_key.hpp"
|
||||
#include "azure/keyvault/keys/key_client.hpp"
|
||||
|
||||
@ -0,0 +1,122 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @brief A long-running operation for deleting a Key.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/core/operation.hpp>
|
||||
#include <azure/core/operation_status.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
|
||||
#include <azure/keyvault/common/internal/keyvault_pipeline.hpp>
|
||||
#include <azure/keyvault/common/keyvault_exception.hpp>
|
||||
|
||||
#include "azure/keyvault/keys/deleted_key.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
|
||||
|
||||
/**
|
||||
* @brief A long running operation to delete a key.
|
||||
*
|
||||
*/
|
||||
class DeleteKeyOperation
|
||||
: public Azure::Core::Operation<Azure::Security::KeyVault::Keys::DeletedKey> {
|
||||
private:
|
||||
/* DeleteKeyOperation can be constructed only by friends classes (internal creation). The
|
||||
* constructor is private and requires internal components.*/
|
||||
friend class KeyClient;
|
||||
|
||||
std::shared_ptr<Azure::Security::KeyVault::Common::Internal::KeyVaultPipeline> m_pipeline;
|
||||
Azure::Security::KeyVault::Keys::DeletedKey m_value;
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> m_rawResponse;
|
||||
std::string m_continuationToken;
|
||||
|
||||
/* This is the implementation for checking the status of a deleted key. The key is considered
|
||||
* deleted if querying /deletedkeys/keyName returns 200 from server. Or whenever soft-delete is
|
||||
* disabled.*/
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> PollInternal(
|
||||
Azure::Core::Context& context) override;
|
||||
|
||||
Azure::Core::Response<Azure::Security::KeyVault::Keys::DeletedKey> PollUntilDoneInternal(
|
||||
Azure::Core::Context& context,
|
||||
std::chrono::milliseconds period) override
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
m_rawResponse = Poll(context);
|
||||
if (IsDone())
|
||||
{
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(period);
|
||||
}
|
||||
|
||||
return Azure::Core::Response<Azure::Security::KeyVault::Keys::DeletedKey>(
|
||||
m_value, std::make_unique<Azure::Core::Http::RawResponse>(*m_rawResponse));
|
||||
}
|
||||
|
||||
/*
|
||||
* Only friend classes are permitted to construct a DeleteOperation. This is because a
|
||||
* KeyVaultPipelne is required and it is not exposed to customers.
|
||||
*
|
||||
* Since C++ doesn't offer `internal` access, we use friends-only instead.
|
||||
*/
|
||||
DeleteKeyOperation(
|
||||
std::shared_ptr<Azure::Security::KeyVault::Common::Internal::KeyVaultPipeline>
|
||||
keyvaultPipeline,
|
||||
Azure::Core::Response<Azure::Security::KeyVault::Keys::DeletedKey> response)
|
||||
: m_pipeline(keyvaultPipeline)
|
||||
{
|
||||
if (!response.HasValue())
|
||||
{
|
||||
throw Azure::Security::KeyVault::Common::KeyVaultException(
|
||||
"The response does not contain a value.");
|
||||
}
|
||||
// The response becomes useless and the value and rawResponse are now owned by the
|
||||
// DeleteKeyOperation. This is fine because the DeleteKeyOperation is what the delete key api
|
||||
// will return.
|
||||
m_value = response.ExtractValue();
|
||||
m_rawResponse = response.ExtractRawResponse();
|
||||
|
||||
// Build the full url for continuation token. It is only used in case customers wants to use
|
||||
// it on their own. The Operation uses the KeyVaultPipeline from the client which knows how to
|
||||
// build this url.
|
||||
m_continuationToken = m_pipeline->GetVaultUrl() + "/" + std::string(Details::DeletedKeysPath)
|
||||
+ "/" + m_value.Name();
|
||||
|
||||
// The recoveryId is only returned if soft-delete is enabled.
|
||||
// The LRO is considered completed for non soft-delete (key will be eventually removed).
|
||||
if (m_value.RecoveryId.empty())
|
||||
{
|
||||
m_status = Azure::Core::OperationStatus::Succeeded;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get the #DeletedKey object.
|
||||
*
|
||||
* @remark The deleted key contains the recovery id if the key can be recovered.
|
||||
*
|
||||
* @return A deleted key object.
|
||||
*/
|
||||
Azure::Security::KeyVault::Keys::DeletedKey Value() const override { return m_value; }
|
||||
|
||||
/**
|
||||
* @brief Get an Url as string which can be used to get the status of the delete key operation.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string GetResumeToken() const override { return m_continuationToken; }
|
||||
};
|
||||
|
||||
}}}} // namespace Azure::Security::KeyVault::Keys
|
||||
@ -0,0 +1,64 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @brief Represents a Key Vault key that has been deleted, allowing it to be recovered, if needed.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/datetime.hpp>
|
||||
|
||||
#include "azure/keyvault/keys/key_constants.hpp"
|
||||
#include "azure/keyvault/keys/key_vault_key.hpp"
|
||||
|
||||
namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
|
||||
|
||||
/**
|
||||
* @brief Represents a Key Vault key that has been deleted, allowing it to be recovered, if
|
||||
* needed.
|
||||
*
|
||||
*/
|
||||
struct DeletedKey : public KeyVaultKey
|
||||
{
|
||||
/**
|
||||
* @brief A recovery url that can be used to recover it.
|
||||
*
|
||||
*/
|
||||
std::string RecoveryId;
|
||||
|
||||
/**
|
||||
* @brief Construct an empty DeletedKey
|
||||
*
|
||||
*/
|
||||
DeletedKey() = default;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Deleted Key object.
|
||||
*
|
||||
* @param name THe name of the deleted key.
|
||||
*/
|
||||
DeletedKey(std::string name) : KeyVaultKey(name) {}
|
||||
|
||||
/**
|
||||
* @brief Indicate when the key was deleted.
|
||||
*
|
||||
*/
|
||||
Azure::Core::DateTime DeletedDate;
|
||||
|
||||
/**
|
||||
* @brief Indicate when the deleted key will be purged.
|
||||
*
|
||||
*/
|
||||
Azure::Core::DateTime ScheduledPurgeDate;
|
||||
};
|
||||
|
||||
/*********************** Deserializer / Serializer ******************************/
|
||||
namespace Details {
|
||||
DeletedKey DeletedKeyDeserialize(
|
||||
std::string const& name,
|
||||
Azure::Core::Http::RawResponse const& rawResponse);
|
||||
} // namespace Details
|
||||
|
||||
}}}} // namespace Azure::Security::KeyVault::Keys
|
||||
@ -11,8 +11,10 @@
|
||||
#include <azure/core/credentials.hpp>
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
|
||||
#include <azure/keyvault/common/internal/keyvault_pipeline.hpp>
|
||||
|
||||
#include "azure/keyvault/keys/delete_key_operation.hpp"
|
||||
#include "azure/keyvault/keys/key_client_options.hpp"
|
||||
#include "azure/keyvault/keys/key_constants.hpp"
|
||||
#include "azure/keyvault/keys/key_create_options.hpp"
|
||||
@ -32,7 +34,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
|
||||
*/
|
||||
class KeyClient {
|
||||
protected:
|
||||
std::unique_ptr<Azure::Security::KeyVault::Common::Internal::KeyVaultPipeline> m_pipeline;
|
||||
// Using a shared pipeline for a client to share it with LRO (like delete key)
|
||||
std::shared_ptr<Azure::Security::KeyVault::Common::Internal::KeyVaultPipeline> m_pipeline;
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -83,7 +86,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
|
||||
return m_pipeline->SendRequest<KeyVaultKey>(
|
||||
context,
|
||||
Azure::Core::Http::HttpMethod::Get,
|
||||
[name](Azure::Core::Http::RawResponse const& rawResponse) {
|
||||
[&name](Azure::Core::Http::RawResponse const& rawResponse) {
|
||||
return Details::KeyVaultKeyDeserialize(name, rawResponse);
|
||||
},
|
||||
{Details::KeysPath, name, options.Version});
|
||||
@ -111,10 +114,39 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
|
||||
context,
|
||||
Azure::Core::Http::HttpMethod::Post,
|
||||
Details::KeyRequestParameters(keyType, options),
|
||||
[name](Azure::Core::Http::RawResponse const& rawResponse) {
|
||||
[&name](Azure::Core::Http::RawResponse const& rawResponse) {
|
||||
return Details::KeyVaultKeyDeserialize(name, rawResponse);
|
||||
},
|
||||
{Details::KeysPath, name, "create"});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deletes a key of any type from storage in Azure Key Vault.
|
||||
*
|
||||
* @remark The delete key operation cannot be used to remove individual versions of a key. This
|
||||
* operation removes the cryptographic material associated with the key, which means the key is
|
||||
* not usable for Sign/Verify, Wrap/Unwrap or Encrypt/Decrypt operations. This operation
|
||||
* requires the keys/delete permission.
|
||||
*
|
||||
* @param name The name of the key.
|
||||
* @param context A cancellation token controlling the request lifetime.
|
||||
* @return A #DeleteKeyOperation to wait on this long-running operation. If the key is soft
|
||||
* delete-enabled, you only need to wait for the operation to complete if you need to recover or
|
||||
* purge the key; otherwise, the key is deleted automatically on purge schedule.
|
||||
*/
|
||||
Azure::Security::KeyVault::Keys::DeleteKeyOperation StartDeleteKey(
|
||||
std::string const& name,
|
||||
Azure::Core::Context const& context = Azure::Core::Context()) const
|
||||
{
|
||||
return Azure::Security::KeyVault::Keys::DeleteKeyOperation(
|
||||
m_pipeline,
|
||||
m_pipeline->SendRequest<Azure::Security::KeyVault::Keys::DeletedKey>(
|
||||
context,
|
||||
Azure::Core::Http::HttpMethod::Delete,
|
||||
[&name](Azure::Core::Http::RawResponse const& rawResponse) {
|
||||
return Details::DeletedKeyDeserialize(name, rawResponse);
|
||||
},
|
||||
{Details::KeysPath, name}));
|
||||
}
|
||||
};
|
||||
}}}} // namespace Azure::Security::KeyVault::Keys
|
||||
|
||||
@ -52,4 +52,9 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam
|
||||
constexpr static const char OctValue[] = "oct";
|
||||
constexpr static const char OctHsmValue[] = "oct-HSM";
|
||||
|
||||
/***************** Deleted Key *****************/
|
||||
constexpr static const char RecoveryIdPropertyName[] = "recoveryId";
|
||||
constexpr static const char DeletedOnPropertyName[] = "deletedDate";
|
||||
constexpr static const char ScheduledPurgeDatePropertyName[] = "scheduledPurgeDate";
|
||||
|
||||
}}}}} // namespace Azure::Security::KeyVault::Keys::Details
|
||||
|
||||
@ -19,24 +19,77 @@
|
||||
|
||||
namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
|
||||
|
||||
/**
|
||||
* @brief A key resource and its properties.
|
||||
*
|
||||
*/
|
||||
struct KeyVaultKey
|
||||
{
|
||||
/**
|
||||
* @brief The cryptographic key, the key type, and the operations you can perform using the key.
|
||||
*
|
||||
*/
|
||||
JsonWebKey Key;
|
||||
|
||||
/**
|
||||
* @brief The additional properties.
|
||||
*
|
||||
*/
|
||||
KeyProperties Properties;
|
||||
|
||||
/**
|
||||
* @brief Construct an empty Key.
|
||||
*
|
||||
*/
|
||||
KeyVaultKey() = default;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Key Vault Key object.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
KeyVaultKey(std::string name) : Properties(std::move(name)) {}
|
||||
|
||||
/**
|
||||
* @brief Get the Key identifier.
|
||||
*
|
||||
* @return The key id.
|
||||
*/
|
||||
std::string const& Id() const { return Key.Id; }
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the Key.
|
||||
*
|
||||
* @return The name of the key.
|
||||
*/
|
||||
std::string const& Name() const { return Properties.Name; }
|
||||
|
||||
/**
|
||||
* @brief Get the Key Type.
|
||||
*
|
||||
* @return The type of the Key.
|
||||
*/
|
||||
KeyTypeEnum const& GetKeyType() const { return Key.KeyType; }
|
||||
|
||||
/**
|
||||
* @brief Gets the operations you can perform using the key.
|
||||
*
|
||||
* @return A vector with the supported operations for the key.
|
||||
*/
|
||||
std::vector<KeyOperation> const& KeyOperations() const { return Key.KeyOperations(); }
|
||||
};
|
||||
|
||||
/*********************** Deserializer / Serializer ******************************/
|
||||
namespace Details {
|
||||
// Creates a new key based on a name and an http raw response.
|
||||
KeyVaultKey KeyVaultKeyDeserialize(
|
||||
std::string const& name,
|
||||
Azure::Core::Http::RawResponse const& rawResponse);
|
||||
|
||||
// Updates a Key based on an Http raw response.
|
||||
void KeyVaultKeyDeserialize(
|
||||
KeyVaultKey& key,
|
||||
Azure::Core::Http::RawResponse const& rawResponse);
|
||||
} // namespace Details
|
||||
|
||||
}}}} // namespace Azure::Security::KeyVault::Keys
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/keyvault/keys/delete_key_operation.hpp"
|
||||
|
||||
using namespace Azure::Security::KeyVault::Keys;
|
||||
|
||||
namespace {
|
||||
|
||||
// For delete key, the LRO ends when we can retreive the Key from the deleted keys list from the
|
||||
// server.
|
||||
inline Azure::Core::OperationStatus CheckCompleted(Azure::Core::Http::RawResponse const& response)
|
||||
{
|
||||
auto code = response.GetStatusCode();
|
||||
switch (code)
|
||||
{
|
||||
case Azure::Core::Http::HttpStatusCode::Ok:
|
||||
case Azure::Core::Http::HttpStatusCode::Forbidden: // Access denied but proof the key was
|
||||
// deleted.
|
||||
return Azure::Core::OperationStatus::Succeeded;
|
||||
case Azure::Core::Http::HttpStatusCode::NotFound:
|
||||
return Azure::Core::OperationStatus::Running;
|
||||
default:
|
||||
throw Azure::Security::KeyVault::Common::KeyVaultException::CreateFromResponse(response);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse>
|
||||
Azure::Security::KeyVault::Keys::DeleteKeyOperation::PollInternal(Azure::Core::Context& context)
|
||||
{
|
||||
if (!IsDone())
|
||||
{
|
||||
m_rawResponse = m_pipeline->GetResponse(
|
||||
context, Azure::Core::Http::HttpMethod::Get, {Details::DeletedKeysPath, m_value.Name()});
|
||||
m_status = CheckCompleted(*m_rawResponse);
|
||||
}
|
||||
|
||||
// To ensure the success of calling Poll multiple times, even after operation is completed, a
|
||||
// copy of the raw http response is returned instead of transfering the ownership of the raw
|
||||
// response inside the Operation.
|
||||
return std::make_unique<Azure::Core::Http::RawResponse>(*m_rawResponse);
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/keyvault/keys/deleted_key.hpp"
|
||||
#include "azure/keyvault/keys/key_constants.hpp"
|
||||
#include "azure/keyvault/keys/key_vault_key.hpp"
|
||||
|
||||
#include <azure/keyvault/common/internal/unix_time_helper.hpp>
|
||||
|
||||
#include <azure/core/internal/json.hpp>
|
||||
|
||||
using namespace Azure::Security::KeyVault::Keys;
|
||||
using Azure::Security::KeyVault::Common::Internal::UnixTimeConverter;
|
||||
|
||||
DeletedKey Details::DeletedKeyDeserialize(
|
||||
std::string const& name,
|
||||
Azure::Core::Http::RawResponse const& rawResponse)
|
||||
{
|
||||
auto body = rawResponse.GetBody();
|
||||
auto jsonParser = Azure::Core::Internal::Json::json::parse(body);
|
||||
|
||||
// "Key"
|
||||
DeletedKey deletedKey(name);
|
||||
Details::KeyVaultKeyDeserialize(deletedKey, rawResponse);
|
||||
|
||||
// recoveryId
|
||||
// deletedDate
|
||||
// scheduledPurgeDate
|
||||
deletedKey.RecoveryId = jsonParser[Details::RecoveryIdPropertyName].get<std::string>();
|
||||
deletedKey.DeletedDate = UnixTimeConverter::UnixTimeToDatetime(
|
||||
jsonParser[Details::DeletedOnPropertyName].get<uint64_t>());
|
||||
deletedKey.ScheduledPurgeDate = UnixTimeConverter::UnixTimeToDatetime(
|
||||
jsonParser[Details::ScheduledPurgeDatePropertyName].get<uint64_t>());
|
||||
|
||||
return deletedKey;
|
||||
}
|
||||
@ -41,6 +41,6 @@ KeyClient::KeyClient(
|
||||
std::make_unique<Azure::Core::Http::TransportPolicy>(options.TransportPolicyOptions));
|
||||
Azure::Core::Http::Url url(vaultUrl);
|
||||
|
||||
m_pipeline = std::make_unique<Azure::Security::KeyVault::Common::Internal::KeyVaultPipeline>(
|
||||
m_pipeline = std::make_shared<Azure::Security::KeyVault::Common::Internal::KeyVaultPipeline>(
|
||||
url, apiVersion, std::move(policies));
|
||||
}
|
||||
|
||||
@ -4,9 +4,12 @@
|
||||
#include "azure/keyvault/keys/key_vault_key.hpp"
|
||||
#include "azure/keyvault/keys/key_constants.hpp"
|
||||
|
||||
#include <azure/keyvault/common/internal/unix_time_helper.hpp>
|
||||
|
||||
#include <azure/core/internal/json.hpp>
|
||||
|
||||
using namespace Azure::Security::KeyVault::Keys;
|
||||
using Azure::Security::KeyVault::Common::Internal::UnixTimeConverter;
|
||||
|
||||
namespace {
|
||||
void ParseStringOperationsToKeyOperations(
|
||||
@ -23,12 +26,20 @@ void ParseStringOperationsToKeyOperations(
|
||||
KeyVaultKey Details::KeyVaultKeyDeserialize(
|
||||
std::string const& name,
|
||||
Azure::Core::Http::RawResponse const& rawResponse)
|
||||
{
|
||||
KeyVaultKey key(name);
|
||||
Details::KeyVaultKeyDeserialize(key, rawResponse);
|
||||
return key;
|
||||
}
|
||||
|
||||
void Details::KeyVaultKeyDeserialize(
|
||||
KeyVaultKey& key,
|
||||
Azure::Core::Http::RawResponse const& rawResponse)
|
||||
{
|
||||
auto body = rawResponse.GetBody();
|
||||
auto jsonParser = Azure::Core::Internal::Json::json::parse(body);
|
||||
|
||||
// "Key"
|
||||
KeyVaultKey key(name);
|
||||
auto const& jsonKey = jsonParser[Details::KeyPropertyName];
|
||||
{
|
||||
// key_ops
|
||||
@ -42,6 +53,11 @@ KeyVaultKey Details::KeyVaultKeyDeserialize(
|
||||
= Details::KeyTypeFromString(jsonKey[Details::KeyTypePropertyName].get<std::string>());
|
||||
|
||||
// "Attributes"
|
||||
{
|
||||
auto attributes = jsonParser[Details::AttributesPropertyName];
|
||||
key.Properties.CreatedOn
|
||||
= UnixTimeConverter::UnixTimeToDatetime(attributes["created"].get<uint64_t>());
|
||||
}
|
||||
|
||||
// "Tags"
|
||||
auto const& tags = jsonParser[Details::TagsPropertyName];
|
||||
@ -51,6 +67,4 @@ KeyVaultKey Details::KeyVaultKeyDeserialize(
|
||||
key.Properties.Tags.emplace(tag.key(), tag.value().get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "key_client_base_test.hpp"
|
||||
|
||||
#include <azure/keyvault/key_vault.hpp>
|
||||
#include <azure/keyvault/keys/key_constants.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -22,6 +23,26 @@ void CheckValidResponse(
|
||||
auto rawResponse = response.ExtractRawResponse();
|
||||
EXPECT_EQ(rawResponse->GetStatusCode(), expectedCode);
|
||||
}
|
||||
|
||||
std::string GetNotFoundErrorMsg(std::string const& keyName)
|
||||
{
|
||||
return "A key with (name/id) " + keyName
|
||||
+ " was not found in this key vault. If you recently deleted this key you may be able "
|
||||
"to recover it using the correct recovery command. For help resolving this issue, "
|
||||
"please see https://go.microsoft.com/fwlink/?linkid=2125182";
|
||||
}
|
||||
|
||||
std::string GetConflictErrorMsg(std::string const& keyName)
|
||||
{
|
||||
return "Key " + keyName
|
||||
+ " is currently in a deleted but recoverable state, and its name cannot be reused; in this "
|
||||
"state, the key can only be recovered or purged.";
|
||||
}
|
||||
|
||||
std::string GetConflictDeletingErrorMsg(std::string const& keyName)
|
||||
{
|
||||
return "Key " + keyName + " is currently being deleted and cannot be re-created; retry later.";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using namespace Azure::Security::KeyVault::Keys::Test;
|
||||
@ -121,3 +142,237 @@ TEST_F(KeyVaultClientTest, CreateKeyWithTags)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Test Key Delete.
|
||||
// The test works for either soft-delete or not, but for non soft-delete, the LRO is completed as
|
||||
// soon as the Operation returns.
|
||||
TEST_F(KeyVaultClientTest, DeleteKey)
|
||||
{
|
||||
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
|
||||
std::string keyName("deleteThisKey");
|
||||
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
CheckValidResponse(keyResponse);
|
||||
auto keyVaultKey = keyResponse.ExtractValue();
|
||||
EXPECT_EQ(keyVaultKey.Name(), keyName);
|
||||
}
|
||||
{
|
||||
// Setting a timeout context to avoid this test to run up to ctest default timeout
|
||||
// The polling operation would usually complete in ~20 seconds.
|
||||
// Setting 3 min as timeout just because I like number 3. We just want to prevent test running
|
||||
// for so long if something happens and no exception is thrown (paranoid scenario)
|
||||
auto duration = std::chrono::system_clock::now() + std::chrono::minutes(3);
|
||||
auto cancelToken = Azure::Core::GetApplicationContext().WithDeadline(duration);
|
||||
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
auto expectedStatusToken = m_keyVaultUrl + "/"
|
||||
+ std::string(Azure::Security::KeyVault::Keys::Details::DeletedKeysPath) + "/" + keyName;
|
||||
EXPECT_EQ(keyResponseLRO.GetResumeToken(), expectedStatusToken);
|
||||
// poll each second until key is soft-deleted
|
||||
// Will throw and fail test if test takes more than 3 minutes (token cancelled)
|
||||
auto keyResponse = keyResponseLRO.PollUntilDone(cancelToken, std::chrono::milliseconds(1000));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(KeyVaultClientTest, DeleteKeyOperationPoll)
|
||||
{
|
||||
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
|
||||
std::string keyName("deleteThisKeyPoll");
|
||||
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
CheckValidResponse(keyResponse);
|
||||
auto keyVaultKey = keyResponse.ExtractValue();
|
||||
EXPECT_EQ(keyVaultKey.Name(), keyName);
|
||||
}
|
||||
{
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
auto pollResponse = keyResponseLRO.Poll();
|
||||
// Expected not completed operation
|
||||
EXPECT_EQ(
|
||||
static_cast<typename std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
pollResponse->GetStatusCode()),
|
||||
404);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete key which doesn't exists
|
||||
TEST_F(KeyVaultClientTest, DeleteInvalidKey)
|
||||
{
|
||||
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
|
||||
std::string keyName("thisKeyDoesNotExists");
|
||||
|
||||
auto wasThrown = false;
|
||||
try
|
||||
{
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
}
|
||||
catch (Azure::Security::KeyVault::Common::KeyVaultException const& error)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
static_cast<typename std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
error.StatusCode),
|
||||
404);
|
||||
EXPECT_EQ(error.Message, GetNotFoundErrorMsg(keyName));
|
||||
EXPECT_EQ(error.ErrorCode, "KeyNotFound");
|
||||
wasThrown = true;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
EXPECT_TRUE(wasThrown);
|
||||
}
|
||||
|
||||
TEST_F(KeyVaultClientTest, DoubleDelete)
|
||||
{
|
||||
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
|
||||
std::string keyName("DeleteMeTwoTimes");
|
||||
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
}
|
||||
{
|
||||
auto duration = std::chrono::system_clock::now() + std::chrono::minutes(3);
|
||||
auto cancelToken = Azure::Core::GetApplicationContext().WithDeadline(duration);
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
auto keyResponse = keyResponseLRO.PollUntilDone(cancelToken, std::chrono::milliseconds(1000));
|
||||
}
|
||||
// delete same key again
|
||||
auto wasThrown = false;
|
||||
try
|
||||
{
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
}
|
||||
catch (Azure::Security::KeyVault::Common::KeyVaultException const& error)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
static_cast<typename std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
error.StatusCode),
|
||||
404);
|
||||
EXPECT_EQ(error.Message, GetNotFoundErrorMsg(keyName));
|
||||
EXPECT_EQ(error.ErrorCode, "KeyNotFound");
|
||||
wasThrown = true;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
EXPECT_TRUE(wasThrown);
|
||||
}
|
||||
|
||||
TEST_F(KeyVaultClientTest, DoubleDeleteBeforePollComplete)
|
||||
{
|
||||
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
|
||||
std::string keyName("DeleteMeBeforePollComplete1");
|
||||
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
}
|
||||
{
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
}
|
||||
// delete same key again before waitting for poll complete
|
||||
auto wasThrown = false;
|
||||
try
|
||||
{
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
}
|
||||
catch (Azure::Security::KeyVault::Common::KeyVaultException const& error)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
static_cast<typename std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
error.StatusCode),
|
||||
404);
|
||||
|
||||
EXPECT_EQ(error.Message, GetNotFoundErrorMsg(keyName));
|
||||
EXPECT_EQ(error.ErrorCode, "KeyNotFound");
|
||||
wasThrown = true;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
EXPECT_TRUE(wasThrown);
|
||||
}
|
||||
|
||||
TEST_F(KeyVaultClientTest, CreateDeletedKey)
|
||||
{
|
||||
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
|
||||
std::string keyName("YouCanCreateMeAfterYouDeletedMe");
|
||||
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
}
|
||||
{
|
||||
auto duration = std::chrono::system_clock::now() + std::chrono::minutes(3);
|
||||
auto cancelToken = Azure::Core::GetApplicationContext().WithDeadline(duration);
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
auto keyResponse = keyResponseLRO.PollUntilDone(cancelToken, std::chrono::milliseconds(1000));
|
||||
}
|
||||
// Create a key with same name
|
||||
auto wasThrown = false;
|
||||
try
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
}
|
||||
catch (Azure::Security::KeyVault::Common::KeyVaultException const& error)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
static_cast<typename std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
error.StatusCode),
|
||||
409);
|
||||
EXPECT_EQ(error.Message, GetConflictErrorMsg(keyName));
|
||||
EXPECT_EQ(error.ErrorCode, "Conflict");
|
||||
|
||||
wasThrown = true;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
EXPECT_TRUE(wasThrown);
|
||||
}
|
||||
|
||||
TEST_F(KeyVaultClientTest, CreateDeletedKeyBeforePollComplete)
|
||||
{
|
||||
Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential);
|
||||
std::string keyName("YouCanCreateMeAfterYouDeletedMeEvenBeforePollComplete");
|
||||
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
}
|
||||
{
|
||||
auto keyResponseLRO = keyClient.StartDeleteKey(keyName);
|
||||
}
|
||||
// Create a key with same name
|
||||
auto wasThrown = false;
|
||||
try
|
||||
{
|
||||
auto keyResponse
|
||||
= keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyTypeEnum::Ec);
|
||||
}
|
||||
catch (Azure::Security::KeyVault::Common::KeyVaultException const& error)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
static_cast<typename std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
error.StatusCode),
|
||||
409);
|
||||
EXPECT_EQ(error.Message, GetConflictDeletingErrorMsg(keyName));
|
||||
EXPECT_EQ(error.ErrorCode, "Conflict");
|
||||
wasThrown = true;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
EXPECT_TRUE(wasThrown);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user