Enable proactive renewal of Managed Identity tokens. (#5336)
* Enable proactive renewal of Managed Identity tokens. * Address PR feedback - move helpers to anonymous namespace and renames. * Address local variable rename suggestion.
This commit is contained in:
parent
f348242546
commit
3b231e5850
@ -4,6 +4,8 @@
|
||||
|
||||
### Features Added
|
||||
|
||||
- [[#4474]](https://github.com/Azure/azure-sdk-for-cpp/issues/4474) Enable proactive renewal of Managed Identity tokens.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
@ -191,6 +191,8 @@ AccessToken AzureCliCredential::GetToken(
|
||||
"accessToken",
|
||||
"expiresIn",
|
||||
std::vector<std::string>{"expires_on", "expiresOn"},
|
||||
"",
|
||||
false,
|
||||
GetLocalTimeToUtcDiffSeconds());
|
||||
}
|
||||
catch (json::exception const&)
|
||||
|
||||
@ -503,7 +503,7 @@ AccessToken ClientCertificateCredential::GetToken(
|
||||
// call it later. Therefore, any capture made here will outlive the possible time frame when the
|
||||
// lambda might get called.
|
||||
return m_tokenCache.GetToken(scopesStr, tenantId, tokenRequestContext.MinimumExpiration, [&]() {
|
||||
return m_tokenCredentialImpl->GetToken(context, [&]() {
|
||||
return m_tokenCredentialImpl->GetToken(context, false, [&]() {
|
||||
auto body = m_requestBody;
|
||||
if (!scopesStr.empty())
|
||||
{
|
||||
|
||||
@ -83,7 +83,7 @@ AccessToken ClientSecretCredential::GetToken(
|
||||
// call it later. Therefore, any capture made here will outlive the possible time frame when the
|
||||
// lambda might get called.
|
||||
return m_tokenCache.GetToken(scopesStr, tenantId, tokenRequestContext.MinimumExpiration, [&]() {
|
||||
return m_tokenCredentialImpl->GetToken(context, [&]() {
|
||||
return m_tokenCredentialImpl->GetToken(context, false, [&]() {
|
||||
auto body = m_requestBody;
|
||||
|
||||
if (!scopesStr.empty())
|
||||
|
||||
@ -137,7 +137,7 @@ Azure::Core::Credentials::AccessToken AppServiceManagedIdentitySource::GetToken(
|
||||
// call it later. Therefore, any capture made here will outlive the possible time frame when the
|
||||
// lambda might get called.
|
||||
return m_tokenCache.GetToken(scopesStr, {}, tokenRequestContext.MinimumExpiration, [&]() {
|
||||
return TokenCredentialImpl::GetToken(context, [&]() {
|
||||
return TokenCredentialImpl::GetToken(context, true, [&]() {
|
||||
auto request = std::make_unique<TokenRequest>(m_request);
|
||||
|
||||
if (!scopesStr.empty())
|
||||
@ -219,7 +219,7 @@ Azure::Core::Credentials::AccessToken CloudShellManagedIdentitySource::GetToken(
|
||||
// call it later. Therefore, any capture made here will outlive the possible time frame when the
|
||||
// lambda might get called.
|
||||
return m_tokenCache.GetToken(scopesStr, {}, tokenRequestContext.MinimumExpiration, [&]() {
|
||||
return TokenCredentialImpl::GetToken(context, [&]() {
|
||||
return TokenCredentialImpl::GetToken(context, true, [&]() {
|
||||
using Azure::Core::Url;
|
||||
using Azure::Core::Http::HttpMethod;
|
||||
|
||||
@ -320,6 +320,7 @@ Azure::Core::Credentials::AccessToken AzureArcManagedIdentitySource::GetToken(
|
||||
return m_tokenCache.GetToken(scopesStr, {}, tokenRequestContext.MinimumExpiration, [&]() {
|
||||
return TokenCredentialImpl::GetToken(
|
||||
context,
|
||||
true,
|
||||
createRequest,
|
||||
[&](auto const statusCode, auto const& response) -> std::unique_ptr<TokenRequest> {
|
||||
using Core::Credentials::AuthenticationException;
|
||||
@ -418,7 +419,7 @@ Azure::Core::Credentials::AccessToken ImdsManagedIdentitySource::GetToken(
|
||||
// call it later. Therefore, any capture made here will outlive the possible time frame when the
|
||||
// lambda might get called.
|
||||
return m_tokenCache.GetToken(scopesStr, {}, tokenRequestContext.MinimumExpiration, [&]() {
|
||||
return TokenCredentialImpl::GetToken(context, [&]() {
|
||||
return TokenCredentialImpl::GetToken(context, true, [&]() {
|
||||
auto request = std::make_unique<TokenRequest>(m_request);
|
||||
|
||||
if (!scopesStr.empty())
|
||||
|
||||
@ -70,6 +70,10 @@ namespace Azure { namespace Identity { namespace _detail {
|
||||
* @param expiresOnPropertyNames Names of properties in the JSON object that represent token
|
||||
* expiration as absolute date-time stamp. Can be empty, in which case no attempt to parse the
|
||||
* corresponding property will be made. Empty string elements will be ignored.
|
||||
* @param refreshInPropertyName Name of a property in the JSON object that represents when to
|
||||
* refresh the token in number of seconds from now.
|
||||
* @param proactiveRenewal A value to indicate whether to refresh tokens, proactively, with half
|
||||
* lifetime or not.
|
||||
* @param utcDiffSeconds Optional. If not 0, it represents the difference between the UTC and a
|
||||
* desired time zone, in seconds. Then, should an RFC3339 timestamp come without a time zone
|
||||
* information, a corresponding time zone offset will be applied to such timestamp.
|
||||
@ -88,6 +92,8 @@ namespace Azure { namespace Identity { namespace _detail {
|
||||
std::string const& accessTokenPropertyName,
|
||||
std::string const& expiresInPropertyName,
|
||||
std::vector<std::string> const& expiresOnPropertyNames,
|
||||
std::string const& refreshInPropertyName = "",
|
||||
bool proactiveRenewal = false,
|
||||
int utcDiffSeconds = 0);
|
||||
|
||||
/**
|
||||
@ -101,6 +107,10 @@ namespace Azure { namespace Identity { namespace _detail {
|
||||
* @param expiresOnPropertyName Name of a property in the JSON object that represents token
|
||||
* expiration as absolute date-time stamp. Can be empty, in which case no attempt to parse it is
|
||||
* made.
|
||||
* @param refreshInPropertyName Name of a property in the JSON object that represents
|
||||
* when to refresh the token in number of seconds from now.
|
||||
* @param proactiveRenewal A value to indicate whether to refresh tokens, proactively, with half
|
||||
* lifetime or not.
|
||||
*
|
||||
* @return A successfully parsed access token.
|
||||
*
|
||||
@ -110,13 +120,17 @@ namespace Azure { namespace Identity { namespace _detail {
|
||||
std::string const& jsonString,
|
||||
std::string const& accessTokenPropertyName,
|
||||
std::string const& expiresInPropertyName,
|
||||
std::string const& expiresOnPropertyName)
|
||||
std::string const& expiresOnPropertyName,
|
||||
std::string const& refreshInPropertyName = "",
|
||||
bool proactiveRenewal = false)
|
||||
{
|
||||
return ParseToken(
|
||||
jsonString,
|
||||
accessTokenPropertyName,
|
||||
expiresInPropertyName,
|
||||
std::vector<std::string>{expiresOnPropertyName});
|
||||
std::vector<std::string>{expiresOnPropertyName},
|
||||
refreshInPropertyName,
|
||||
proactiveRenewal);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,6 +183,8 @@ namespace Azure { namespace Identity { namespace _detail {
|
||||
* @brief Gets an authentication token.
|
||||
*
|
||||
* @param context A context to control the request lifetime.
|
||||
* @param proactiveRenewal A value to indicate whether to refresh tokens, proactively, with half
|
||||
* lifetime or not.
|
||||
* @param createRequest A function to create a token request.
|
||||
* @param shouldRetry A function to determine whether a response should be retried with
|
||||
* another request.
|
||||
@ -177,6 +193,7 @@ namespace Azure { namespace Identity { namespace _detail {
|
||||
*/
|
||||
Core::Credentials::AccessToken GetToken(
|
||||
Core::Context const& context,
|
||||
bool proactiveRenewal,
|
||||
std::function<std::unique_ptr<TokenRequest>()> const& createRequest,
|
||||
std::function<std::unique_ptr<TokenRequest>(
|
||||
Core::Http::HttpStatusCode statusCode,
|
||||
|
||||
@ -35,6 +35,8 @@ using Azure::Core::Http::HttpStatusCode;
|
||||
using Azure::Core::Http::RawResponse;
|
||||
using Azure::Core::Json::_internal::json;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
TokenCredentialImpl::TokenCredentialImpl(TokenCredentialOptions const& options)
|
||||
: m_httpPipeline(options, "identity", PackageVersion::ToString(), {}, {})
|
||||
{
|
||||
@ -91,6 +93,7 @@ std::string TokenCredentialImpl::FormatScopes(
|
||||
|
||||
AccessToken TokenCredentialImpl::GetToken(
|
||||
Context const& context,
|
||||
bool proactiveRenewal,
|
||||
std::function<std::unique_ptr<TokenCredentialImpl::TokenRequest>()> const& createRequest,
|
||||
std::function<std::unique_ptr<TokenCredentialImpl::TokenRequest>(
|
||||
HttpStatusCode statusCode,
|
||||
@ -140,7 +143,9 @@ AccessToken TokenCredentialImpl::GetToken(
|
||||
std::string(responseBodyVector.begin(), responseBodyVector.end()),
|
||||
"access_token",
|
||||
"expires_in",
|
||||
"expires_on");
|
||||
"expires_on",
|
||||
"refresh_in",
|
||||
proactiveRenewal);
|
||||
}
|
||||
catch (AuthenticationException const&)
|
||||
{
|
||||
@ -224,6 +229,30 @@ std::string TimeZoneOffsetAsString(int utcDiffSeconds)
|
||||
return os.str();
|
||||
}
|
||||
|
||||
// Proactive renewal by cutting the refresh time in half if the token expires in more than
|
||||
// 2 hours.
|
||||
std::chrono::seconds GetProactiveRenewalSeconds(std::chrono::seconds seconds)
|
||||
{
|
||||
if (seconds >= std::chrono::seconds(2h))
|
||||
{
|
||||
return seconds / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return seconds;
|
||||
}
|
||||
}
|
||||
|
||||
DateTime GetProactiveRenewalDateTime(std::int64_t posixTimestamp)
|
||||
{
|
||||
const DateTime now = DateTime::clock::now();
|
||||
|
||||
const auto renewInSeconds = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
PosixTimeConverter::PosixTimeToDateTime(posixTimestamp) - now);
|
||||
|
||||
return DateTime(now + GetProactiveRenewalSeconds(renewInSeconds));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AccessToken TokenCredentialImpl::ParseToken(
|
||||
@ -231,6 +260,8 @@ AccessToken TokenCredentialImpl::ParseToken(
|
||||
std::string const& accessTokenPropertyName,
|
||||
std::string const& expiresInPropertyName,
|
||||
std::vector<std::string> const& expiresOnPropertyNames,
|
||||
std::string const& refreshInPropertyName,
|
||||
bool proactiveRenewal,
|
||||
int utcDiffSeconds)
|
||||
{
|
||||
json parsedJson;
|
||||
@ -262,6 +293,35 @@ AccessToken TokenCredentialImpl::ParseToken(
|
||||
accessToken.Token = parsedJson[accessTokenPropertyName].get<std::string>();
|
||||
accessToken.ExpiresOn = std::chrono::system_clock::now();
|
||||
|
||||
// expiresIn = number of seconds until refresh.
|
||||
// expiresOn = timestamp of refresh expressed as seconds since epoch.
|
||||
|
||||
if (!refreshInPropertyName.empty() && parsedJson.contains(refreshInPropertyName))
|
||||
{
|
||||
auto const& refreshIn = parsedJson[refreshInPropertyName];
|
||||
if (refreshIn.is_number_unsigned())
|
||||
{
|
||||
try
|
||||
{
|
||||
// 'refresh_in' as number (seconds until refresh)
|
||||
auto const value = refreshIn.get<std::int64_t>();
|
||||
if (value <= MaxExpirationInSeconds)
|
||||
{
|
||||
static_assert(
|
||||
MaxExpirationInSeconds <= std::numeric_limits<std::int32_t>::max(),
|
||||
"Can safely cast to int32");
|
||||
|
||||
accessToken.ExpiresOn += std::chrono::seconds(static_cast<std::int32_t>(value));
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
// refreshIn.get<std::int64_t>() has thrown, we may throw later.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedJson.contains(expiresInPropertyName))
|
||||
{
|
||||
auto const& expiresIn = parsedJson[expiresInPropertyName];
|
||||
@ -278,7 +338,9 @@ AccessToken TokenCredentialImpl::ParseToken(
|
||||
MaxExpirationInSeconds <= std::numeric_limits<std::int32_t>::max(),
|
||||
"Can safely cast to int32");
|
||||
|
||||
accessToken.ExpiresOn += std::chrono::seconds(static_cast<std::int32_t>(value));
|
||||
auto expiresInSeconds = std::chrono::seconds(static_cast<std::int32_t>(value));
|
||||
accessToken.ExpiresOn
|
||||
+= proactiveRenewal ? GetProactiveRenewalSeconds(expiresInSeconds) : expiresInSeconds;
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
@ -297,8 +359,10 @@ AccessToken TokenCredentialImpl::ParseToken(
|
||||
MaxExpirationInSeconds <= std::numeric_limits<std::int32_t>::max(),
|
||||
"Can safely cast to int32");
|
||||
|
||||
accessToken.ExpiresOn += std::chrono::seconds(static_cast<std::int32_t>(
|
||||
auto expiresInSeconds = std::chrono::seconds(static_cast<std::int32_t>(
|
||||
ParseNumericExpiration(expiresIn.get<std::string>(), MaxExpirationInSeconds)));
|
||||
accessToken.ExpiresOn
|
||||
+= proactiveRenewal ? GetProactiveRenewalSeconds(expiresInSeconds) : expiresInSeconds;
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
@ -342,7 +406,9 @@ AccessToken TokenCredentialImpl::ParseToken(
|
||||
auto const value = expiresOn.get<std::int64_t>();
|
||||
if (value <= MaxPosixTimestamp)
|
||||
{
|
||||
accessToken.ExpiresOn = PosixTimeConverter::PosixTimeToDateTime(value);
|
||||
accessToken.ExpiresOn = proactiveRenewal
|
||||
? GetProactiveRenewalDateTime(value)
|
||||
: PosixTimeConverter::PosixTimeToDateTime(value);
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
@ -359,16 +425,23 @@ AccessToken TokenCredentialImpl::ParseToken(
|
||||
for (auto const& parse : {
|
||||
std::function<DateTime(std::string const&)>([&](auto const& s) {
|
||||
// 'expires_on' as RFC3339 date string (absolute timestamp)
|
||||
return DateTime::Parse(s + tzOffsetStr, DateTime::DateFormat::Rfc3339);
|
||||
auto dateTime = DateTime::Parse(s + tzOffsetStr, DateTime::DateFormat::Rfc3339);
|
||||
return proactiveRenewal ? GetProactiveRenewalDateTime(
|
||||
PosixTimeConverter::DateTimeToPosixTime(dateTime))
|
||||
: dateTime;
|
||||
}),
|
||||
std::function<DateTime(std::string const&)>([](auto const& s) {
|
||||
std::function<DateTime(std::string const&)>([&](auto const& s) {
|
||||
// 'expires_on' as numeric string (posix time representing an absolute timestamp)
|
||||
return PosixTimeConverter::PosixTimeToDateTime(
|
||||
ParseNumericExpiration(s, MaxPosixTimestamp));
|
||||
auto value = ParseNumericExpiration(s, MaxPosixTimestamp);
|
||||
return proactiveRenewal ? GetProactiveRenewalDateTime(value)
|
||||
: PosixTimeConverter::PosixTimeToDateTime(value);
|
||||
}),
|
||||
std::function<DateTime(std::string const&)>([](auto const& s) {
|
||||
std::function<DateTime(std::string const&)>([&](auto const& s) {
|
||||
// 'expires_on' as RFC1123 date string (absolute timestamp)
|
||||
return DateTime::Parse(s, DateTime::DateFormat::Rfc1123);
|
||||
auto dateTime = DateTime::Parse(s, DateTime::DateFormat::Rfc1123);
|
||||
return proactiveRenewal ? GetProactiveRenewalDateTime(
|
||||
PosixTimeConverter::DateTimeToPosixTime(dateTime))
|
||||
: dateTime;
|
||||
}),
|
||||
})
|
||||
{
|
||||
|
||||
@ -129,7 +129,7 @@ AccessToken WorkloadIdentityCredential::GetToken(
|
||||
// call it later. Therefore, any capture made here will outlive the possible time frame when the
|
||||
// lambda might get called.
|
||||
return m_tokenCache.GetToken(scopesStr, tenantId, tokenRequestContext.MinimumExpiration, [&]() {
|
||||
return m_tokenCredentialImpl->GetToken(context, [&]() {
|
||||
return m_tokenCredentialImpl->GetToken(context, false, [&]() {
|
||||
auto body = m_requestBody;
|
||||
if (!scopesStr.empty())
|
||||
{
|
||||
|
||||
@ -125,11 +125,11 @@ TEST(ManagedIdentityCredential, AppServiceV2019)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
|
||||
Logger::SetListener(nullptr);
|
||||
}
|
||||
@ -217,11 +217,11 @@ TEST(ManagedIdentityCredential, AppServiceV2019ClientId)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
}
|
||||
|
||||
TEST(ManagedIdentityCredential, AppServiceV2019InvalidUrl)
|
||||
@ -407,11 +407,11 @@ TEST(ManagedIdentityCredential, AppServiceV2017)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
|
||||
Logger::SetListener(nullptr);
|
||||
}
|
||||
@ -499,11 +499,11 @@ TEST(ManagedIdentityCredential, AppServiceV2017ClientId)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
}
|
||||
|
||||
TEST(ManagedIdentityCredential, AppServiceV2017InvalidUrl)
|
||||
@ -665,11 +665,11 @@ TEST(ManagedIdentityCredential, CloudShell)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
|
||||
Logger::SetListener(nullptr);
|
||||
}
|
||||
@ -747,11 +747,11 @@ TEST(ManagedIdentityCredential, CloudShellClientId)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
}
|
||||
|
||||
TEST(ManagedIdentityCredential, CloudShellInvalidUrl)
|
||||
@ -965,11 +965,11 @@ TEST(ManagedIdentityCredential, AzureArc)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
|
||||
Logger::SetListener(nullptr);
|
||||
}
|
||||
@ -1234,26 +1234,38 @@ TEST(ManagedIdentityCredential, Imds)
|
||||
|
||||
return credential;
|
||||
},
|
||||
{{"https://azure.com/.default"}, {"https://outlook.com/.default"}, {}},
|
||||
{{"https://azure.com/.default"}, {"https://outlook.com/.default"}, {}, {}, {}, {}},
|
||||
std::vector<std::string>{
|
||||
"{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}",
|
||||
"{\"expires_in\":7200, \"access_token\":\"ACCESSTOKEN2\"}",
|
||||
"{\"expires_in\":9999, \"access_token\":\"ACCESSTOKEN3\"}"});
|
||||
"{\"expires_in\":9999, \"access_token\":\"ACCESSTOKEN3\"}",
|
||||
"{\"expires_in\":7200, \"access_token\":\"ACCESSTOKEN4\", \"refresh_in\":9999}",
|
||||
"{\"expires_in\":7199, \"access_token\":\"ACCESSTOKEN5\"}",
|
||||
"{\"expires_in\":7202, \"access_token\":\"ACCESSTOKEN6\"}"});
|
||||
|
||||
EXPECT_EQ(actual.Requests.size(), 3U);
|
||||
EXPECT_EQ(actual.Responses.size(), 3U);
|
||||
EXPECT_EQ(actual.Requests.size(), 6U);
|
||||
EXPECT_EQ(actual.Responses.size(), 6U);
|
||||
|
||||
auto const& request0 = actual.Requests.at(0);
|
||||
auto const& request1 = actual.Requests.at(1);
|
||||
auto const& request2 = actual.Requests.at(2);
|
||||
auto const& request3 = actual.Requests.at(3);
|
||||
auto const& request4 = actual.Requests.at(4);
|
||||
auto const& request5 = actual.Requests.at(5);
|
||||
|
||||
auto const& response0 = actual.Responses.at(0);
|
||||
auto const& response1 = actual.Responses.at(1);
|
||||
auto const& response2 = actual.Responses.at(2);
|
||||
auto const& response3 = actual.Responses.at(3);
|
||||
auto const& response4 = actual.Responses.at(4);
|
||||
auto const& response5 = actual.Responses.at(5);
|
||||
|
||||
EXPECT_EQ(request0.HttpMethod, HttpMethod::Get);
|
||||
EXPECT_EQ(request1.HttpMethod, HttpMethod::Get);
|
||||
EXPECT_EQ(request2.HttpMethod, HttpMethod::Get);
|
||||
EXPECT_EQ(request3.HttpMethod, HttpMethod::Get);
|
||||
EXPECT_EQ(request4.HttpMethod, HttpMethod::Get);
|
||||
EXPECT_EQ(request5.HttpMethod, HttpMethod::Get);
|
||||
|
||||
EXPECT_EQ(
|
||||
request0.AbsoluteUrl,
|
||||
@ -1272,9 +1284,27 @@ TEST(ManagedIdentityCredential, Imds)
|
||||
"http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
"?api-version=2018-02-01");
|
||||
|
||||
EXPECT_EQ(
|
||||
request3.AbsoluteUrl,
|
||||
"http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
"?api-version=2018-02-01");
|
||||
|
||||
EXPECT_EQ(
|
||||
request4.AbsoluteUrl,
|
||||
"http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
"?api-version=2018-02-01");
|
||||
|
||||
EXPECT_EQ(
|
||||
request5.AbsoluteUrl,
|
||||
"http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
"?api-version=2018-02-01");
|
||||
|
||||
EXPECT_TRUE(request0.Body.empty());
|
||||
EXPECT_TRUE(request1.Body.empty());
|
||||
EXPECT_TRUE(request2.Body.empty());
|
||||
EXPECT_TRUE(request3.Body.empty());
|
||||
EXPECT_TRUE(request4.Body.empty());
|
||||
EXPECT_TRUE(request5.Body.empty());
|
||||
|
||||
{
|
||||
EXPECT_NE(request0.Headers.find("Metadata"), request0.Headers.end());
|
||||
@ -1285,21 +1315,42 @@ TEST(ManagedIdentityCredential, Imds)
|
||||
|
||||
EXPECT_NE(request2.Headers.find("Metadata"), request2.Headers.end());
|
||||
EXPECT_EQ(request2.Headers.at("Metadata"), "true");
|
||||
|
||||
EXPECT_NE(request3.Headers.find("Metadata"), request3.Headers.end());
|
||||
EXPECT_EQ(request3.Headers.at("Metadata"), "true");
|
||||
|
||||
EXPECT_NE(request4.Headers.find("Metadata"), request4.Headers.end());
|
||||
EXPECT_EQ(request4.Headers.at("Metadata"), "true");
|
||||
|
||||
EXPECT_NE(request5.Headers.find("Metadata"), request5.Headers.end());
|
||||
EXPECT_EQ(request5.Headers.at("Metadata"), "true");
|
||||
}
|
||||
|
||||
EXPECT_EQ(response0.AccessToken.Token, "ACCESSTOKEN1");
|
||||
EXPECT_EQ(response1.AccessToken.Token, "ACCESSTOKEN2");
|
||||
EXPECT_EQ(response2.AccessToken.Token, "ACCESSTOKEN3");
|
||||
EXPECT_EQ(response3.AccessToken.Token, "ACCESSTOKEN4");
|
||||
EXPECT_EQ(response4.AccessToken.Token, "ACCESSTOKEN5");
|
||||
EXPECT_EQ(response5.AccessToken.Token, "ACCESSTOKEN6");
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
|
||||
EXPECT_GE(response3.AccessToken.ExpiresOn, response3.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response3.AccessToken.ExpiresOn, response3.LatestExpiration + 9999s);
|
||||
|
||||
EXPECT_GE(response4.AccessToken.ExpiresOn, response4.EarliestExpiration + 7199s);
|
||||
EXPECT_LE(response4.AccessToken.ExpiresOn, response4.LatestExpiration + 7199s);
|
||||
|
||||
EXPECT_GE(response5.AccessToken.ExpiresOn, response5.EarliestExpiration + 3601s);
|
||||
EXPECT_LE(response5.AccessToken.ExpiresOn, response5.LatestExpiration + 3601s);
|
||||
|
||||
Logger::SetListener(nullptr);
|
||||
}
|
||||
@ -1387,11 +1438,11 @@ TEST(ManagedIdentityCredential, ImdsClientId)
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 4999s);
|
||||
}
|
||||
|
||||
TEST(ManagedIdentityCredential, ImdsCreation)
|
||||
@ -1483,6 +1534,6 @@ TEST(ManagedIdentityCredential, ImdsCreation)
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 7200s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 7200s);
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 3600s);
|
||||
}
|
||||
|
||||
@ -31,30 +31,35 @@ private:
|
||||
HttpMethod m_httpMethod = HttpMethod(std::string());
|
||||
Url m_url;
|
||||
std::unique_ptr<TokenCredentialImpl> m_tokenCredentialImpl;
|
||||
bool m_proactiveRenewal;
|
||||
|
||||
public:
|
||||
explicit TokenCredentialImplTester(
|
||||
HttpMethod httpMethod,
|
||||
Url url,
|
||||
TokenCredentialOptions const& options)
|
||||
TokenCredentialOptions const& options,
|
||||
bool proactiveRenewal = false)
|
||||
: TokenCredential("TokenCredentialImplTester"), m_httpMethod(std::move(httpMethod)),
|
||||
m_url(std::move(url)), m_tokenCredentialImpl(new TokenCredentialImpl(options))
|
||||
m_url(std::move(url)), m_tokenCredentialImpl(new TokenCredentialImpl(options)),
|
||||
m_proactiveRenewal(proactiveRenewal)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TokenCredentialImplTester(
|
||||
std::function<void()> throwingFunction,
|
||||
TokenCredentialOptions const& options)
|
||||
TokenCredentialOptions const& options,
|
||||
bool proactiveRenewal = false)
|
||||
: TokenCredential("TokenCredentialImplTester"),
|
||||
m_throwingFunction(std::move(throwingFunction)),
|
||||
m_tokenCredentialImpl(new TokenCredentialImpl(options))
|
||||
m_tokenCredentialImpl(new TokenCredentialImpl(options)),
|
||||
m_proactiveRenewal(proactiveRenewal)
|
||||
{
|
||||
}
|
||||
|
||||
AccessToken GetToken(TokenRequestContext const& tokenRequestContext, Context const& context)
|
||||
const override
|
||||
{
|
||||
return m_tokenCredentialImpl->GetToken(context, [&]() {
|
||||
return m_tokenCredentialImpl->GetToken(context, m_proactiveRenewal, [&]() {
|
||||
m_throwingFunction();
|
||||
|
||||
std::string scopesStr;
|
||||
@ -179,6 +184,95 @@ TEST(TokenCredentialImpl, Normal)
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9999s);
|
||||
}
|
||||
|
||||
TEST(TokenCredentialImpl, Normal_ProactiveRenewal)
|
||||
{
|
||||
auto const actual = CredentialTestHelper::SimulateTokenRequest(
|
||||
[](auto transport) {
|
||||
TokenCredentialOptions options;
|
||||
options.Transport.Transport = transport;
|
||||
|
||||
return std::make_unique<TokenCredentialImplTester>(
|
||||
HttpMethod::Delete, Url("https://outlook.com/"), options, true);
|
||||
},
|
||||
{{"https://azure.com/.default", "https://microsoft.com/.default"},
|
||||
{"https://azure.com/.default", "https://microsoft.com/.default"},
|
||||
{"https://azure.com/.default", "https://microsoft.com/.default"},
|
||||
{"https://azure.com/.default", "https://microsoft.com/.default"}},
|
||||
std::vector<std::string>{
|
||||
"{\"expires_in\":3600, \"access_token\":\"ACCESSTOKEN1\"}",
|
||||
"{\"access_token\":\"ACCESSTOKEN2\", \"expires_in\":7199}",
|
||||
"{\"access_token\":\"ACCESSTOKEN3\", \"expires_in\":7200}",
|
||||
"{\"ab\":1,\"expires_in\":9999,\"cd\":2,\"access_token\":\"ACCESSTOKEN4\",\"ef\":3}"});
|
||||
|
||||
EXPECT_EQ(actual.Responses.size(), 4U);
|
||||
|
||||
auto const& response0 = actual.Responses.at(0);
|
||||
auto const& response1 = actual.Responses.at(1);
|
||||
auto const& response2 = actual.Responses.at(2);
|
||||
auto const& response3 = actual.Responses.at(3);
|
||||
|
||||
EXPECT_EQ(response0.AccessToken.Token, "ACCESSTOKEN1");
|
||||
EXPECT_EQ(response1.AccessToken.Token, "ACCESSTOKEN2");
|
||||
EXPECT_EQ(response2.AccessToken.Token, "ACCESSTOKEN3");
|
||||
EXPECT_EQ(response3.AccessToken.Token, "ACCESSTOKEN4");
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 7199s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 7199s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response3.AccessToken.ExpiresOn, response3.EarliestExpiration + 4999s);
|
||||
EXPECT_LE(response3.AccessToken.ExpiresOn, response3.LatestExpiration + 4999s);
|
||||
}
|
||||
|
||||
TEST(TokenCredentialImpl, RefreshInTakesPrecedence)
|
||||
{
|
||||
for (bool proactiveRenewal : {false, true})
|
||||
{
|
||||
auto const actual = CredentialTestHelper::SimulateTokenRequest(
|
||||
[&](auto transport) {
|
||||
TokenCredentialOptions options;
|
||||
options.Transport.Transport = transport;
|
||||
|
||||
return std::make_unique<TokenCredentialImplTester>(
|
||||
HttpMethod::Delete, Url("https://outlook.com/"), options, proactiveRenewal);
|
||||
},
|
||||
{{"https://azure.com/.default", "https://microsoft.com/.default"},
|
||||
{"https://azure.com/.default", "https://microsoft.com/.default"},
|
||||
{"https://azure.com/.default", "https://microsoft.com/.default"}},
|
||||
std::vector<std::string>{
|
||||
"{\"expires_in\":7200, \"access_token\":\"ACCESSTOKEN1\", \"refresh_in\":8400}",
|
||||
"{\"access_token\":\"ACCESSTOKEN2\", \"refresh_in\":1200}",
|
||||
"{\"refresh_in\":9998, "
|
||||
"\"ab\":1,\"expires_in\":9999,\"cd\":2,\"access_token\":\"ACCESSTOKEN3\",\"ef\":3}"});
|
||||
|
||||
EXPECT_EQ(actual.Responses.size(), 3U);
|
||||
|
||||
auto const& response0 = actual.Responses.at(0);
|
||||
auto const& response1 = actual.Responses.at(1);
|
||||
auto const& response2 = actual.Responses.at(2);
|
||||
|
||||
EXPECT_EQ(response0.AccessToken.Token, "ACCESSTOKEN1");
|
||||
EXPECT_EQ(response1.AccessToken.Token, "ACCESSTOKEN2");
|
||||
EXPECT_EQ(response2.AccessToken.Token, "ACCESSTOKEN3");
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
EXPECT_GE(response0.AccessToken.ExpiresOn, response0.EarliestExpiration + 8400s);
|
||||
EXPECT_LE(response0.AccessToken.ExpiresOn, response0.LatestExpiration + 8400s);
|
||||
|
||||
EXPECT_GE(response1.AccessToken.ExpiresOn, response1.EarliestExpiration + 1200s);
|
||||
EXPECT_LE(response1.AccessToken.ExpiresOn, response1.LatestExpiration + 1200s);
|
||||
|
||||
EXPECT_GE(response2.AccessToken.ExpiresOn, response2.EarliestExpiration + 9998s);
|
||||
EXPECT_LE(response2.AccessToken.ExpiresOn, response2.LatestExpiration + 9998s);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TokenCredentialImpl, StdException)
|
||||
{
|
||||
static_cast<void>(CredentialTestHelper::SimulateTokenRequest(
|
||||
@ -334,22 +428,25 @@ TEST(TokenCredentialImpl, FormatScopes)
|
||||
|
||||
TEST(TokenCredentialImpl, NoExpiration)
|
||||
{
|
||||
static_cast<void>(CredentialTestHelper::SimulateTokenRequest(
|
||||
[](auto transport) {
|
||||
TokenCredentialOptions options;
|
||||
options.Transport.Transport = transport;
|
||||
for (bool proactiveRenewal : {false, true})
|
||||
{
|
||||
static_cast<void>(CredentialTestHelper::SimulateTokenRequest(
|
||||
[&](auto transport) {
|
||||
TokenCredentialOptions options;
|
||||
options.Transport.Transport = transport;
|
||||
|
||||
return std::make_unique<TokenCredentialImplTester>(
|
||||
HttpMethod::Delete, Url("https://outlook.com/"), options);
|
||||
},
|
||||
{{"https://azure.com/.default", "https://microsoft.com/.default"}},
|
||||
{"{\"access_token\":\"ACCESSTOKEN\"}"},
|
||||
[](auto& credential, auto& tokenRequestContext, auto& context) {
|
||||
AccessToken token;
|
||||
EXPECT_THROW(
|
||||
token = credential.GetToken(tokenRequestContext, context), AuthenticationException);
|
||||
return token;
|
||||
}));
|
||||
return std::make_unique<TokenCredentialImplTester>(
|
||||
HttpMethod::Delete, Url("https://outlook.com/"), options, proactiveRenewal);
|
||||
},
|
||||
{{"https://azure.com/.default", "https://microsoft.com/.default"}},
|
||||
{"{\"access_token\":\"ACCESSTOKEN\"}"},
|
||||
[](auto& credential, auto& tokenRequestContext, auto& context) {
|
||||
AccessToken token;
|
||||
EXPECT_THROW(
|
||||
token = credential.GetToken(tokenRequestContext, context), AuthenticationException);
|
||||
return token;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TokenCredentialImpl, NoToken)
|
||||
@ -667,6 +764,268 @@ TEST(TokenCredentialImpl, ExpirationFormats)
|
||||
EXPECT_EQ(response43.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
}
|
||||
|
||||
TEST(TokenCredentialImpl, ExpirationFormats_ProactiveRenewal)
|
||||
{
|
||||
auto const actual = CredentialTestHelper::SimulateTokenRequest(
|
||||
[](auto transport) {
|
||||
TokenCredentialOptions options;
|
||||
options.Transport.Transport = transport;
|
||||
|
||||
return std::make_unique<TokenCredentialImplTester>(
|
||||
HttpMethod::Get, Url("https://microsoft.com/"), options, true);
|
||||
},
|
||||
std::vector<decltype(TokenRequestContext::Scopes)>(47, {"https://azure.com/.default"}),
|
||||
std::vector<std::string>{
|
||||
MakeTokenResponse("00", "3600", ""),
|
||||
MakeTokenResponse("01", "\"3600\"", ""),
|
||||
MakeTokenResponse("02", "\"unknown format\"", ""),
|
||||
MakeTokenResponse("03", "\"\"", ""),
|
||||
MakeTokenResponse("04", "null", ""),
|
||||
MakeTokenResponse("05", "", "43040261106"),
|
||||
MakeTokenResponse("06", "", "\"43040261106\""),
|
||||
MakeTokenResponse("07", "", "\"3333-11-22T04:05:06.000Z\""),
|
||||
MakeTokenResponse("08", "", "\"Sun, 22 Nov 3333 04:05:06 GMT\""),
|
||||
MakeTokenResponse("09", "", "\"unknown format\""),
|
||||
MakeTokenResponse("10", "", "\"\""),
|
||||
MakeTokenResponse("11", "", "null"),
|
||||
MakeTokenResponse("12", "3600", "43040261106"),
|
||||
MakeTokenResponse("13", "3600", "\"43040261106\""),
|
||||
MakeTokenResponse("14", "3600", "\"3333-11-22T04:05:06.000Z\""),
|
||||
MakeTokenResponse("15", "3600", "\"Sun, 22 Nov 3333 04:05:06 GMT\""),
|
||||
MakeTokenResponse("16", "3600", "\"unknown format\""),
|
||||
MakeTokenResponse("17", "3600", "\"\""),
|
||||
MakeTokenResponse("18", "3600", "null"),
|
||||
MakeTokenResponse("19", "\"3600\"", "43040261106"),
|
||||
MakeTokenResponse("20", "\"3600\"", "\"43040261106\""),
|
||||
MakeTokenResponse("21", "\"3600\"", "\"3333-11-22T04:05:06.000Z\""),
|
||||
MakeTokenResponse("22", "\"3600\"", "\"Sun, 22 Nov 3333 04:05:06 GMT\""),
|
||||
MakeTokenResponse("23", "\"3600\"", "\"unknown format\""),
|
||||
MakeTokenResponse("24", "\"3600\"", "\"\""),
|
||||
MakeTokenResponse("25", "\"3600\"", "null"),
|
||||
MakeTokenResponse("26", "\"unknown format\"", "43040261106"),
|
||||
MakeTokenResponse("27", "\"unknown format\"", "\"43040261106\""),
|
||||
MakeTokenResponse("28", "\"unknown format\"", "\"3333-11-22T04:05:06.000Z\""),
|
||||
MakeTokenResponse("29", "\"unknown format\"", "\"Sun, 22 Nov 3333 04:05:06 GMT\""),
|
||||
MakeTokenResponse("30", "\"unknown format\"", "\"unknown format\""),
|
||||
MakeTokenResponse("31", "\"unknown format\"", "\"\""),
|
||||
MakeTokenResponse("32", "\"unknown format\"", "null"),
|
||||
MakeTokenResponse("33", "\"\"", "43040261106"),
|
||||
MakeTokenResponse("34", "\"\"", "\"43040261106\""),
|
||||
MakeTokenResponse("35", "\"\"", "\"3333-11-22T04:05:06.000Z\""),
|
||||
MakeTokenResponse("36", "\"\"", "\"Sun, 22 Nov 3333 04:05:06 GMT\""),
|
||||
MakeTokenResponse("37", "\"\"", "\"unknown format\""),
|
||||
MakeTokenResponse("38", "\"\"", "\"\""),
|
||||
MakeTokenResponse("39", "\"\"", "null"),
|
||||
MakeTokenResponse("40", "null", "43040261106"),
|
||||
MakeTokenResponse("41", "null", "\"43040261106\""),
|
||||
MakeTokenResponse("42", "null", "\"3333-11-22T04:05:06.000Z\""),
|
||||
MakeTokenResponse("43", "null", "\"Sun, 22 Nov 3333 04:05:06 GMT\""),
|
||||
MakeTokenResponse("44", "null", "\"unknown format\""),
|
||||
MakeTokenResponse("45", "null", "\"\""),
|
||||
MakeTokenResponse("46", "null", "null"),
|
||||
},
|
||||
[](auto& credential, auto& tokenRequestContext, auto& context) {
|
||||
AccessToken token;
|
||||
token.Token = "FAILED";
|
||||
|
||||
try
|
||||
{
|
||||
token = credential.GetToken(tokenRequestContext, context);
|
||||
}
|
||||
catch (AuthenticationException const&)
|
||||
{
|
||||
}
|
||||
|
||||
return token;
|
||||
});
|
||||
|
||||
EXPECT_EQ(actual.Requests.size(), 47U);
|
||||
EXPECT_EQ(actual.Responses.size(), 47U);
|
||||
|
||||
auto const& response00 = actual.Responses.at(0);
|
||||
auto const& response01 = actual.Responses.at(1);
|
||||
auto const& response02 = actual.Responses.at(2);
|
||||
auto const& response03 = actual.Responses.at(3);
|
||||
auto const& response04 = actual.Responses.at(4);
|
||||
auto const& response05 = actual.Responses.at(5);
|
||||
auto const& response06 = actual.Responses.at(6);
|
||||
auto const& response07 = actual.Responses.at(7);
|
||||
auto const& response08 = actual.Responses.at(8);
|
||||
auto const& response09 = actual.Responses.at(9);
|
||||
auto const& response10 = actual.Responses.at(10);
|
||||
auto const& response11 = actual.Responses.at(11);
|
||||
auto const& response12 = actual.Responses.at(12);
|
||||
auto const& response13 = actual.Responses.at(13);
|
||||
auto const& response14 = actual.Responses.at(14);
|
||||
auto const& response15 = actual.Responses.at(15);
|
||||
auto const& response16 = actual.Responses.at(16);
|
||||
auto const& response17 = actual.Responses.at(17);
|
||||
auto const& response18 = actual.Responses.at(18);
|
||||
auto const& response19 = actual.Responses.at(19);
|
||||
auto const& response20 = actual.Responses.at(20);
|
||||
auto const& response21 = actual.Responses.at(21);
|
||||
auto const& response22 = actual.Responses.at(22);
|
||||
auto const& response23 = actual.Responses.at(23);
|
||||
auto const& response24 = actual.Responses.at(24);
|
||||
auto const& response25 = actual.Responses.at(25);
|
||||
auto const& response26 = actual.Responses.at(26);
|
||||
auto const& response27 = actual.Responses.at(27);
|
||||
auto const& response28 = actual.Responses.at(28);
|
||||
auto const& response29 = actual.Responses.at(29);
|
||||
auto const& response30 = actual.Responses.at(30);
|
||||
auto const& response31 = actual.Responses.at(31);
|
||||
auto const& response32 = actual.Responses.at(32);
|
||||
auto const& response33 = actual.Responses.at(33);
|
||||
auto const& response34 = actual.Responses.at(34);
|
||||
auto const& response35 = actual.Responses.at(35);
|
||||
auto const& response36 = actual.Responses.at(36);
|
||||
auto const& response37 = actual.Responses.at(37);
|
||||
auto const& response38 = actual.Responses.at(38);
|
||||
auto const& response39 = actual.Responses.at(39);
|
||||
auto const& response40 = actual.Responses.at(40);
|
||||
auto const& response41 = actual.Responses.at(41);
|
||||
auto const& response42 = actual.Responses.at(42);
|
||||
auto const& response43 = actual.Responses.at(43);
|
||||
auto const& response44 = actual.Responses.at(44);
|
||||
auto const& response45 = actual.Responses.at(45);
|
||||
auto const& response46 = actual.Responses.at(46);
|
||||
|
||||
EXPECT_EQ(response00.AccessToken.Token, "ACCESSTOKEN00");
|
||||
EXPECT_EQ(response01.AccessToken.Token, "ACCESSTOKEN01");
|
||||
EXPECT_EQ(response02.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response03.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response04.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response05.AccessToken.Token, "ACCESSTOKEN05");
|
||||
EXPECT_EQ(response06.AccessToken.Token, "ACCESSTOKEN06");
|
||||
EXPECT_EQ(response07.AccessToken.Token, "ACCESSTOKEN07");
|
||||
EXPECT_EQ(response08.AccessToken.Token, "ACCESSTOKEN08");
|
||||
EXPECT_EQ(response09.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response10.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response11.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response12.AccessToken.Token, "ACCESSTOKEN12");
|
||||
EXPECT_EQ(response13.AccessToken.Token, "ACCESSTOKEN13");
|
||||
EXPECT_EQ(response14.AccessToken.Token, "ACCESSTOKEN14");
|
||||
EXPECT_EQ(response15.AccessToken.Token, "ACCESSTOKEN15");
|
||||
EXPECT_EQ(response16.AccessToken.Token, "ACCESSTOKEN16");
|
||||
EXPECT_EQ(response17.AccessToken.Token, "ACCESSTOKEN17");
|
||||
EXPECT_EQ(response18.AccessToken.Token, "ACCESSTOKEN18");
|
||||
EXPECT_EQ(response19.AccessToken.Token, "ACCESSTOKEN19");
|
||||
EXPECT_EQ(response20.AccessToken.Token, "ACCESSTOKEN20");
|
||||
EXPECT_EQ(response21.AccessToken.Token, "ACCESSTOKEN21");
|
||||
EXPECT_EQ(response22.AccessToken.Token, "ACCESSTOKEN22");
|
||||
EXPECT_EQ(response23.AccessToken.Token, "ACCESSTOKEN23");
|
||||
EXPECT_EQ(response24.AccessToken.Token, "ACCESSTOKEN24");
|
||||
EXPECT_EQ(response25.AccessToken.Token, "ACCESSTOKEN25");
|
||||
EXPECT_EQ(response26.AccessToken.Token, "ACCESSTOKEN26");
|
||||
EXPECT_EQ(response27.AccessToken.Token, "ACCESSTOKEN27");
|
||||
EXPECT_EQ(response28.AccessToken.Token, "ACCESSTOKEN28");
|
||||
EXPECT_EQ(response29.AccessToken.Token, "ACCESSTOKEN29");
|
||||
EXPECT_EQ(response30.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response31.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response32.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response33.AccessToken.Token, "ACCESSTOKEN33");
|
||||
EXPECT_EQ(response34.AccessToken.Token, "ACCESSTOKEN34");
|
||||
EXPECT_EQ(response35.AccessToken.Token, "ACCESSTOKEN35");
|
||||
EXPECT_EQ(response36.AccessToken.Token, "ACCESSTOKEN36");
|
||||
EXPECT_EQ(response37.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response38.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response39.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response40.AccessToken.Token, "ACCESSTOKEN40");
|
||||
EXPECT_EQ(response41.AccessToken.Token, "ACCESSTOKEN41");
|
||||
EXPECT_EQ(response42.AccessToken.Token, "ACCESSTOKEN42");
|
||||
EXPECT_EQ(response43.AccessToken.Token, "ACCESSTOKEN43");
|
||||
EXPECT_EQ(response44.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response45.AccessToken.Token, "FAILED");
|
||||
EXPECT_EQ(response46.AccessToken.Token, "FAILED");
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
EXPECT_GE(response00.AccessToken.ExpiresOn, response00.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response00.AccessToken.ExpiresOn, response00.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response01.AccessToken.ExpiresOn, response01.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response01.AccessToken.ExpiresOn, response01.LatestExpiration + 3600s);
|
||||
|
||||
// Approximate midpoint between now and the actual expiry date, simulating half lifetime of the
|
||||
// expiry.
|
||||
EXPECT_GE(response05.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response05.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response06.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response06.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response07.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response07.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response08.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response08.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
|
||||
EXPECT_GE(response12.AccessToken.ExpiresOn, response12.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response12.AccessToken.ExpiresOn, response12.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response13.AccessToken.ExpiresOn, response13.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response13.AccessToken.ExpiresOn, response13.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response14.AccessToken.ExpiresOn, response14.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response14.AccessToken.ExpiresOn, response14.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response15.AccessToken.ExpiresOn, response15.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response15.AccessToken.ExpiresOn, response15.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response16.AccessToken.ExpiresOn, response16.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response16.AccessToken.ExpiresOn, response16.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response17.AccessToken.ExpiresOn, response17.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response17.AccessToken.ExpiresOn, response17.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response18.AccessToken.ExpiresOn, response18.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response18.AccessToken.ExpiresOn, response18.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response19.AccessToken.ExpiresOn, response19.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response19.AccessToken.ExpiresOn, response19.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response20.AccessToken.ExpiresOn, response20.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response20.AccessToken.ExpiresOn, response20.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response21.AccessToken.ExpiresOn, response21.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response21.AccessToken.ExpiresOn, response21.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response22.AccessToken.ExpiresOn, response22.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response22.AccessToken.ExpiresOn, response22.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response23.AccessToken.ExpiresOn, response23.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response23.AccessToken.ExpiresOn, response23.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response24.AccessToken.ExpiresOn, response24.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response24.AccessToken.ExpiresOn, response24.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response25.AccessToken.ExpiresOn, response25.EarliestExpiration + 3600s);
|
||||
EXPECT_LE(response25.AccessToken.ExpiresOn, response25.LatestExpiration + 3600s);
|
||||
|
||||
EXPECT_GE(response26.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response26.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response27.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response27.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response28.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response28.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response29.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response29.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
|
||||
EXPECT_GE(response33.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response33.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response34.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response34.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response35.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response35.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response36.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response36.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
|
||||
EXPECT_GE(response40.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response40.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response41.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response41.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response42.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response42.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
EXPECT_GE(response43.AccessToken.ExpiresOn, DateTime(2678, 12, 31, 16, 19, 11));
|
||||
EXPECT_LE(response43.AccessToken.ExpiresOn, DateTime(3333, 11, 22, 4, 5, 6));
|
||||
}
|
||||
|
||||
TEST(TokenCredentialImpl, MaxValues)
|
||||
{
|
||||
// 'exp_in' negative
|
||||
@ -689,6 +1048,26 @@ TEST(TokenCredentialImpl, MaxValues)
|
||||
"{\"token\": \"x\",\"exp_in\":2147483648}", "token", "exp_in", "exp_at")),
|
||||
std::exception);
|
||||
|
||||
// 'ref_in' negative
|
||||
EXPECT_THROW(
|
||||
static_cast<void>(TokenCredentialImpl::ParseToken(
|
||||
"{\"token\": \"x\",\"ref_in\":-1}", "token", "exp_in", "exp_at", "ref_in")),
|
||||
std::exception);
|
||||
|
||||
// 'ref_in' zero
|
||||
EXPECT_NO_THROW(static_cast<void>(TokenCredentialImpl::ParseToken(
|
||||
"{\"token\": \"x\",\"ref_in\":0}", "token", "exp_in", "exp_at", "ref_in")));
|
||||
|
||||
// 'ref_in' == int32 max
|
||||
EXPECT_NO_THROW(static_cast<void>(TokenCredentialImpl::ParseToken(
|
||||
"{\"token\": \"x\",\"ref_in\":2147483647}", "token", "exp_in", "exp_at", "ref_in")));
|
||||
|
||||
// 'ref_in' > int32 max
|
||||
EXPECT_THROW(
|
||||
static_cast<void>(TokenCredentialImpl::ParseToken(
|
||||
"{\"token\": \"x\",\"ref_in\":2147483648}", "token", "exp_in", "exp_at", "ref_in")),
|
||||
std::exception);
|
||||
|
||||
// 'exp_at' negative
|
||||
EXPECT_THROW(
|
||||
static_cast<void>(TokenCredentialImpl::ParseToken(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user