diff --git a/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md b/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md index be452c3cb..6ee23a614 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/azure-security-keyvault-keys/CHANGELOG.md @@ -10,6 +10,10 @@ - Removed `Azure::Security::KeyVault::Keys::JsonWebKey::to_json`. +### Bug Fixes + +- Fix getting a resume token from delete and recover key operations. + ## 4.0.0-beta.1 (2021-04-07) ### New Features diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/delete_key_operation.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/delete_key_operation.hpp index 8d76a3c14..e59cd7760 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/delete_key_operation.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/delete_key_operation.hpp @@ -75,6 +75,14 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::shared_ptr keyvaultPipeline, Azure::Response response); + DeleteKeyOperation( + std::shared_ptr keyvaultPipeline, + std::string resumeToken) + : m_pipeline(keyvaultPipeline), m_value(DeletedKey(resumeToken)), + m_continuationToken(std::move(resumeToken)) + { + } + /** * @brief Get the #Azure::Core::Http::RawResponse of the operation request. * @return A reference to an #Azure::Core::Http::RawResponse. diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp index 468cb8ac7..5f113b64e 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp @@ -225,6 +225,18 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::string const& name, Azure::Core::Context const& context = Azure::Core::Context()) const; + /** + * @brief Construct a #DeletedKeyOperation from a resume token previously generated by calling + * #DeleteKeyOperation::GetResumeToken after a call to #StartDeleteKey(). + * + * @param resumeToken A generated token from a DeletedKeyOperation. + * @param context A cancellation token controlling the request lifetime. + * @return Azure::Security::KeyVault::Keys::DeleteKeyOperation + */ + Azure::Security::KeyVault::Keys::DeleteKeyOperation ResumeDeleteKey( + std::string const& resumeToken, + Azure::Core::Context const& context = Azure::Core::Context()) const; + /** * @brief Gets the public part of a deleted key. * @@ -293,6 +305,19 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::string const& name, Azure::Core::Context const& context = Azure::Core::Context()) const; + /** + * @brief Construct a #RecoverDeletedKeyOperation from a resume token previously generated by + * calling #RecoverDeletedKeyOperation::GetResumeToken after a call to + * #StartRecoverDeletedKey(). + * + * @param resumeToken A generated token from a StartRecoverDeletedKey. + * @param context A cancellation token controlling the request lifetime. + * @return Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation + */ + Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation ResumeRecoverDeletedKey( + std::string const& resumeToken, + Azure::Core::Context const& context = Azure::Core::Context()) const; + /** * @brief The update key operation changes specified attributes of a stored key and can be * applied to any key type and key version stored in Azure Key Vault. diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp index 9a32361a1..556a30b76 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/recover_deleted_key_operation.hpp @@ -71,6 +71,14 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::shared_ptr keyvaultPipeline, Azure::Response response); + RecoverDeletedKeyOperation( + std::shared_ptr keyvaultPipeline, + std::string resumeToken) + : m_pipeline(keyvaultPipeline), m_value(DeletedKey(resumeToken)), + m_continuationToken(std::move(resumeToken)) + { + } + /** * @brief Get the #Azure::Core::Http::RawResponse of the operation request. * @return A reference to an #Azure::Core::Http::RawResponse. diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp index e206a120a..244bb16c1 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp @@ -60,11 +60,8 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation::DeleteKeyOperation( m_value = response.Value; m_rawResponse = std::move(response.RawResponse); - // 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(_detail::DeletedKeysPath) - + "/" + m_value.Name(); + // The key name is enough to be used as continuation token. + m_continuationToken = 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). diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp index 1ec337770..17d9df199 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp @@ -202,6 +202,28 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation KeyClient::StartDeleteKey( {_detail::KeysPath, name})); } +Azure::Security::KeyVault::Keys::DeleteKeyOperation KeyClient::ResumeDeleteKey( + std::string const& resumeToken, + Azure::Core::Context const& context) const +{ + Azure::Security::KeyVault::Keys::DeleteKeyOperation operation(m_pipeline, resumeToken); + // Need to fix this to use context directly. See: + // https://github.com/Azure/azure-sdk-for-cpp/issues/2091 + operation.Poll(context.GetApplicationContext()); + return operation; +} + +Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation KeyClient::ResumeRecoverDeletedKey( + std::string const& resumeToken, + Azure::Core::Context const& context) const +{ + Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation operation(m_pipeline, resumeToken); + // Need to fix this to use context directly. See: + // https://github.com/Azure/azure-sdk-for-cpp/issues/2091 + operation.Poll(context.GetApplicationContext()); + return operation; +} + Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation KeyClient::StartRecoverDeletedKey( std::string const& name, Azure::Core::Context const& context) const diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp index 7c89b6ddd..5fa2216c4 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp @@ -62,9 +62,6 @@ Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation::RecoverDeletedKeyOp m_value = response.Value; m_rawResponse = std::move(response.RawResponse); - // 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(_detail::DeletedKeysPath) - + "/" + m_value.Name(); + // The key name is enough to resume the operation + m_continuationToken = m_value.Name(); } diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp index 2e24afca9..ca42fb469 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_delete_test_live.cpp @@ -62,8 +62,7 @@ TEST_F(KeyVaultClientTest, DeleteKey) 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 expectedStatusToken = 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) @@ -320,3 +319,84 @@ TEST_F(KeyVaultClientTest, GetDeletedKey) Azure::Security::KeyVault::Keys::KeyType::KeyTypeToString(deletedKey.Key.KeyType)); } } + +TEST_F(KeyVaultClientTest, DeleteOperationResumeToken) +{ + Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); + auto keyName = GetUniqueName(); + + { + auto keyResponse + = keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::JsonWebKeyType::Ec); + CheckValidResponse(keyResponse); + auto keyVaultKey = keyResponse.Value; + EXPECT_EQ(keyVaultKey.Name(), keyName); + } + std::string resumeToken; + { + auto keyResponseLRO = keyClient.StartDeleteKey(keyName); + resumeToken = keyResponseLRO.GetResumeToken(); + } + // Resume operation from token + { + auto resumeOperation = keyClient.ResumeDeleteKey(resumeToken); + resumeOperation.PollUntilDone(std::chrono::milliseconds(500)); + } + { + // recover + auto recoverOperation = keyClient.StartRecoverDeletedKey(keyName); + auto keyResponse = recoverOperation.PollUntilDone(std::chrono::milliseconds(500)); + auto key = keyResponse.Value; + // Delete again for purging + auto deleteOp = keyClient.StartDeleteKey(key.Name()); + deleteOp.PollUntilDone(std::chrono::milliseconds(200)); + } + { + // Purge + auto response = keyClient.PurgeDeletedKey(keyName); + CheckValidResponse(response, Azure::Core::Http::HttpStatusCode::NoContent); + } +} + +TEST_F(KeyVaultClientTest, RecoverOperationResumeToken) +{ + Azure::Security::KeyVault::Keys::KeyClient keyClient(m_keyVaultUrl, m_credential); + auto keyName = GetUniqueName(); + + { + auto keyResponse + = keyClient.CreateKey(keyName, Azure::Security::KeyVault::Keys::JsonWebKeyType::Ec); + CheckValidResponse(keyResponse); + auto keyVaultKey = keyResponse.Value; + EXPECT_EQ(keyVaultKey.Name(), keyName); + } + std::string resumeToken; + { + auto keyResponseLRO = keyClient.StartDeleteKey(keyName); + resumeToken = keyResponseLRO.GetResumeToken(); + } + // Resume operation from token + { + auto resumeOperation = keyClient.ResumeDeleteKey(resumeToken); + resumeOperation.PollUntilDone(std::chrono::milliseconds(500)); + } + { + // recover + auto recoverOperation = keyClient.StartRecoverDeletedKey(keyName); + resumeToken = recoverOperation.GetResumeToken(); + } + { + // resume from token + auto resumeRecoveryOp = keyClient.ResumeRecoverDeletedKey(resumeToken); + auto keyResponse = resumeRecoveryOp.PollUntilDone(std::chrono::milliseconds(500)); + auto key = keyResponse.Value; + // Delete again for purging + auto deleteOp = keyClient.StartDeleteKey(key.Name()); + deleteOp.PollUntilDone(std::chrono::milliseconds(200)); + } + { + // Purge + auto response = keyClient.PurgeDeletedKey(keyName); + CheckValidResponse(response, Azure::Core::Http::HttpStatusCode::NoContent); + } +}