From 0b777603dcfad9d6e65893ea6ec28cba27be02d3 Mon Sep 17 00:00:00 2001 From: Mykhailo Kuchma Date: Sat, 6 Jun 2020 12:59:46 +0300 Subject: [PATCH] Move the authorization header function to utils This is needed to enable testing and enabled new code to reuse it. Adapt function to coding style, switch from string concatenation to reusable string stream. Remove a needless auth namespace. Resolves: OLPEDGE-2030 Signed-off-by: Mykhailo Kuchma --- .../src/AuthenticationClientImpl.cpp | 81 +++---------- .../src/AuthenticationClientImpl.h | 6 - .../src/AuthenticationClientUtils.cpp | 113 +++++++++++++++--- .../src/AuthenticationClientUtils.h | 15 +++ .../tests/AuthenticationClientTest.cpp | 21 +++- 5 files changed, 142 insertions(+), 94 deletions(-) diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp index 77dd3055d..3c269c4d9 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp @@ -32,7 +32,6 @@ #include #include "AuthenticationClientUtils.h" #include "Constants.h" -#include "Crypto.h" #include "SignInResultImpl.h" #include "SignInUserResultImpl.h" #include "SignOutResultImpl.h" @@ -43,31 +42,14 @@ #include "olp/core/http/NetworkUtils.h" #include "olp/core/logging/Log.h" #include "olp/core/thread/TaskScheduler.h" -#include "olp/core/utils/Base64.h" -#include "olp/core/utils/Url.h" namespace { -namespace auth = olp::authentication; namespace client = olp::client; using olp::authentication::Constants; -// Helper characters -constexpr auto kParamAdd = "&"; -constexpr auto kParamComma = ","; -constexpr auto kParamEquals = "="; -constexpr auto kParamQuote = "\""; -constexpr auto kLineFeed = '\n'; - // Tags constexpr auto kApplicationJson = "application/json"; -const std::string kOauthPost = "POST"; -const std::string kOauthConsumerKey = "oauth_consumer_key"; -const std::string kOauthNonce = "oauth_nonce"; -const std::string kOauthSignature = "oauth_signature"; -const std::string kOauthTimestamp = "oauth_timestamp"; -const std::string kOauthVersion = "oauth_version"; -const std::string kOauthSignatureMethod = "oauth_signature_method"; const std::string kOauthEndpoint = "/oauth2/token"; const std::string kSignoutEndpoint = "/logout"; const std::string kTermsEndpoint = "/terms"; @@ -106,8 +88,6 @@ constexpr auto kResource = "resource"; constexpr auto kDiagnostics = "diagnostics"; constexpr auto kOperator = "operator"; // Values -constexpr auto kVersion = "1.0"; -constexpr auto kHmac = "HMAC-SHA256"; constexpr auto kErrorWrongTimestamp = 401204; constexpr auto kLogTag = "AuthenticationClient"; @@ -135,9 +115,11 @@ olp::client::HttpResponse AuthenticationClientImpl::CallAuth( const SignInProperties& properties, std::time_t timestamp) { const auto url = settings_.token_endpoint_url + kOauthEndpoint; + auto auth_header = + GenerateAuthorizationHeader(credentials, url, timestamp, GenerateUid()); + client::OlpClient::ParametersType headers = { - {http::kAuthorizationHeader, - GenerateHeader(credentials, url, timestamp)}}; + {http::kAuthorizationHeader, std::move(auth_header)}}; if (context.IsCancelled()) { return {static_cast(olp::http::ErrorCode::CANCELLED_ERROR), "Cancelled"}; @@ -354,8 +336,11 @@ client::CancellationToken AuthenticationClientImpl::HandleUserRequest( network_settings.WithProxySettings(settings_.network_proxy_settings.get()); } request.WithVerb(http::NetworkRequest::HttpVerb::POST); - request.WithHeader(http::kAuthorizationHeader, - GenerateHeader(credentials, url)); + + auto auth_header = GenerateAuthorizationHeader( + credentials, url, std::time(nullptr), GenerateUid()); + + request.WithHeader(http::kAuthorizationHeader, std::move(auth_header)); request.WithHeader(http::kContentTypeHeader, kApplicationJson); request.WithHeader(http::kUserAgentHeader, http::kOlpSdkUserAgent); request.WithSettings(std::move(network_settings)); @@ -465,8 +450,11 @@ client::CancellationToken AuthenticationClientImpl::SignUpHereUser( network_settings.WithProxySettings(settings_.network_proxy_settings.get()); } request.WithVerb(http::NetworkRequest::HttpVerb::POST); - request.WithHeader(http::kAuthorizationHeader, - GenerateHeader(credentials, url)); + + auto auth_header = GenerateAuthorizationHeader( + credentials, url, std::time(nullptr), GenerateUid()); + + request.WithHeader(http::kAuthorizationHeader, std::move(auth_header)); request.WithHeader(http::kContentTypeHeader, kApplicationJson); request.WithHeader(http::kUserAgentHeader, http::kOlpSdkUserAgent); request.WithSettings(std::move(network_settings)); @@ -695,47 +683,6 @@ client::CancellationToken AuthenticationClientImpl::Authorize( std::move(callback)); } -std::string AuthenticationClientImpl::Base64Encode( - const std::vector& vector) { - std::string ret = olp::utils::Base64Encode(vector); - // Base64 encode sometimes return multiline with garbage at the end - if (!ret.empty()) { - auto loc = ret.find(kLineFeed); - if (loc != std::string::npos) ret = ret.substr(0, loc); - } - return ret; -} - -std::string AuthenticationClientImpl::GenerateHeader( - const AuthenticationCredentials& credentials, const std::string& url, - const time_t& timestamp) { - std::string uid = GenerateUid(); - const std::string currentTime = std::to_string(timestamp); - const std::string encodedUri = utils::Url::Encode(url); - const std::string encodedQuery = utils::Url::Encode( - kOauthConsumerKey + kParamEquals + credentials.GetKey() + kParamAdd + - kOauthNonce + kParamEquals + uid + kParamAdd + kOauthSignatureMethod + - kParamEquals + kHmac + kParamAdd + kOauthTimestamp + kParamEquals + - currentTime + kParamAdd + kOauthVersion + kParamEquals + kVersion); - const std::string signatureBase = - kOauthPost + kParamAdd + encodedUri + kParamAdd + encodedQuery; - const std::string encodeKey = credentials.GetSecret() + kParamAdd; - auto hmacResult = Crypto::hmac_sha256(encodeKey, signatureBase); - auto signature = Base64Encode(hmacResult); - std::string authorization = - "OAuth " + kOauthConsumerKey + kParamEquals + kParamQuote + - utils::Url::Encode(credentials.GetKey()) + kParamQuote + kParamComma + - kOauthNonce + kParamEquals + kParamQuote + utils::Url::Encode(uid) + - kParamQuote + kParamComma + kOauthSignatureMethod + kParamEquals + - kParamQuote + kHmac + kParamQuote + kParamComma + kOauthTimestamp + - kParamEquals + kParamQuote + utils::Url::Encode(currentTime) + - kParamQuote + kParamComma + kOauthVersion + kParamEquals + kParamQuote + - kVersion + kParamQuote + kParamComma + kOauthSignature + kParamEquals + - kParamQuote + utils::Url::Encode(signature) + kParamQuote; - - return authorization; -} - std::string AuthenticationClientImpl::GenerateBearerHeader( const std::string& bearer_token) { std::string authorization = http::kBearer + std::string(" "); diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h index a14661947..e5a1f7df4 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h @@ -126,12 +126,6 @@ class AuthenticationClientImpl final { static TimeResponse ParseTimeResponse(std::stringstream& payload); - std::string Base64Encode(const std::vector& vector); - - std::string GenerateHeader(const AuthenticationCredentials& credentials, - const std::string& url, - const time_t& timestamp = std::time(nullptr)); - std::string GenerateBearerHeader(const std::string& bearer_token); client::OlpClient::RequestBodyType GenerateClientBody( diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp index 8367c80a9..e2904aeb7 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp @@ -19,21 +19,54 @@ #include "AuthenticationClientUtils.h" +#include +#include +#include + #include #include #include #include -#include -#include -#include - #include "Constants.h" +#include "Crypto.h" #include "olp/core/http/NetworkUtils.h" +#include "olp/core/utils/Base64.h" +#include "olp/core/utils/Url.h" namespace olp { namespace authentication { -namespace auth = olp::authentication; + +namespace { +// Helper characters +constexpr auto kParamAdd = "&"; +constexpr auto kParamComma = ","; +constexpr auto kParamEquals = "="; +constexpr auto kParamQuote = "\""; +constexpr auto kLineFeed = '\n'; + +constexpr auto kOauthPost = "POST"; +constexpr auto kOauthVersion = "oauth_version"; +constexpr auto kOauthConsumerKey = "oauth_consumer_key"; +constexpr auto kOauthNonce = "oauth_nonce"; +constexpr auto kOauthSignature = "oauth_signature"; +constexpr auto kOauthTimestamp = "oauth_timestamp"; +constexpr auto kOauthSignatureMethod = "oauth_signature_method"; +constexpr auto kVersion = "1.0"; +constexpr auto kHmac = "HMAC-SHA256"; + +std::string Base64Encode(const std::vector& vector) { + std::string ret = olp::utils::Base64Encode(vector); + // Base64 encode sometimes return multiline with garbage at the end + if (!ret.empty()) { + auto loc = ret.find(kLineFeed); + if (loc != std::string::npos) ret = ret.substr(0, loc); + } + return ret; +} + +} // namespace + namespace client = olp::client; constexpr auto kDate = "date"; @@ -75,9 +108,8 @@ void ExecuteOrSchedule( task_scheduler->ScheduleTask(std::move(func)); } -auth::IntrospectAppResult GetIntrospectAppResult( - const rapidjson::Document& doc) { - auth::IntrospectAppResult result; +IntrospectAppResult GetIntrospectAppResult(const rapidjson::Document& doc) { + IntrospectAppResult result; if (doc.HasMember(Constants::CLIENT_ID)) { result.SetClientId(doc[Constants::CLIENT_ID].GetString()); } @@ -161,27 +193,27 @@ auth::IntrospectAppResult GetIntrospectAppResult( return result; } -auth::DecisionType GetPermission(const std::string& str) { - return (str.compare("allow") == 0) ? auth::DecisionType::kAllow - : auth::DecisionType::kDeny; +DecisionType GetPermission(const std::string& str) { + return (str.compare("allow") == 0) ? DecisionType::kAllow + : DecisionType::kDeny; } -std::vector GetDiagnostics(rapidjson::Document& doc) { - std::vector results; +std::vector GetDiagnostics(rapidjson::Document& doc) { + std::vector results; const auto& array = doc[Constants::DIAGNOSTICS].GetArray(); for (auto& element : array) { - auth::ActionResult action; + ActionResult action; if (element.HasMember(Constants::DECISION)) { action.SetDecision( GetPermission(element[Constants::DECISION].GetString())); // get permissions if avialible if (element.HasMember(Constants::PERMISSIONS) && element[Constants::PERMISSIONS].IsArray()) { - std::vector permissions; + std::vector permissions; const auto& permissions_array = element[Constants::PERMISSIONS].GetArray(); for (auto& permission_element : permissions_array) { - auth::ActionResult::Permissions permission; + ActionResult::Permissions permission; if (permission_element.HasMember(Constants::ACTION)) { permission.first = permission_element[Constants::ACTION].GetString(); @@ -201,8 +233,8 @@ std::vector GetDiagnostics(rapidjson::Document& doc) { return results; } -auth::AuthorizeResult GetAuthorizeResult(rapidjson::Document& doc) { - auth::AuthorizeResult result; +AuthorizeResult GetAuthorizeResult(rapidjson::Document& doc) { + AuthorizeResult result; if (doc.HasMember(Constants::IDENTITY)) { auto uris = doc[Constants::IDENTITY].GetObject(); @@ -227,7 +259,7 @@ auth::AuthorizeResult GetAuthorizeResult(rapidjson::Document& doc) { } client::OlpClient CreateOlpClient( - const auth::AuthenticationSettings& auth_settings, + const AuthenticationSettings& auth_settings, boost::optional authentication_settings) { client::OlpClientSettings settings; settings.network_request_handler = auth_settings.network_request_handler; @@ -242,5 +274,48 @@ client::OlpClient CreateOlpClient( return client; } +std::string GenerateAuthorizationHeader( + const AuthenticationCredentials& credentials, const std::string& url, + time_t timestamp, std::string nonce) { + const std::string timestamp_str = std::to_string(timestamp); + + std::stringstream stream; + + stream << kOauthConsumerKey << kParamEquals << credentials.GetKey() + << kParamAdd << kOauthNonce << kParamEquals << nonce << kParamAdd + << kOauthSignatureMethod << kParamEquals << kHmac << kParamAdd + << kOauthTimestamp << kParamEquals << timestamp_str << kParamAdd + << kOauthVersion << kParamEquals << kVersion; + + const auto encoded_query = utils::Url::Encode(stream.str()); + + stream.clear(); + + stream << kOauthPost << kParamAdd << utils::Url::Encode(url) << kParamAdd + << encoded_query; + + const auto signature_base = stream.str(); + + stream.clear(); + + const std::string encode_key = credentials.GetSecret() + kParamAdd; + auto hmac_result = Crypto::hmac_sha256(encode_key, signature_base); + auto signature = Base64Encode(hmac_result); + + stream << "OAuth " << kOauthConsumerKey << kParamEquals << kParamQuote + << utils::Url::Encode(credentials.GetKey()) << kParamQuote + << kParamComma << kOauthNonce << kParamEquals << kParamQuote + << utils::Url::Encode(nonce) << kParamQuote << kParamComma + << kOauthSignatureMethod << kParamEquals << kParamQuote << kHmac + << kParamQuote << kParamComma << kOauthTimestamp << kParamEquals + << kParamQuote << utils::Url::Encode(timestamp_str) << kParamQuote + << kParamComma << kOauthVersion << kParamEquals << kParamQuote + << kVersion << kParamQuote << kParamComma << kOauthSignature + << kParamEquals << kParamQuote << utils::Url::Encode(signature) + << kParamQuote; + + return stream.str(); +} + } // namespace authentication } // namespace olp diff --git a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h index 843af53ea..da1a1929c 100644 --- a/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h +++ b/olp-cpp-sdk-authentication/src/AuthenticationClientUtils.h @@ -25,6 +25,7 @@ #include +#include "olp/authentication/AuthenticationCredentials.h" #include "olp/authentication/AuthenticationSettings.h" #include "olp/authentication/AuthorizeResult.h" #include "olp/authentication/ErrorResponse.h" @@ -129,5 +130,19 @@ client::OlpClient CreateOlpClient( const AuthenticationSettings& auth_settings, boost::optional authentication_settings); +/* + * @brief Generate authorization header. + * + * @param credentials Client credentials. + * @param url Authorization endpoint URL. + * @param timestamp Current time. + * @param nonce A unique value, must be used once. (Refer to OAuth docs). + * + * @return The authorization header string. + */ +std::string GenerateAuthorizationHeader( + const AuthenticationCredentials& credentials, const std::string& url, + time_t timestamp, std::string nonce); + } // namespace authentication } // namespace olp diff --git a/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp b/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp index ff2658a74..5eaa8ba99 100644 --- a/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp +++ b/olp-cpp-sdk-authentication/tests/AuthenticationClientTest.cpp @@ -28,10 +28,27 @@ namespace { constexpr auto kTime = "Fri, 29 May 2020 11:07:45 GMT"; } // namespace +namespace auth = olp::authentication; + TEST(AuthenticationClientTest, TimeParsing) { { SCOPED_TRACE("Parse time"); - - EXPECT_EQ(olp::authentication::ParseTime(kTime), 1590750465); + EXPECT_EQ(auth::ParseTime(kTime), 1590750465); } } + +TEST(AuthenticationClientTest, GenerateAuthorizationHeader) { + auth::AuthenticationCredentials credentials("key", "secret"); + const auto url = "https://auth.server.com"; + auto sig = auth::GenerateAuthorizationHeader(credentials, url, 0, "unique"); + auto expected_sig = + "oauth_consumer_key=key&oauth_nonce=unique&oauth_signature_method=HMAC-" + "SHA256&oauth_timestamp=0&oauth_version=1.0POST&https%3A%2F%2Fauth." + "server.com&oauth_consumer_key%3Dkey%26oauth_nonce%3Dunique%26oauth_" + "signature_method%3DHMAC-SHA256%26oauth_timestamp%3D0%26oauth_version%" + "3D1.0OAuth " + "oauth_consumer_key=\"key\",oauth_nonce=\"unique\",oauth_signature_" + "method=\"HMAC-SHA256\",oauth_timestamp=\"0\",oauth_version=\"1.0\"," + "oauth_signature=\"ncwRtcqRSM04FIFch8Ay4l7bRmp96lifuHEops4AqEw%3D\""; + EXPECT_EQ(sig, expected_sig); +}