Implement Phase 3 features for DefaultAzureCredential (#6724)

* Implement Phase 3 features for DefaultAzureCredential

* Forgotten change to update header

* Clang-format

* GCC fix

* Change 'envVarName' string parameter to a boolean 'requireEnvVarValue' parameter

* Rename EnvVarName to CredentialSpecifierEnvVarName and make it constexpr auto

* requireEnvVarValue => requireCredentialSpecifierEnvVarValue

* Clang-format

* Update unit test name

* Clang-format

* Update CHANGELOG with new features and bug fixes

---------

Co-authored-by: Anton Kolesnyk <antkmsft@users.noreply.github.com>
This commit is contained in:
Anton Kolesnyk 2025-09-10 13:34:22 -07:00 committed by GitHub
parent 573fe95a0e
commit 11a2a38aa7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 62 additions and 11 deletions

View File

@ -12,6 +12,12 @@ namespace Azure { namespace Core { namespace _internal {
~Environment() = delete;
public:
static std::string GetVariable(const std::string& name) { return GetVariable(name.c_str()); }
static void SetVariable(const std::string& name, const std::string& value)
{
SetVariable(name.c_str(), value.c_str());
}
static std::string GetVariable(const char* name);
static void SetVariable(const char* name, const char* value);
};

View File

@ -38,11 +38,13 @@ namespace Azure { namespace Core { namespace _internal {
return IsDigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
static constexpr bool IsAlphaNumeric(char c) noexcept
static constexpr bool IsAlpha(char c) noexcept
{
return IsDigit(c) || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
static constexpr bool IsAlphaNumeric(char c) noexcept { return IsDigit(c) || IsAlpha(c); }
static constexpr bool IsSpace(char c) noexcept { return c == ' ' || (c >= '\t' && c <= '\r'); }
static constexpr bool IsPrintable(char c) noexcept { return c >= ' ' && c <= '~'; }

View File

@ -2,8 +2,11 @@
## 1.13.1 (2025-09-11)
### Bugs Fixed
### Features Added
- Added a constructor overload for `DefaultAzureCredential` with a boolean parameter to indicate whether to throw an exception if `AZURE_TOKEN_CREDENTIALS` environment variable doesn't have a value.
### Bugs Fixed
- Fixed IMDS token requests for managed identities, which were broken by an invalid URL path in 1.12.0-beta.1. (A community contribution, courtesy of _[chewi](https://github.com/chewi)_)
### Acknowledgments

View File

@ -62,6 +62,19 @@ namespace Azure { namespace Identity {
*/
explicit DefaultAzureCredential(Core::Credentials::TokenCredentialOptions const& options);
/**
* @brief Constructs `%DefaultAzureCredential`.
*
* @param requireCredentialSpecifierEnvVarValue Throw an exception if `AZURE_TOKEN_CREDENTIALS`
* environment variable is not set.
*
* @param options Generic Token Credential Options.
*
*/
explicit DefaultAzureCredential(
bool requireCredentialSpecifierEnvVarValue,
Core::Credentials::TokenCredentialOptions const& options = {});
/**
* @brief Destructs `%DefaultAzureCredential`.
*

View File

@ -26,7 +26,18 @@ using Azure::Core::_internal::StringExtensions;
using Azure::Core::Diagnostics::Logger;
using Azure::Identity::_detail::IdentityLog;
namespace {
constexpr auto CredentialSpecifierEnvVarName = "AZURE_TOKEN_CREDENTIALS";
} // namespace
DefaultAzureCredential::DefaultAzureCredential(
Core::Credentials::TokenCredentialOptions const& options)
: DefaultAzureCredential(false, options)
{
}
DefaultAzureCredential::DefaultAzureCredential(
bool requireCredentialSpecifierEnvVarValue,
Core::Credentials::TokenCredentialOptions const& options)
: TokenCredential("DefaultAzureCredential")
{
@ -77,10 +88,16 @@ DefaultAzureCredential::DefaultAzureCredential(
[](auto options) { return std::make_shared<AzureCliCredential>(options); }},
};
constexpr auto envVarName = "AZURE_TOKEN_CREDENTIALS";
const auto envVarValue = Environment::GetVariable(envVarName);
const auto envVarValue = Environment::GetVariable(CredentialSpecifierEnvVarName);
const auto trimmedEnvVarValue = StringExtensions::Trim(envVarValue);
if (requireCredentialSpecifierEnvVarValue && trimmedEnvVarValue.empty())
{
throw AuthenticationException(
GetCredentialName() + ": '" + CredentialSpecifierEnvVarName
+ "' environment variable is empty.");
}
bool specificCred = false;
if (!trimmedEnvVarValue.empty())
{
@ -92,8 +109,8 @@ DefaultAzureCredential::DefaultAzureCredential(
specificCred = true;
IdentityLog::Write(
IdentityLog::Level::Verbose,
GetCredentialName() + ": '" + envVarName + "' environment variable is set to '"
+ envVarValue
GetCredentialName() + ": '" + CredentialSpecifierEnvVarName
+ "' environment variable is set to '" + envVarValue
+ "', therefore credential chain will only contain single credential: "
+ cred.CredentialName + '.');
credentialChain.emplace_back(cred.Create(options));
@ -143,7 +160,8 @@ DefaultAzureCredential::DefaultAzureCredential(
}
}
const auto logMsg = GetCredentialName() + ": '" + envVarName + "' environment variable is "
const auto logMsg = GetCredentialName() + ": '" + CredentialSpecifierEnvVarName
+ "' environment variable is "
+ (envVarValue.empty() ? "not set" : ("set to '" + envVarValue + "'"))
+ ((devCredCount > 0)
? (", therefore " + devCredNames + " will " + (isProd ? "NOT " : "")
@ -177,10 +195,13 @@ DefaultAzureCredential::DefaultAzureCredential(
}
throw AuthenticationException(
GetCredentialName() + ": Invalid value '" + envVarValue + "' for the '" + envVarName
GetCredentialName() + ": Invalid value '" + envVarValue + "' for the '"
+ CredentialSpecifierEnvVarName
+ "' environment variable. Allowed values are 'dev', 'prod'" + allowedCredNames
+ " (case insensitive). "
"It is also valid to not have the environment variable defined.");
+ " (case insensitive)."
+ (requireCredentialSpecifierEnvVarValue
? ""
: " It is also valid to not have the environment variable defined."));
}
}
}

View File

@ -522,3 +522,9 @@ TEST_P(LogMessagesForSpecificCredential, )
Logger::SetListener(nullptr);
}
TEST(DefaultAzureCredential, RequireCredentialSpecifierEnvVarValue)
{
EXPECT_THROW(
static_cast<void>(std::make_unique<DefaultAzureCredential>(true)), AuthenticationException);
}