Undocument ChainedCred usage by DefaultAzCred & remove friend and private ctor (#4447)

* Undocument ChainedCred usage by DefaultAzCred & remove friend and private ctor

* Clang warning fix

---------

Co-authored-by: Anton Kolesnyk <antkmsft@users.noreply.github.com>
This commit is contained in:
Anton Kolesnyk 2023-03-15 17:00:01 -07:00 committed by GitHub
parent 9f19a28af3
commit acb8e3b9e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 81 additions and 60 deletions

View File

@ -62,6 +62,7 @@ set(
set(
AZURE_IDENTITY_SOURCE
src/private/chained_token_credential_impl.hpp
src/private/managed_identity_source.hpp
src/private/package_version.hpp
src/private/token_credential_impl.hpp

View File

@ -58,7 +58,7 @@ The `DefaultAzureCredential` attempts to authenticate via the following mechanis
1. **Azure CLI** - If the developer has authenticated an account via the Azure CLI `az login` command, the `DefaultAzureCredential` will authenticate with that account.
1. **Managed Identity** - If the application is deployed to an Azure host with Managed Identity enabled, the `DefaultAzureCredential` will authenticate with that account.
`DefaultAzureCredential` uses [`ChainedTokenCredential`](#chained-token-credential) that consists of a chain of `EnvironmentCredential`, `AzureCliCredential`, and `ManagedIdentityCredential`. Implementation, including the order in which credentials are applied is documented, but it may change from release to release.
Even though the credentials being used and their order is documented, it may change from release to release.
`DefaultAzureCredential` intends to provide a credential that "just works out of the box and without requiring any information", if only the environment is set up sufficiently for the credential to work.
Therefore, it could be simple to use, but since it uses a chain of credentials, it could be a bit complicated to diagnose if the environment setup is not sufficient.

View File

@ -15,7 +15,9 @@
#include <vector>
namespace Azure { namespace Identity {
class DefaultAzureCredential;
namespace _detail {
class ChainedTokenCredentialImpl;
}
/**
* @brief Chained Token Credential provides a token credential implementation which chains
@ -24,10 +26,6 @@ namespace Azure { namespace Identity {
*
*/
class ChainedTokenCredential final : public Core::Credentials::TokenCredential {
// Friend declaration is needed for DefaultAzureCredential to access ChainedTokenCredential's
// private constructor built to be used specifically by it.
friend class DefaultAzureCredential;
public:
/**
* @brief A container type to store the ordered chain of credentials.
@ -62,10 +60,7 @@ namespace Azure { namespace Identity {
Core::Context const& context) const override;
private:
explicit ChainedTokenCredential(Sources sources, std::string const& enclosingCredential);
Sources m_sources;
std::string m_logPrefix;
std::unique_ptr<_detail::ChainedTokenCredentialImpl> m_impl;
};
}} // namespace Azure::Identity

View File

@ -8,13 +8,15 @@
#pragma once
#include <azure/core/credentials/credentials.hpp>
#include <azure/core/credentials/token_credential_options.hpp>
#include <azure/identity/chained_token_credential.hpp>
#include <memory>
namespace Azure { namespace Identity {
namespace _detail {
class ChainedTokenCredentialImpl;
}
/**
* @brief Default Azure Credential combines multiple credentials that depend on the setup
@ -22,7 +24,7 @@ namespace Azure { namespace Identity {
* sufficiently for at least one of such credentials to work, `DefaultAzureCredential` will work
* as well.
*
* @details This credential is using the #ChainedTokenCredential of 3 credentials in the order:
* @details This credential is using several credentials in the following order:
* #EnvironmentCredential, #AzureCliCredential, and #ManagedIdentityCredential. Even though the
* credentials being used and their order is documented, it may be changed in the future versions
* of the SDK, potentially bringing breaking changes in its behavior.
@ -68,7 +70,7 @@ namespace Azure { namespace Identity {
Core::Context const& context) const override;
private:
std::shared_ptr<ChainedTokenCredential> m_credentials;
std::unique_ptr<_detail::ChainedTokenCredentialImpl> m_impl;
};
}} // namespace Azure::Identity

View File

@ -2,36 +2,42 @@
// SPDX-License-Identifier: MIT
#include "azure/identity/chained_token_credential.hpp"
#include "azure/core/internal/diagnostics/log.hpp"
#include "private/chained_token_credential_impl.hpp"
#include <utility>
using namespace Azure::Identity;
using namespace Azure::Identity::_detail;
using namespace Azure::Core::Credentials;
using Azure::Core::Context;
using Azure::Core::Diagnostics::Logger;
using Azure::Core::Diagnostics::_internal::Log;
ChainedTokenCredential::ChainedTokenCredential(ChainedTokenCredential::Sources sources)
: TokenCredential("ChainedTokenCredential"),
m_impl(std::make_unique<ChainedTokenCredentialImpl>(GetCredentialName(), std::move(sources)))
{
}
ChainedTokenCredential::~ChainedTokenCredential() = default;
AccessToken ChainedTokenCredential::GetToken(
TokenRequestContext const& tokenRequestContext,
Context const& context) const
{
return m_impl->GetToken(GetCredentialName(), tokenRequestContext, context);
}
namespace {
constexpr auto IdentityPrefix = "Identity: ";
} // namespace
ChainedTokenCredential::ChainedTokenCredential(ChainedTokenCredential::Sources sources)
: ChainedTokenCredential(sources, {})
ChainedTokenCredentialImpl::ChainedTokenCredentialImpl(
std::string const& credentialName,
ChainedTokenCredential::Sources&& sources)
: m_sources(std::move(sources))
{
}
ChainedTokenCredential::ChainedTokenCredential(
ChainedTokenCredential::Sources sources,
std::string const& enclosingCredential)
: TokenCredential("ChainedTokenCredential"), m_sources(std::move(sources))
{
m_logPrefix = IdentityPrefix
+ (enclosingCredential.empty() ? GetCredentialName()
: (enclosingCredential + " -> " + GetCredentialName()))
+ ": ";
auto const logLevel = m_sources.empty() ? Logger::Level::Warning : Logger::Level::Informational;
if (Log::ShouldWrite(logLevel))
{
@ -54,19 +60,12 @@ ChainedTokenCredential::ChainedTokenCredential(
credSourceDetails += '.';
}
Log::Write(
logLevel,
IdentityPrefix
+ (enclosingCredential.empty()
? (GetCredentialName() + ": Created")
: (enclosingCredential + ": Created " + GetCredentialName()))
+ credSourceDetails);
Log::Write(logLevel, IdentityPrefix + credentialName + ": Created" + credSourceDetails);
}
}
ChainedTokenCredential::~ChainedTokenCredential() = default;
AccessToken ChainedTokenCredential::GetToken(
AccessToken ChainedTokenCredentialImpl::GetToken(
std::string const& credentialName,
TokenRequestContext const& tokenRequestContext,
Context const& context) const
{
@ -78,7 +77,9 @@ AccessToken ChainedTokenCredential::GetToken(
if (Log::ShouldWrite(logLevel))
{
Log::Write(
logLevel, m_logPrefix + "Authentication did not succeed: List of sources is empty.");
logLevel,
IdentityPrefix + credentialName
+ ": Authentication did not succeed: List of sources is empty.");
}
}
else
@ -95,8 +96,8 @@ AccessToken ChainedTokenCredential::GetToken(
{
Log::Write(
logLevel,
m_logPrefix + "Successfully got token from " + m_sources[i]->GetCredentialName()
+ '.');
IdentityPrefix + credentialName + ": Successfully got token from "
+ m_sources[i]->GetCredentialName() + '.');
}
}
@ -110,8 +111,8 @@ AccessToken ChainedTokenCredential::GetToken(
{
Log::Write(
logLevel,
m_logPrefix + "Failed to get token from " + m_sources[i]->GetCredentialName() + ": "
+ e.what());
IdentityPrefix + credentialName + ": Failed to get token from "
+ m_sources[i]->GetCredentialName() + ": " + e.what());
}
}
@ -122,12 +123,13 @@ AccessToken ChainedTokenCredential::GetToken(
{
Log::Write(
logLevel,
m_logPrefix + "Didn't succeed to get a token from any credential in the chain.");
IdentityPrefix + credentialName
+ ": Didn't succeed to get a token from any credential in the chain.");
}
}
}
}
}
throw AuthenticationException("Failed to get token from " + GetCredentialName() + '.');
throw AuthenticationException("Failed to get token from " + credentialName + '.');
}

View File

@ -6,6 +6,7 @@
#include "azure/identity/azure_cli_credential.hpp"
#include "azure/identity/environment_credential.hpp"
#include "azure/identity/managed_identity_credential.hpp"
#include "private/chained_token_credential_impl.hpp"
#include "azure/core/internal/diagnostics/log.hpp"
@ -31,8 +32,7 @@ DefaultAzureCredential::DefaultAzureCredential(TokenCredentialOptions const& opt
Log::Write(
logLevel,
std::string(IdentityPrefix) + "Creating " + GetCredentialName()
+ " which combines mutiple parameterless credentials "
"into a single one (by using ChainedTokenCredential).\n"
+ " which combines mutiple parameterless credentials into a single one.\n"
+ GetCredentialName()
+ " is only recommended for the early stages of development, "
"and not for usage in production environment."
@ -48,10 +48,9 @@ DefaultAzureCredential::DefaultAzureCredential(TokenCredentialOptions const& opt
auto const azCliCred = std::make_shared<AzureCliCredential>(options);
auto const managedIdentityCred = std::make_shared<ManagedIdentityCredential>(options);
// Using the ChainedTokenCredential's private constructor for more detailed log messages.
m_credentials.reset(new ChainedTokenCredential(
ChainedTokenCredential::Sources{envCred, azCliCred, managedIdentityCred},
GetCredentialName())); // extra arg for the ChainedTokenCredential's private constructor.
m_impl = std::make_unique<_detail::ChainedTokenCredentialImpl>(
GetCredentialName(),
ChainedTokenCredential::Sources{envCred, azCliCred, managedIdentityCred});
}
DefaultAzureCredential::~DefaultAzureCredential() = default;
@ -62,7 +61,7 @@ AccessToken DefaultAzureCredential::GetToken(
{
try
{
return m_credentials->GetToken(tokenRequestContext, context);
return m_impl->GetToken(GetCredentialName(), tokenRequestContext, context);
}
catch (AuthenticationException const&)
{

View File

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include "azure/identity/chained_token_credential.hpp"
namespace Azure { namespace Identity { namespace _detail {
class ChainedTokenCredentialImpl final {
public:
ChainedTokenCredentialImpl(
std::string const& credentialName,
ChainedTokenCredential::Sources&& sources);
Core::Credentials::AccessToken GetToken(
std::string const& credentialName,
Core::Credentials::TokenRequestContext const& tokenRequestContext,
Core::Context const& context) const;
private:
ChainedTokenCredential::Sources m_sources;
};
}}} // namespace Azure::Identity::_detail

View File

@ -74,9 +74,8 @@ TEST(DefaultAzureCredential, LogMessages)
EXPECT_EQ(log[0].first, Logger::Level::Verbose);
EXPECT_EQ(
log[0].second,
"Identity: Creating DefaultAzureCredential which combines mutiple parameterless "
"credentials "
"into a single one (by using ChainedTokenCredential)."
"Identity: Creating DefaultAzureCredential which combines "
"mutiple parameterless credentials into a single one."
"\nDefaultAzureCredential is only recommended for the early stages of development, "
"and not for usage in production environment."
"\nOnce the developer focuses on the Credentials and Authentication aspects of their "
@ -131,8 +130,7 @@ TEST(DefaultAzureCredential, LogMessages)
EXPECT_EQ(log[8].first, Logger::Level::Informational);
EXPECT_EQ(
log[8].second,
"Identity: DefaultAzureCredential: Created ChainedTokenCredential "
"with the following credentials: "
"Identity: DefaultAzureCredential: Created with the following credentials: "
"EnvironmentCredential, AzureCliCredential, ManagedIdentityCredential.");
log.clear();
@ -149,8 +147,7 @@ TEST(DefaultAzureCredential, LogMessages)
EXPECT_EQ(log[3].first, Logger::Level::Informational);
EXPECT_EQ(
log[3].second,
"Identity: DefaultAzureCredential -> ChainedTokenCredential: "
"Successfully got token from EnvironmentCredential.");
"Identity: DefaultAzureCredential: Successfully got token from EnvironmentCredential.");
Logger::SetListener(nullptr);
}