Remake ClientSecretCredential (#212)
This commit is contained in:
parent
0cfcdca725
commit
ad2e63afe1
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
69
sdk/core/azure-core/inc/credentials/policy/policies.hpp
Normal file
69
sdk/core/azure-core/inc/credentials/policy/policies.hpp
Normal 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
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
25
sdk/core/azure-core/src/credentials/policy/policies.cpp
Normal file
25
sdk/core/azure-core/src/credentials/policy/policies.cpp
Normal 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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user