From 9631e5978aabf93129497c05db24f5d9265e6184 Mon Sep 17 00:00:00 2001 From: Anton Kolesnyk <41349689+antkmsft@users.noreply.github.com> Date: Wed, 1 Apr 2020 15:51:24 -0700 Subject: [PATCH] Add credential classes (#42) --- sdk/core/azure-core/CMakeLists.txt | 1 + .../inc/credentials/credentials.hpp | 104 ++++++++++++++++++ .../inc/internal/credentials_internal.hpp | 62 +++++++++++ .../src/credentials/credentials.cpp | 34 ++++++ sdk/core/azure-core/test/main.cpp | 100 ++++++++++++++++- 5 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 sdk/core/azure-core/inc/credentials/credentials.hpp create mode 100644 sdk/core/azure-core/inc/internal/credentials_internal.hpp create mode 100644 sdk/core/azure-core/src/credentials/credentials.cpp diff --git a/sdk/core/azure-core/CMakeLists.txt b/sdk/core/azure-core/CMakeLists.txt index d30470842..95e821f6e 100644 --- a/sdk/core/azure-core/CMakeLists.txt +++ b/sdk/core/azure-core/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) add_library ( azure-core + src/credentials/credentials src/http/http src/http/request ) diff --git a/sdk/core/azure-core/inc/credentials/credentials.hpp b/sdk/core/azure-core/inc/credentials/credentials.hpp new file mode 100644 index 000000000..b516c812d --- /dev/null +++ b/sdk/core/azure-core/inc/credentials/credentials.hpp @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +namespace azure +{ +namespace core +{ +namespace credentials +{ + +class Credential +{ + virtual void SetScopes(std::string const& scopes) { (void)scopes; } + +public: + class Internal; + virtual ~Credential() noexcept = default; + +protected: + Credential() = default; + + Credential(Credential const& other) = default; + Credential& operator=(Credential const& other) = default; +}; + +class TokenCredential : public Credential +{ + struct Token + { + public: + std::string Scopes; + std::string TokenString; + std::chrono::system_clock::time_point ExpiresAt; + }; + + Token m_token; + mutable std::mutex m_tokenMutex; + + void SetScopes(std::string const& scopes) override; + + Token GetToken() const; + + void SetToken( + std::string const& tokenString, + std::chrono::system_clock::time_point const& expiresAt); + + void SetToken(Token const& token); + +public: + class Internal; + +protected: + TokenCredential() = default; + + TokenCredential(TokenCredential const& other) : Credential(other), m_token(other.GetToken()) {} + + TokenCredential& operator=(TokenCredential const& other) + { + this->SetToken(other.GetToken()); + return *this; + } +}; + +class ClientSecretCredential : public TokenCredential +{ + std::string m_tenantId; + std::string m_clientId; + std::string m_clientSecret; + +public: + class Internal; + + ClientSecretCredential( + std::string const& tenantId, + std::string const& clientId, + std::string const& clientSecret) + : m_tenantId(tenantId), m_clientId(clientId), m_clientSecret(clientSecret) + { + } + + ClientSecretCredential(ClientSecretCredential const& other) + : TokenCredential(other), m_tenantId(other.m_tenantId), m_clientId(other.m_clientId), + m_clientSecret(other.m_clientSecret) + { + } + + ClientSecretCredential& operator=(ClientSecretCredential const& other) + { + this->m_tenantId = other.m_tenantId; + this->m_clientId = other.m_clientId; + this->m_clientSecret = other.m_clientSecret; + return *this; + } +}; + +} // namespace credentials +} // namespace core +} // namespace azure diff --git a/sdk/core/azure-core/inc/internal/credentials_internal.hpp b/sdk/core/azure-core/inc/internal/credentials_internal.hpp new file mode 100644 index 000000000..ef868a2aa --- /dev/null +++ b/sdk/core/azure-core/inc/internal/credentials_internal.hpp @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +namespace azure +{ +namespace core +{ +namespace credentials +{ + +class Credential::Internal +{ +public: + static void SetScopes(Credential& credential, std::string const& scopes) + { + credential.SetScopes(scopes); + } +}; + +class TokenCredential::Internal +{ +public: + static Token GetToken(TokenCredential const& credential) + { + return credential.GetToken(); + } + + static void SetToken( + TokenCredential& credential, + std::string const& token, + std::chrono::system_clock::time_point const& expiration) + { + credential.SetToken(token, expiration); + } +}; + +class ClientSecretCredential::Internal +{ +public: + static std::string const& GetTenantId(ClientSecretCredential const& credential) + { + return credential.m_tenantId; + } + + static std::string const& GetClientId(ClientSecretCredential const& credential) + { + return credential.m_clientId; + } + + static std::string const& GetClientSecret(ClientSecretCredential const& credential) + { + return credential.m_clientSecret; + } +}; + +} // namespace credentials +} // namespace core +} // namespace azure diff --git a/sdk/core/azure-core/src/credentials/credentials.cpp b/sdk/core/azure-core/src/credentials/credentials.cpp new file mode 100644 index 000000000..5364e1494 --- /dev/null +++ b/sdk/core/azure-core/src/credentials/credentials.cpp @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include + +using namespace azure::core::credentials; + +void TokenCredential::SetScopes(std::string const& scopes) +{ + std::lock_guard const lock(this->m_tokenMutex); + if (this->m_token.Scopes != scopes) + { + this->m_token = { scopes, {}, {} }; + } +} + +TokenCredential::Token TokenCredential::GetToken() const +{ + std::lock_guard const lock(this->m_tokenMutex); + return this->m_token; +} + +void TokenCredential::SetToken( + std::string const& tokenString, + std::chrono::system_clock::time_point const& expiresAt) +{ + this->SetToken({ this->m_token.Scopes, tokenString, expiresAt }); +} + +void TokenCredential::SetToken(Token const& token) +{ + std::lock_guard const lock(this->m_tokenMutex); + this->m_token = token; +} diff --git a/sdk/core/azure-core/test/main.cpp b/sdk/core/azure-core/test/main.cpp index c79bb7469..c51ec3a16 100644 --- a/sdk/core/azure-core/test/main.cpp +++ b/sdk/core/azure-core/test/main.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "gtest/gtest.h" @@ -148,4 +149,101 @@ TEST(Http_Request, add_path) [](std::string a, std::string b) { return a == b; }, req.getEncodedUrl(), url + "/path/path2/path3?query=value"); -} \ No newline at end of file +} + +TEST(Credential, ClientSecretCredential) +{ + // Client Secret credential properties + credentials::ClientSecretCredential clientSecretCredential( + "tenantId", "clientId", "clientSecret"); + + EXPECT_EQ( + credentials::ClientSecretCredential::Internal::GetTenantId(clientSecretCredential), + "tenantId"); + + EXPECT_EQ( + credentials::ClientSecretCredential::Internal::GetClientId(clientSecretCredential), + "clientId"); + + EXPECT_EQ( + credentials::ClientSecretCredential::Internal::GetClientSecret(clientSecretCredential), + "clientSecret"); + + // Token credential + { + auto const emptyString = std::string(); + auto const defaultTime = std::chrono::system_clock::time_point(); + { + // Default values + { + auto const initialToken + = credentials::TokenCredential::Internal::GetToken(clientSecretCredential); + + EXPECT_EQ(initialToken.TokenString, emptyString); + EXPECT_EQ(initialToken.Scopes, emptyString); + EXPECT_EQ(initialToken.ExpiresAt, defaultTime); + } + + { + // Set scopes + std::string const scopes = "scope"; + { + credentials::Credential::Internal::SetScopes(clientSecretCredential, scopes); + + auto const scopedToken + = credentials::TokenCredential::Internal::GetToken(clientSecretCredential); + + EXPECT_EQ(scopedToken.TokenString, emptyString); + EXPECT_EQ(scopedToken.Scopes, scopes); + EXPECT_EQ(scopedToken.ExpiresAt, defaultTime); + } + + // Set token + { + std::string const token = "token"; + auto const recentTime = std::chrono::system_clock::now(); + + { + credentials::TokenCredential::Internal::SetToken( + clientSecretCredential, token, recentTime); + + auto const refreshedToken + = credentials::TokenCredential::Internal::GetToken(clientSecretCredential); + + EXPECT_EQ(refreshedToken.TokenString, token); + EXPECT_EQ(refreshedToken.Scopes, scopes); + EXPECT_EQ(refreshedToken.ExpiresAt, recentTime); + } + + // Setting the very same scopes set earlier does not reset token + { + credentials::Credential::Internal::SetScopes( + clientSecretCredential, std::string(scopes)); + + auto const rescopedToken + = credentials::TokenCredential::Internal::GetToken(clientSecretCredential); + + EXPECT_EQ(rescopedToken.TokenString, token); + EXPECT_EQ(rescopedToken.Scopes, scopes); + EXPECT_EQ(rescopedToken.ExpiresAt, recentTime); + } + } + } + + // Updating scopes does reset the token + { + std::string const another_scopes = "another_scopes"; + + credentials::Credential::Internal::SetScopes( + clientSecretCredential, std::string(another_scopes)); + + auto const resetToken + = credentials::TokenCredential::Internal::GetToken(clientSecretCredential); + + EXPECT_EQ(resetToken.TokenString, emptyString); + EXPECT_EQ(resetToken.Scopes, another_scopes); + EXPECT_EQ(resetToken.ExpiresAt, defaultTime); + } + } + } +}