Remake ClientSecretCredential (#212)

This commit is contained in:
Anton Kolesnyk 2020-06-27 15:27:11 -07:00 committed by GitHub
parent 0cfcdca725
commit ad2e63afe1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 364 additions and 471 deletions

View File

@ -18,6 +18,7 @@ add_library (
${TARGET_NAME}
src/context.cpp
src/credentials/credentials.cpp
src/credentials/policy/policies.cpp
src/http/http.cpp
src/http/policy.cpp
src/http/request.cpp

View File

@ -3,87 +3,81 @@
#pragma once
#include "azure.hpp"
#include <chrono>
#include <memory>
#include <mutex>
#include <context.hpp>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
namespace Azure { namespace Core { namespace Credentials {
namespace Details {
class CredentialTest;
}
class Credential {
virtual void SetScopes(std::string const& scopes) { AZURE_UNREFERENCED_PARAMETER(scopes); }
public:
class Internal;
virtual ~Credential() noexcept = default;
protected:
Credential() = default;
Credential(Credential const& other) = default;
Credential& operator=(Credential const& other) = default;
struct AccessToken
{
std::string Token;
std::chrono::system_clock::time_point ExpiresOn;
};
class TokenCredential : public Credential {
friend class Details::CredentialTest;
class Token;
std::shared_ptr<Token> m_token;
std::mutex m_mutex;
std::string UpdateTokenNonThreadSafe(Token& token);
virtual bool IsTokenExpired(std::chrono::system_clock::time_point const& tokenExpiration) const;
virtual void RefreshToken(
std::string& newTokenString,
std::chrono::system_clock::time_point& newExpiration)
= 0;
class TokenCredential {
public:
class Internal;
TokenCredential(TokenCredential const& other);
TokenCredential& operator=(TokenCredential const& other);
virtual AccessToken GetToken(Context& context, std::vector<std::string> const& scopes)
const = 0;
protected:
TokenCredential() = default;
TokenCredential(TokenCredential const& other, int) : Credential(other) {}
TokenCredential() {}
void Init(TokenCredential const& other);
virtual std::string GetToken();
void ResetToken();
private:
TokenCredential(TokenCredential const&) = delete;
void operator=(TokenCredential const&) = delete;
};
class ClientSecretCredential : public TokenCredential {
friend class Details::CredentialTest;
class ClientSecret;
std::shared_ptr<ClientSecret> m_clientSecret;
std::mutex m_mutex;
void SetScopes(std::string const& scopes) override;
std::string GetToken() override;
void RefreshToken(
std::string& newTokenString,
std::chrono::system_clock::time_point& newExpiration) override;
private:
std::string const m_tenantId;
std::string const m_clientId;
std::string const m_clientSecret;
public:
ClientSecretCredential(
std::string const& tenantId,
std::string const& clientId,
std::string const& clientSecret);
std::string const& clientSecret)
: m_tenantId(tenantId), m_clientId(clientId), m_clientSecret(clientSecret)
{
}
ClientSecretCredential(ClientSecretCredential const& other);
ClientSecretCredential& operator=(ClientSecretCredential const& other);
ClientSecretCredential(
std::string const&& tenantId,
std::string const& clientId,
std::string const& clientSecret)
: m_tenantId(std::move(tenantId)), m_clientId(clientId), m_clientSecret(clientSecret)
{
}
ClientSecretCredential(
std::string const&& tenantId,
std::string const&& clientId,
std::string const& clientSecret)
: m_tenantId(std::move(tenantId)), m_clientId(std::move(clientId)),
m_clientSecret(clientSecret)
{
}
ClientSecretCredential(
std::string const&& tenantId,
std::string const&& clientId,
std::string const&& clientSecret)
: m_tenantId(std::move(tenantId)), m_clientId(std::move(clientId)),
m_clientSecret(std::move(clientSecret))
{
}
AccessToken GetToken(Context& context, std::vector<std::string> const& scopes) const override;
};
class AuthenticationException : public std::runtime_error {
public:
explicit AuthenticationException(std::string const& msg) : std::runtime_error(msg) {}
};
}}} // namespace Azure::Core::Credentials

View File

@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <credentials/credentials.hpp>
#include <http/policy.hpp>
#include <memory>
#include <mutex>
#include <string>
#include <utility>
namespace Azure { namespace Core { namespace Credentials { namespace Policy {
class BearerTokenAuthenticationPolicy : public Http::HttpPolicy {
private:
std::shared_ptr<TokenCredential const> const m_credential;
std::vector<std::string> m_scopes;
mutable AccessToken m_accessToken;
mutable std::mutex m_accessTokenMutex;
BearerTokenAuthenticationPolicy(BearerTokenAuthenticationPolicy const&) = delete;
void operator=(BearerTokenAuthenticationPolicy const&) = delete;
public:
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
std::string const& scope)
: m_credential(credential)
{
m_scopes.push_back(scope);
}
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
std::vector<std::string> const& scopes)
: m_credential(credential), m_scopes(scopes)
{
}
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
std::vector<std::string> const&& scopes)
: m_credential(credential), m_scopes(std::move(scopes))
{
}
template <typename ScopesIterator>
BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> const& credential,
ScopesIterator const& scopesBegin,
ScopesIterator const& scopesEnd)
: m_credential(credential), m_scopes(scopesBegin, scopesEnd)
{
}
HttpPolicy* Clone() const override
{
return new BearerTokenAuthenticationPolicy(m_credential, m_scopes);
}
std::unique_ptr<Http::Response> Send(
Context& context,
Http::Request& request,
Http::NextHttpPolicy policy) const override;
};
}}}} // namespace Azure::Core::Credentials::Policy

View File

@ -1,68 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <credentials/credentials.hpp>
#include <chrono>
#include <functional>
#include <mutex>
#include <string>
namespace Azure { namespace Core { namespace Credentials {
class Credential::Internal {
public:
static void SetScopes(Credential& credential, std::string const& scopes)
{
credential.SetScopes(scopes);
}
};
class TokenCredential::Token {
friend class TokenCredential;
friend class Details::CredentialTest;
std::string m_tokenString;
std::chrono::system_clock::time_point m_expiresAt;
std::mutex m_mutex;
};
class TokenCredential::Internal {
public:
static std::string GetToken(TokenCredential& credential)
{
return credential.GetToken();
}
};
class ClientSecretCredential::ClientSecret {
friend class ClientSecretCredential;
friend class Details::CredentialTest;
std::string m_tenantId;
std::string m_clientId;
std::string m_clientSecret;
std::string m_scopes;
public:
ClientSecret(
std::string const& tenantId,
std::string const& clientId,
std::string const& clientSecret)
: m_tenantId(tenantId), m_clientId(clientId), m_clientSecret(clientSecret)
{
}
ClientSecret(
std::string const& tenantId,
std::string const& clientId,
std::string const& clientSecret,
std::string const& scopes)
: m_tenantId(tenantId), m_clientId(clientId), m_clientSecret(clientSecret), m_scopes(scopes)
{
}
};
}}} // namespace Azure::Core::Credentials

View File

@ -2,125 +2,205 @@
// SPDX-License-Identifier: MIT
#include <credentials/credentials.hpp>
#include <internal/credentials_internal.hpp>
#include <http/curl/curl.hpp>
#include <http/http.hpp>
#include <http/pipeline.hpp>
#include <http/stream.hpp>
#include <iomanip>
#include <sstream>
#include <stdexcept>
using namespace Azure::Core::Credentials;
std::string TokenCredential::UpdateTokenNonThreadSafe(Token& token)
namespace {
std::string UrlEncode(std::string const& s)
{
std::string newTokenString;
std::chrono::system_clock::time_point newExpiration;
std::ostringstream encoded;
encoded << std::hex;
this->RefreshToken(newTokenString, newExpiration);
token.m_tokenString = newTokenString;
token.m_expiresAt = newExpiration;
return newTokenString;
}
bool TokenCredential::IsTokenExpired(
std::chrono::system_clock::time_point const& tokenExpiration) const
{
return tokenExpiration <= std::chrono::system_clock::now() - std::chrono::minutes(5);
}
TokenCredential::TokenCredential(TokenCredential const& other) : Credential(other)
{
this->Init(other);
}
TokenCredential& TokenCredential::operator=(TokenCredential const& other)
{
std::lock_guard<std::mutex> const thisTokenPtrLock(this->m_mutex);
this->Init(other);
return *this;
}
void TokenCredential::Init(TokenCredential const& other)
{
std::lock_guard<std::mutex> const otherTokenPtrLock(const_cast<std::mutex&>(other.m_mutex));
this->m_token = other.m_token;
}
std::string TokenCredential::GetToken()
{
std::lock_guard<std::mutex> const tokenPtrLock(this->m_mutex);
if (!this->m_token)
for (auto c : s)
{
this->m_token = std::make_shared<Token>();
return UpdateTokenNonThreadSafe(*this->m_token);
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
|| (c == '-' || c == '.' || c == '_' || c == '~'))
{
encoded << c;
}
else
{
encoded << std::uppercase;
encoded << '%' << std::setw(2) << int((unsigned char)c);
encoded << std::nouppercase;
}
}
std::lock_guard<std::mutex> const tokenLock(this->m_token->m_mutex);
Token& token = *this->m_token;
return this->IsTokenExpired(token.m_expiresAt) ? UpdateTokenNonThreadSafe(token)
: token.m_tokenString;
return encoded.str();
}
} // namespace
void TokenCredential::ResetToken()
AccessToken ClientSecretCredential::GetToken(
Context& context,
std::vector<std::string> const& scopes) const
{
std::lock_guard<std::mutex> const tokenPtrLock(this->m_mutex);
this->m_token.reset();
}
void ClientSecretCredential::SetScopes(std::string const& scopes)
{
std::lock_guard<std::mutex> const clientSecretPtrLock(this->m_mutex);
if (scopes == this->m_clientSecret->m_scopes)
return;
this->TokenCredential::ResetToken();
if (this->m_clientSecret.unique())
static std::string const errorMsgPrefix("ClientSecretCredential::GetToken: ");
try
{
this->m_clientSecret->m_scopes = scopes;
return;
std::ostringstream url;
url << "https://login.microsoftonline.com/" << UrlEncode(m_tenantId) << "/oauth2/v2.0/token";
std::ostringstream body;
body << "grant_type=client_credentials&client_id=" << UrlEncode(m_clientId)
<< "&client_secret=" << UrlEncode(m_clientSecret);
if (!scopes.empty())
{
auto scopesIter = scopes.begin();
body << "&scope=" << UrlEncode(*scopesIter);
auto const scopesEnd = scopes.end();
for (++scopesIter; scopesIter != scopesEnd; ++scopesIter)
{
body << " " << *scopesIter;
}
}
auto const bodyString = body.str();
std::vector<std::uint8_t> bodyVec;
bodyVec.reserve(bodyString.size());
for (auto c : bodyString)
{
bodyVec.push_back(static_cast<std::uint8_t>(c));
}
auto bodyStream = std::make_unique<Http::MemoryBodyStream>(bodyVec);
Http::Request request(Http::HttpMethod::Post, url.str(), bodyStream.get());
bodyStream.release();
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Content-Length", std::to_string(bodyVec.size()));
std::shared_ptr<Http::HttpTransport> transport = std::make_unique<Http::CurlTransport>();
std::vector<std::unique_ptr<Http::HttpPolicy>> policies;
policies.push_back(std::make_unique<Http::RequestIdPolicy>());
Http::RetryOptions retryOptions;
policies.push_back(std::make_unique<Http::RetryPolicy>(retryOptions));
policies.push_back(std::make_unique<Http::TransportPolicy>(std::move(transport)));
Http::HttpPipeline httpPipeline(policies);
std::shared_ptr<Http::Response> response = httpPipeline.Send(context, request);
if (!response)
{
throw AuthenticationException(errorMsgPrefix + "null response");
}
auto const statusCode = response->GetStatusCode();
if (statusCode != Http::HttpStatusCode::Ok)
{
std::ostringstream errorMsg;
errorMsg << errorMsgPrefix << "error response: "
<< static_cast<std::underlying_type<Http::HttpStatusCode>::type>(statusCode)
<< " " << response->GetReasonPhrase();
throw AuthenticationException(errorMsg.str());
}
auto const responseStream = response->GetBodyStream();
auto const responseStreamLength = responseStream->Length();
std::string responseBody(static_cast<std::string::size_type>(responseStreamLength), 0);
responseStream->Read(
static_cast<std::uint8_t*>(static_cast<void*>(&responseBody[0])), responseStreamLength);
// TODO: use JSON parser.
auto const responseBodySize = responseBody.size();
static std::string const jsonExpiresIn = "expires_in";
static std::string const jsonAccessToken = "access_token";
auto responseBodyPos = responseBody.find(':', responseBody.find(jsonExpiresIn));
if (responseBodyPos == std::string::npos)
{
std::ostringstream errorMsg;
errorMsg << errorMsgPrefix << "response json: \'" << jsonExpiresIn << "\' not found.";
throw AuthenticationException(errorMsg.str());
}
for (; responseBodyPos < responseBodySize; ++responseBodyPos)
{
auto c = responseBody[responseBodyPos];
if (c != ':' && c != ' ' && c != '\"' && c != '\'')
{
break;
}
}
long long expiresInSeconds = 0;
for (; responseBodyPos < responseBodySize; ++responseBodyPos)
{
auto c = responseBody[responseBodyPos];
if (c < '0' || c > '9')
{
break;
}
expiresInSeconds = (expiresInSeconds * 10) + (c - '0');
}
responseBodyPos = responseBody.find(':', responseBody.find(jsonAccessToken));
if (responseBodyPos == std::string::npos)
{
std::ostringstream errorMsg;
errorMsg << errorMsgPrefix << "response json: \'" << jsonAccessToken << "\' not found.";
throw AuthenticationException(errorMsg.str());
}
for (; responseBodyPos < responseBodySize; ++responseBodyPos)
{
auto c = responseBody[responseBodyPos];
if (c != ':' && c != ' ' && c != '\"' && c != '\'')
{
break;
}
}
auto const tokenBegin = responseBodyPos;
for (; responseBodyPos < responseBodySize; ++responseBodyPos)
{
auto c = responseBody[responseBodyPos];
if (c == '\"' || c == '\'')
{
break;
}
}
auto const tokenEnd = responseBodyPos;
expiresInSeconds -= 2 * 60;
auto const responseBodyBegin = responseBody.begin();
return {
std::string(responseBodyBegin + tokenBegin, responseBodyBegin + tokenEnd),
std::chrono::system_clock::now()
+ std::chrono::seconds(expiresInSeconds < 0 ? 0 : expiresInSeconds),
};
}
catch (AuthenticationException const&)
{
throw;
}
catch (std::exception const& e)
{
throw new AuthenticationException(e.what());
}
catch (...)
{
throw new AuthenticationException("unknown error");
}
this->m_clientSecret = std::make_shared<ClientSecret>(
this->m_clientSecret->m_tenantId,
this->m_clientSecret->m_clientId,
this->m_clientSecret->m_clientSecret,
scopes);
}
std::string ClientSecretCredential::GetToken()
{
std::lock_guard<std::mutex> const clientSecretPtrLock(this->m_mutex);
return this->TokenCredential::GetToken();
}
void ClientSecretCredential::RefreshToken(
std::string& newTokenString,
std::chrono::system_clock::time_point& newExpiration)
{
// TODO: get token using scopes, tenantId, clientId, and clientSecretId.
AZURE_UNREFERENCED_PARAMETER(newTokenString);
AZURE_UNREFERENCED_PARAMETER(newExpiration);
}
ClientSecretCredential::ClientSecretCredential(
std::string const& tenantId,
std::string const& clientId,
std::string const& clientSecret)
: m_clientSecret(new ClientSecret(tenantId, clientId, clientSecret))
{
}
ClientSecretCredential::ClientSecretCredential(ClientSecretCredential const& other)
: TokenCredential(other, 0)
{
std::lock_guard<std::mutex> const otherClientSecretPtrLock(
const_cast<std::mutex&>(other.m_mutex));
this->TokenCredential::Init(other);
}
ClientSecretCredential& ClientSecretCredential::operator=(ClientSecretCredential const& other)
{
std::lock_guard<std::mutex> const otherClientSecretPtrLock(this->m_mutex);
this->TokenCredential::operator=(other);
return *this;
}

View File

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <credentials/policy/policies.hpp>
using namespace Azure::Core::Credentials::Policy;
std::unique_ptr<Azure::Core::Http::Response> BearerTokenAuthenticationPolicy::Send(
Context& context,
Http::Request& request,
Http::NextHttpPolicy policy) const
{
{
std::lock_guard<std::mutex> lock(m_accessTokenMutex);
if (std::chrono::system_clock::now() > m_accessToken.ExpiresOn)
{
m_accessToken = m_credential->GetToken(context, m_scopes);
}
request.AddHeader("authorization", "Bearer " + m_accessToken.Token);
}
return policy.Send(context, request);
}

View File

@ -3,7 +3,6 @@
#include "gtest/gtest.h"
#include <http/http.hpp>
#include <internal/credentials_internal.hpp>
#include <string>
#include <vector>
@ -116,211 +115,3 @@ TEST(Http_Request, add_path)
req.GetEncodedUrl(),
url + "/path/path2/path3?query=value");
}
class Azure::Core::Credentials::Details::CredentialTest : public ClientSecretCredential {
public:
CredentialTest(
std::string const& tenantId,
std::string const& clientId,
std::string const& clientSecret)
: ClientSecretCredential(tenantId, clientId, clientSecret)
{
}
std::string NewTokenString;
std::chrono::system_clock::time_point NewExpiration;
bool IsExpired;
std::string GetTenantId() const
{
return this->ClientSecretCredential::m_clientSecret->m_tenantId;
}
std::string GetClientId() const
{
return this->ClientSecretCredential::m_clientSecret->m_clientId;
}
std::string GetClientSecret() const
{
return this->ClientSecretCredential::m_clientSecret->m_clientSecret;
}
std::string GetScopes() const { return this->ClientSecretCredential::m_clientSecret->m_scopes; }
bool IsTokenPtrNull() const { return !this->TokenCredential::m_token; }
std::string GetTokenString() const { return this->TokenCredential::m_token->m_tokenString; }
std::chrono::system_clock::time_point GetExpiration() const
{
return this->TokenCredential::m_token->m_expiresAt;
}
private:
void RefreshToken(
std::string& newTokenString,
std::chrono::system_clock::time_point& newExpiration) override
{
newTokenString = this->NewTokenString;
newExpiration = this->NewExpiration;
}
bool IsTokenExpired(std::chrono::system_clock::time_point const&) const override
{
return this->IsExpired;
}
};
TEST(Credential, ClientSecretCredential)
{
// Client Secret credential properties
std::string const tenantId = "tenantId";
std::string const clientId = "clientId";
std::string const clientSecret = "clientSecret";
Credentials::Details::CredentialTest clientSecretCredential(tenantId, clientId, clientSecret);
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
// Token credential
{
auto const emptyString = std::string();
auto const defaultTime = std::chrono::system_clock::time_point();
{
// Default values
{
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
}
{
// Set scopes
std::string const scopes = "scope";
{
Credentials::Credential::Internal::SetScopes(clientSecretCredential, scopes);
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
}
// Get token
{
std::string const olderToken = "olderToken";
std::string const newToken = "newToken";
auto const olderTime = defaultTime + std::chrono::minutes(10);
auto const newTime = olderTime + std::chrono::minutes(10);
{
clientSecretCredential.IsExpired = true;
clientSecretCredential.NewTokenString = olderToken;
clientSecretCredential.NewExpiration = olderTime;
auto const tokenReceived
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), false);
EXPECT_EQ(tokenReceived, olderToken);
EXPECT_EQ(clientSecretCredential.GetTokenString(), olderToken);
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
EXPECT_EQ(clientSecretCredential.GetExpiration(), olderTime);
}
// Attemp to get the token when it is not expired yet
{
clientSecretCredential.IsExpired = false;
clientSecretCredential.NewTokenString = newToken;
clientSecretCredential.NewExpiration = newTime;
auto const tokenReceived
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
EXPECT_EQ(tokenReceived, olderToken);
EXPECT_EQ(clientSecretCredential.GetTokenString(), olderToken);
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
EXPECT_EQ(clientSecretCredential.GetExpiration(), olderTime);
}
// Attempt to get token after it expired
{
clientSecretCredential.IsExpired = true;
auto const tokenReceived
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
EXPECT_EQ(tokenReceived, newToken);
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
clientSecretCredential.IsExpired = false;
}
// Setting the very same scopes set earlier does not reset token
{
std::string const scopesCopy
= scopes.substr(0, scopes.length() / 2) + scopes.substr(scopes.length() / 2);
{
auto const scopesPtr = scopes.c_str();
auto const scopesCopyPtr = scopesCopy.c_str();
EXPECT_NE(scopesPtr, scopesCopyPtr);
EXPECT_EQ(scopes, scopesCopy);
}
Credentials::Credential::Internal::SetScopes(clientSecretCredential, scopesCopy);
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
auto const tokenReceived
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
EXPECT_EQ(tokenReceived, newToken);
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
}
// Updating scopes does reset the token
{
clientSecretCredential.IsExpired = false;
std::string const anotherScopes = "anotherScopes";
std::string const anotherToken = "anotherToken";
auto const anotherTime = newTime + std::chrono::minutes(10);
clientSecretCredential.NewTokenString = anotherToken;
clientSecretCredential.NewExpiration = anotherTime;
auto tokenReceived
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
EXPECT_EQ(tokenReceived, newToken);
EXPECT_EQ(clientSecretCredential.GetTokenString(), newToken);
EXPECT_EQ(clientSecretCredential.GetScopes(), scopes);
EXPECT_EQ(clientSecretCredential.GetExpiration(), newTime);
Credentials::Credential::Internal::SetScopes(
clientSecretCredential, std::string(anotherScopes));
EXPECT_EQ(clientSecretCredential.GetTenantId(), tenantId);
EXPECT_EQ(clientSecretCredential.GetClientId(), clientId);
EXPECT_EQ(clientSecretCredential.GetClientSecret(), clientSecret);
EXPECT_EQ(clientSecretCredential.GetScopes(), anotherScopes);
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), true);
tokenReceived
= Credentials::TokenCredential::Internal::GetToken(clientSecretCredential);
EXPECT_EQ(clientSecretCredential.IsTokenPtrNull(), false);
EXPECT_EQ(tokenReceived, anotherToken);
EXPECT_EQ(clientSecretCredential.GetTokenString(), anotherToken);
EXPECT_EQ(clientSecretCredential.GetScopes(), anotherScopes);
EXPECT_EQ(clientSecretCredential.GetExpiration(), anotherTime);
}
}
}
}
}
}

View File

@ -1,29 +1,30 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: MIT
cmake_minimum_required (VERSION 3.15)
add_executable (
azure-storage-test
test_base.hpp
test_base.cpp
blob_service_client_test.cpp
blob_container_client_test.hpp
blob_container_client_test.cpp
block_blob_client_test.hpp
block_blob_client_test.cpp
append_blob_client_test.hpp
append_blob_client_test.cpp
page_blob_client_test.hpp
page_blob_client_test.cpp
main.cpp
)
target_link_libraries(azure-storage-test PRIVATE azure-storage)
if (MSVC)
target_compile_options(azure-storage-test PRIVATE /wd6326 /wd26495 /wd26812)
endif()
add_gtest(azure-storage-test)
### https://github.com/Azure/azure-sdk-for-cpp/issues/224 ###
#cmake_minimum_required (VERSION 3.15)
#
#add_executable (
# azure-storage-test
# test_base.hpp
# test_base.cpp
# blob_service_client_test.cpp
# blob_container_client_test.hpp
# blob_container_client_test.cpp
# block_blob_client_test.hpp
# block_blob_client_test.cpp
# append_blob_client_test.hpp
# append_blob_client_test.cpp
# page_blob_client_test.hpp
# page_blob_client_test.cpp
# main.cpp
#)
#
#target_link_libraries(azure-storage-test PRIVATE azure-storage)
#
#if (MSVC)
# target_compile_options(azure-storage-test PRIVATE /wd6326 /wd26495 /wd26812)
#endif()
#
#add_gtest(azure-storage-test)