Identity: Log Client ID used in ManagedIdentityCredential (#6426)

* Identity: Log Client ID used in ManagedIdentityCredential

* Clang-format

* [&]

---------

Co-authored-by: Anton Kolesnyk <antkmsft@users.noreply.github.com>
This commit is contained in:
Anton Kolesnyk 2025-02-24 12:27:33 -08:00 committed by GitHub
parent 8d21d5d40d
commit 237e617a4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 165 additions and 21 deletions

View File

@ -5,6 +5,7 @@
### Features Added
- Added `Subscription` to `AzureCliCredentialOptions` which allows the caller to specify an Azure subscription that does not match the current Azure CLI subscription.
- [[#6321]](https://github.com/Azure/azure-sdk-for-cpp/issues/6321) Log Client ID used in `ManagedIdentityCredential`.
### Breaking Changes

View File

@ -28,9 +28,15 @@ namespace {
// host.
std::string const ImdsEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token";
std::string WithSourceMessage(std::string const& credSource)
std::string WithSourceAndClientIdMessage(std::string const& credSource, std::string const& clientId)
{
return " with " + credSource + " source";
std::string result = " with " + credSource + " source";
if (!clientId.empty())
{
result += " and Client ID '" + clientId + '\'';
}
return result;
}
void PrintEnvNotSetUpMessage(std::string const& credName, std::string const& credSource)
@ -38,7 +44,7 @@ void PrintEnvNotSetUpMessage(std::string const& credName, std::string const& cre
IdentityLog::Write(
IdentityLog::Level::Verbose,
credName + ": Environment is not set up for the credential to be created"
+ WithSourceMessage(credSource) + '.');
+ WithSourceAndClientIdMessage(credSource, {}) + '.');
}
// ExpectedArcKeyDirectory returns the directory expected to contain Azure Arc keys.
@ -113,7 +119,8 @@ Azure::Core::Url ManagedIdentitySource::ParseEndpointUrl(
std::string const& credName,
std::string const& url,
char const* envVarName,
std::string const& credSource)
std::string const& credSource,
std::string const& clientId)
{
using Azure::Core::Url;
using Azure::Core::Credentials::AuthenticationException;
@ -124,7 +131,7 @@ Azure::Core::Url ManagedIdentitySource::ParseEndpointUrl(
IdentityLog::Write(
IdentityLog::Level::Informational,
credName + " will be created" + WithSourceMessage(credSource) + '.');
credName + " will be created" + WithSourceAndClientIdMessage(credSource, clientId) + '.');
return endpointUrl;
}
@ -135,7 +142,7 @@ Azure::Core::Url ManagedIdentitySource::ParseEndpointUrl(
{
}
auto const errorMessage = credName + WithSourceMessage(credSource)
auto const errorMessage = credName + WithSourceAndClientIdMessage(credSource, {})
+ ": Failed to create: The environment variable \'" + envVarName
+ "\' contains an invalid URL.";
@ -166,7 +173,7 @@ std::unique_ptr<ManagedIdentitySource> AppServiceManagedIdentitySource::Create(
objectId,
resourceId,
options,
ParseEndpointUrl(credName, msiEndpoint, endpointVarName, credSource),
ParseEndpointUrl(credName, msiEndpoint, endpointVarName, credSource, clientId),
msiSecret));
}
@ -287,7 +294,7 @@ std::unique_ptr<ManagedIdentitySource> CloudShellManagedIdentitySource::Create(
constexpr auto EndpointVarName = "MSI_ENDPOINT";
auto msiEndpoint = Environment::GetVariable(EndpointVarName);
std::string const CredSource = "Cloud Shell";
std::string const credSource = "Cloud Shell";
if (!msiEndpoint.empty())
{
@ -299,10 +306,12 @@ std::unique_ptr<ManagedIdentitySource> CloudShellManagedIdentitySource::Create(
}
return std::unique_ptr<ManagedIdentitySource>(new CloudShellManagedIdentitySource(
clientId, options, ParseEndpointUrl(credName, msiEndpoint, EndpointVarName, CredSource)));
clientId,
options,
ParseEndpointUrl(credName, msiEndpoint, EndpointVarName, credSource, clientId)));
}
PrintEnvNotSetUpMessage(credName, CredSource);
PrintEnvNotSetUpMessage(credName, credSource);
return nullptr;
}
@ -380,7 +389,8 @@ std::unique_ptr<ManagedIdentitySource> AzureArcManagedIdentitySource::Create(
}
return std::unique_ptr<ManagedIdentitySource>(new AzureArcManagedIdentitySource(
options, ParseEndpointUrl(credName, identityEndpoint, EndpointVarName, credSource)));
options,
ParseEndpointUrl(credName, identityEndpoint, EndpointVarName, credSource, clientId)));
}
AzureArcManagedIdentitySource::AzureArcManagedIdentitySource(
@ -488,7 +498,8 @@ std::unique_ptr<ManagedIdentitySource> ImdsManagedIdentitySource::Create(
{
IdentityLog::Write(
IdentityLog::Level::Informational,
credName + " will be created" + WithSourceMessage("Azure Instance Metadata Service")
credName + " will be created"
+ WithSourceAndClientIdMessage("Azure Instance Metadata Service", clientId)
+ ".\nSuccessful creation does not guarantee further successful token retrieval.");
return std::unique_ptr<ManagedIdentitySource>(

View File

@ -32,7 +32,8 @@ namespace Azure { namespace Identity { namespace _detail {
std::string const& credName,
std::string const& url,
char const* envVarName,
std::string const& credSource);
std::string const& credSource,
std::string const& clientId);
explicit ManagedIdentitySource(
std::string clientId,

View File

@ -238,8 +238,14 @@ namespace Azure { namespace Identity { namespace Test {
TEST(ManagedIdentityCredential, AppServiceV2019ClientId)
{
using Azure::Core::Diagnostics::Logger;
using LogMsgVec = std::vector<std::pair<Logger::Level, std::string>>;
LogMsgVec log;
Logger::SetLevel(Logger::Level::Verbose);
Logger::SetListener([&](auto lvl, auto msg) { log.push_back(std::make_pair(lvl, msg)); });
auto const actual = CredentialTestHelper::SimulateTokenRequest(
[](auto transport) {
[&](auto transport) {
TokenCredentialOptions options;
options.Transport.Transport = transport;
@ -252,8 +258,19 @@ namespace Azure { namespace Identity { namespace Test {
{"IDENTITY_SERVER_THUMBPRINT", "0123456789abcdef0123456789abcdef01234567"},
});
return std::make_unique<ManagedIdentityCredential>(
auto credential = std::make_unique<ManagedIdentityCredential>(
"fedcba98-7654-3210-0123-456789abcdef", options);
EXPECT_EQ(log.size(), LogMsgVec::size_type(1));
EXPECT_EQ(log[0].first, Logger::Level::Informational);
EXPECT_EQ(
log[0].second,
"Identity: ManagedIdentityCredential will be created with App Service 2019 source"
" and Client ID 'fedcba98-7654-3210-0123-456789abcdef'.");
log.clear();
return credential;
},
{{"https://azure.com/.default"}, {"https://outlook.com/.default"}, {}},
std::vector<std::string>{
@ -324,6 +341,8 @@ namespace Azure { namespace Identity { namespace Test {
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
Logger::SetListener(nullptr);
}
TEST(ManagedIdentityCredential, AppServiceV2019ResourceId)
@ -699,8 +718,14 @@ namespace Azure { namespace Identity { namespace Test {
TEST(ManagedIdentityCredential, AppServiceV2017ClientId)
{
using Azure::Core::Diagnostics::Logger;
using LogMsgVec = std::vector<std::pair<Logger::Level, std::string>>;
LogMsgVec log;
Logger::SetLevel(Logger::Level::Verbose);
Logger::SetListener([&](auto lvl, auto msg) { log.push_back(std::make_pair(lvl, msg)); });
auto const actual = CredentialTestHelper::SimulateTokenRequest(
[](auto transport) {
[&](auto transport) {
TokenCredentialOptions options;
options.Transport.Transport = transport;
@ -713,8 +738,26 @@ namespace Azure { namespace Identity { namespace Test {
{"IDENTITY_SERVER_THUMBPRINT", "0123456789abcdef0123456789abcdef01234567"},
});
return std::make_unique<ManagedIdentityCredential>(
auto credential = std::make_unique<ManagedIdentityCredential>(
"fedcba98-7654-3210-0123-456789abcdef", options);
EXPECT_EQ(log.size(), LogMsgVec::size_type(2));
EXPECT_EQ(log[0].first, Logger::Level::Verbose);
EXPECT_EQ(
log[0].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with App Service 2019 source.");
EXPECT_EQ(log[1].first, Logger::Level::Informational);
EXPECT_EQ(
log[1].second,
"Identity: ManagedIdentityCredential will be created with App Service 2017 source"
" and Client ID 'fedcba98-7654-3210-0123-456789abcdef'.");
log.clear();
return credential;
},
{{"https://azure.com/.default"}, {"https://outlook.com/.default"}, {}},
std::vector<std::string>{
@ -785,6 +828,8 @@ namespace Azure { namespace Identity { namespace Test {
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
Logger::SetListener(nullptr);
}
TEST(ManagedIdentityCredential, AppServiceV2017ResourceId)
@ -2455,9 +2500,15 @@ namespace Azure { namespace Identity { namespace Test {
TEST(ManagedIdentityCredential, ImdsClientId)
{
using Azure::Core::Diagnostics::Logger;
using LogMsgVec = std::vector<std::pair<Logger::Level, std::string>>;
LogMsgVec log;
Logger::SetLevel(Logger::Level::Verbose);
Logger::SetListener([&](auto lvl, auto msg) { log.push_back(std::make_pair(lvl, msg)); });
{
auto const actual = CredentialTestHelper::SimulateTokenRequest(
[](auto transport) {
[&](auto transport) {
TokenCredentialOptions options;
options.Transport.Transport = transport;
@ -2470,8 +2521,46 @@ namespace Azure { namespace Identity { namespace Test {
{"IDENTITY_SERVER_THUMBPRINT", ""},
});
return std::make_unique<ManagedIdentityCredential>(
auto credential = std::make_unique<ManagedIdentityCredential>(
"fedcba98-7654-3210-0123-456789abcdef", options);
EXPECT_EQ(log.size(), LogMsgVec::size_type(5));
EXPECT_EQ(log[0].first, Logger::Level::Verbose);
EXPECT_EQ(
log[0].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with App Service 2019 source.");
EXPECT_EQ(log[1].first, Logger::Level::Verbose);
EXPECT_EQ(
log[1].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with App Service 2017 source.");
EXPECT_EQ(log[2].first, Logger::Level::Verbose);
EXPECT_EQ(
log[2].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with Cloud Shell source.");
EXPECT_EQ(log[3].first, Logger::Level::Verbose);
EXPECT_EQ(
log[3].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with Azure Arc source.");
EXPECT_EQ(log[4].first, Logger::Level::Informational);
EXPECT_EQ(
log[4].second,
"Identity: ManagedIdentityCredential will be created "
"with Azure Instance Metadata Service source"
" and Client ID 'fedcba98-7654-3210-0123-456789abcdef'."
"\nSuccessful creation does not guarantee further successful token retrieval.");
log.clear();
return credential;
},
{{"https://azure.com/.default"}, {"https://outlook.com/.default"}, {}},
std::vector<std::string>{
@ -2545,7 +2634,7 @@ namespace Azure { namespace Identity { namespace Test {
}
{
auto const actual = CredentialTestHelper::SimulateTokenRequest(
[](auto transport) {
[&](auto transport) {
ManagedIdentityCredentialOptions options;
options.Transport.Transport = transport;
options.IdentityId = ManagedIdentityId::FromUserAssignedClientId(
@ -2560,7 +2649,47 @@ namespace Azure { namespace Identity { namespace Test {
{"IDENTITY_SERVER_THUMBPRINT", ""},
});
return std::make_unique<ManagedIdentityCredential>(options);
log.clear();
auto credential = std::make_unique<ManagedIdentityCredential>(options);
EXPECT_EQ(log.size(), LogMsgVec::size_type(5));
EXPECT_EQ(log[0].first, Logger::Level::Verbose);
EXPECT_EQ(
log[0].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with App Service 2019 source.");
EXPECT_EQ(log[1].first, Logger::Level::Verbose);
EXPECT_EQ(
log[1].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with App Service 2017 source.");
EXPECT_EQ(log[2].first, Logger::Level::Verbose);
EXPECT_EQ(
log[2].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with Cloud Shell source.");
EXPECT_EQ(log[3].first, Logger::Level::Verbose);
EXPECT_EQ(
log[3].second,
"Identity: ManagedIdentityCredential: Environment is not set up for the credential "
"to be created with Azure Arc source.");
EXPECT_EQ(log[4].first, Logger::Level::Informational);
EXPECT_EQ(
log[4].second,
"Identity: ManagedIdentityCredential will be created "
"with Azure Instance Metadata Service source"
" and Client ID 'fedcba98-7654-3210-0123-456789abcdef'."
"\nSuccessful creation does not guarantee further successful token retrieval.");
log.clear();
return credential;
},
{{"https://azure.com/.default"}, {"https://outlook.com/.default"}, {}},
std::vector<std::string>{
@ -2632,6 +2761,8 @@ namespace Azure { namespace Identity { namespace Test {
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
}
Logger::SetListener(nullptr);
}
TEST(ManagedIdentityCredential, ImdsResourceId)