Skip to content

Commit fc0ea4a

Browse files
Added GetMyAccount API to AuthenticationClient (#1115)
GetMyAccount API will retrieve information about user: firstname, lastname, email, phone number, etc. Functional and integration tests added too. Relates-To: OLPEDGE-2385 Signed-off-by: Andrey Kashcheev <[email protected]>
1 parent 7013a31 commit fc0ea4a

File tree

15 files changed

+532
-113
lines changed

15 files changed

+532
-113
lines changed

olp-cpp-sdk-authentication/.clang-format

Lines changed: 0 additions & 1 deletion
This file was deleted.

olp-cpp-sdk-authentication/include/olp/authentication/AuthenticationClient.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,30 @@ class AUTHENTICATION_API AuthenticationClient {
568568
AuthorizeRequest request,
569569
AuthorizeCallback callback);
570570

571+
/**
572+
* @brief Retrieves the account information associated with the user access
573+
* token.
574+
*
575+
* @note The user information will be filtered based on the scopes in
576+
* the token:
577+
* email - 'email', 'emailVerified', 'recoveryEmail';
578+
* openid - 'userId', 'state';
579+
* phone - 'phoneNumber', 'phoneNumberVerified';
580+
* profile - 'realm', 'facebookId', 'firstname', 'lastname', 'dob',
581+
* 'language', 'countryCode', 'marketingEnabled', 'createdTime',
582+
* 'updatedTime'.
583+
*
584+
* @param access_token A valid access token that is associated with the
585+
* user.
586+
* @param callback The`UserAccountInfoCallback` method that is called when
587+
* the user information request is completed.
588+
*
589+
* @return The `CancellationToken` instance that can be used to cancel
590+
* the request.
591+
*/
592+
client::CancellationToken GetMyAccount(std::string access_token,
593+
UserAccountInfoCallback callback);
594+
571595
private:
572596
std::unique_ptr<AuthenticationClientImpl> impl_;
573597
};

olp-cpp-sdk-authentication/include/olp/authentication/Types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <olp/authentication/AuthenticationError.h>
2525
#include <olp/authentication/AuthorizeResult.h>
2626
#include <olp/authentication/IntrospectAppResult.h>
27+
#include <olp/authentication/UserAccountInfoResponse.h>
2728
#include <olp/core/client/ApiError.h>
2829
#include <olp/core/client/ApiResponse.h>
2930

@@ -49,5 +50,11 @@ using AuthorizeResponse = Response<AuthorizeResult>;
4950

5051
/// Called when the user decision request is completed.
5152
using AuthorizeCallback = Callback<AuthorizeResult>;
53+
54+
/// The UserAccountInfoResponse response alias.
55+
using UserAccountInfoResponse = Response<model::UserAccountInfoResponse>;
56+
57+
/// Called when the get user account request is completed.
58+
using UserAccountInfoCallback = Callback<model::UserAccountInfoResponse>;
5259
} // namespace authentication
5360
} // namespace olp

olp-cpp-sdk-authentication/include/olp/authentication/UserAccountInfoResponse.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
namespace olp {
2525
namespace authentication {
26+
namespace model {
2627
/**
2728
* @brief An account information.
2829
*/
@@ -358,5 +359,6 @@ class AUTHENTICATION_API UserAccountInfoResponse {
358359
std::string hrn_;
359360
std::string account_type_;
360361
};
362+
} // namespace model
361363
} // namespace authentication
362364
} // namespace olp

olp-cpp-sdk-authentication/src/AuthenticationClient.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,10 @@ client::CancellationToken AuthenticationClient::Authorize(
119119
std::move(callback));
120120
}
121121

122+
client::CancellationToken AuthenticationClient::GetMyAccount(
123+
std::string access_token, UserAccountInfoCallback callback) {
124+
return impl_->GetMyAccount(std::move(access_token), std::move(callback));
125+
}
126+
122127
} // namespace authentication
123128
} // namespace olp

olp-cpp-sdk-authentication/src/AuthenticationClientImpl.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const std::string kOauthEndpoint = "/oauth2/token";
5454
const std::string kSignoutEndpoint = "/logout";
5555
const std::string kTermsEndpoint = "/terms";
5656
const std::string kUserEndpoint = "/user";
57+
const std::string kMyAccountEndpoint = "/user/me";
5758
constexpr auto kTimestampEndpoint = "/timestamp";
5859
constexpr auto kIntrospectAppEndpoint = "/app/me";
5960
constexpr auto kDecisionEndpoint = "/decision/authorize";
@@ -683,6 +684,30 @@ client::CancellationToken AuthenticationClientImpl::Authorize(
683684
std::move(callback));
684685
}
685686

687+
client::CancellationToken AuthenticationClientImpl::GetMyAccount(
688+
std::string access_token, UserAccountInfoCallback callback) {
689+
auto task =
690+
[=](client::CancellationContext context) -> UserAccountInfoResponse {
691+
if (!settings_.network_request_handler) {
692+
return client::ApiError({static_cast<int>(http::ErrorCode::IO_ERROR),
693+
"Can not send request while offline"});
694+
}
695+
696+
client::AuthenticationSettings auth_settings;
697+
auth_settings.provider = [&access_token]() { return access_token; };
698+
699+
client::OlpClient client = CreateOlpClient(settings_, auth_settings);
700+
701+
auto http_result =
702+
client.CallApi(kMyAccountEndpoint, "GET", {}, {}, {}, {}, {}, context);
703+
704+
return GetUserAccountInfoResponse(http_result);
705+
};
706+
707+
return AddTask(settings_.task_scheduler, pending_requests_, std::move(task),
708+
std::move(callback));
709+
}
710+
686711
std::string AuthenticationClientImpl::GenerateBearerHeader(
687712
const std::string& bearer_token) {
688713
std::string authorization = http::kBearer + std::string(" ");

olp-cpp-sdk-authentication/src/AuthenticationClientImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ class AuthenticationClientImpl final {
120120
AuthorizeRequest request,
121121
AuthorizeCallback callback);
122122

123+
client::CancellationToken GetMyAccount(std::string access_token,
124+
UserAccountInfoCallback callback);
125+
123126
private:
124127
TimeResponse GetTimeFromServer(client::CancellationContext context,
125128
const client::OlpClient& client);

olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp

Lines changed: 99 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
#include <iomanip>
2424
#include <sstream>
2525

26+
#include <olp/authentication/Crypto.h>
2627
#include <rapidjson/document.h>
2728
#include <rapidjson/istreamwrapper.h>
2829
#include <rapidjson/stringbuffer.h>
2930
#include <rapidjson/writer.h>
30-
3131
#include "Constants.h"
32-
#include <olp/authentication/Crypto.h>
32+
#include "ResponseFromJsonBuilder.h"
33+
#include "olp/core/http/NetworkResponse.h"
3334
#include "olp/core/http/NetworkUtils.h"
3435
#include "olp/core/utils/Base64.h"
3536
#include "olp/core/utils/Url.h"
@@ -60,11 +61,33 @@ std::string Base64Encode(const Crypto::Sha256Digest& digest) {
6061
// Base64 encode sometimes return multiline with garbage at the end
6162
if (!ret.empty()) {
6263
auto loc = ret.find(kLineFeed);
63-
if (loc != std::string::npos) ret = ret.substr(0, loc);
64+
if (loc != std::string::npos) {
65+
ret = ret.substr(0, loc);
66+
}
6467
}
6568
return ret;
6669
}
6770

71+
Response<rapidjson::Document> Parse(client::HttpResponse& http_response) {
72+
rapidjson::IStreamWrapper stream(http_response.response);
73+
rapidjson::Document document;
74+
document.ParseStream(stream);
75+
if (http_response.status != http::HttpStatusCode::OK) {
76+
// HttpResult response can be error message or valid json with it.
77+
std::string msg = http_response.response.str();
78+
if (!document.HasParseError() && document.HasMember(Constants::MESSAGE)) {
79+
msg = document[Constants::MESSAGE].GetString();
80+
}
81+
return client::ApiError({http_response.status, msg});
82+
}
83+
84+
if (document.HasParseError()) {
85+
return client::ApiError({static_cast<int>(http::ErrorCode::UNKNOWN_ERROR),
86+
"Failed to parse response"});
87+
}
88+
89+
return std::move(document);
90+
}
6891
} // namespace
6992

7093
namespace client = olp::client;
@@ -109,88 +132,29 @@ void ExecuteOrSchedule(
109132
}
110133

111134
IntrospectAppResult GetIntrospectAppResult(const rapidjson::Document& doc) {
112-
IntrospectAppResult result;
113-
if (doc.HasMember(Constants::CLIENT_ID)) {
114-
result.SetClientId(doc[Constants::CLIENT_ID].GetString());
115-
}
116-
if (doc.HasMember(Constants::NAME)) {
117-
result.SetName(doc[Constants::NAME].GetString());
118-
}
119-
if (doc.HasMember(Constants::DESCRIPTION)) {
120-
result.SetDescription(doc[Constants::DESCRIPTION].GetString());
121-
}
122-
if (doc.HasMember(Constants::REDIRECT_URIS)) {
123-
auto uris = doc[Constants::REDIRECT_URIS].GetArray();
124-
std::vector<std::string> value_array;
125-
value_array.reserve(uris.Size());
126-
for (auto& value : uris) {
127-
value_array.push_back(value.GetString());
128-
}
129-
result.SetReditectUris(std::move(value_array));
130-
}
131-
if (doc.HasMember(Constants::ALLOWED_SCOPES)) {
132-
auto scopes = doc[Constants::ALLOWED_SCOPES].GetArray();
133-
std::vector<std::string> value_array;
134-
value_array.reserve(scopes.Size());
135-
for (auto& value : scopes) {
136-
value_array.push_back(value.GetString());
137-
}
138-
result.SetAllowedScopes(std::move(value_array));
139-
}
140-
if (doc.HasMember(Constants::TOKEN_ENDPOINT_AUTH_METHOD)) {
141-
result.SetTokenEndpointAuthMethod(
142-
doc[Constants::TOKEN_ENDPOINT_AUTH_METHOD].GetString());
143-
}
144-
if (doc.HasMember(Constants::TOKEN_ENDPOINT_AUTH_METHOD_REASON)) {
145-
result.SetTokenEndpointAuthMethodReason(
146-
doc[Constants::TOKEN_ENDPOINT_AUTH_METHOD_REASON].GetString());
147-
}
148-
if (doc.HasMember(Constants::DOB_REQUIRED)) {
149-
result.SetDobRequired(doc[Constants::DOB_REQUIRED].GetBool());
150-
}
151-
if (doc.HasMember(Constants::TOKEN_DURATION)) {
152-
result.SetTokenDuration(doc[Constants::TOKEN_DURATION].GetInt());
153-
}
154-
if (doc.HasMember(Constants::REFERRERS)) {
155-
auto uris = doc[Constants::REFERRERS].GetArray();
156-
std::vector<std::string> value_array;
157-
value_array.reserve(uris.Size());
158-
for (auto& value : uris) {
159-
value_array.push_back(value.GetString());
160-
}
161-
result.SetReferrers(std::move(value_array));
162-
}
163-
if (doc.HasMember(Constants::STATUS)) {
164-
result.SetStatus(doc[Constants::STATUS].GetString());
165-
}
166-
if (doc.HasMember(Constants::APP_CODE_ENABLED)) {
167-
result.SetAppCodeEnabled(doc[Constants::APP_CODE_ENABLED].GetBool());
168-
}
169-
if (doc.HasMember(Constants::CREATED_TIME)) {
170-
result.SetCreatedTime(doc[Constants::CREATED_TIME].GetInt64());
171-
}
172-
if (doc.HasMember(Constants::REALM)) {
173-
result.SetRealm(doc[Constants::REALM].GetString());
174-
}
175-
if (doc.HasMember(Constants::TYPE)) {
176-
result.SetType(doc[Constants::TYPE].GetString());
177-
}
178-
if (doc.HasMember(Constants::RESPONSE_TYPES)) {
179-
auto types = doc[Constants::RESPONSE_TYPES].GetArray();
180-
std::vector<std::string> value_array;
181-
value_array.reserve(types.Size());
182-
for (auto& value : types) {
183-
value_array.push_back(value.GetString());
184-
}
185-
result.SetResponseTypes(std::move(value_array));
186-
}
187-
if (doc.HasMember(Constants::TIER)) {
188-
result.SetTier(doc[Constants::TIER].GetString());
189-
}
190-
if (doc.HasMember(Constants::HRN)) {
191-
result.SetHrn(doc[Constants::HRN].GetString());
192-
}
193-
return result;
135+
return ResponseFromJsonBuilder<IntrospectAppResult>::Build(doc)
136+
.Value(Constants::CLIENT_ID, &IntrospectAppResult::SetClientId)
137+
.Value(Constants::NAME, &IntrospectAppResult::SetName)
138+
.Value(Constants::DESCRIPTION, &IntrospectAppResult::SetDescription)
139+
.Array(Constants::REDIRECT_URIS, &IntrospectAppResult::SetReditectUris)
140+
.Array(Constants::ALLOWED_SCOPES, &IntrospectAppResult::SetAllowedScopes)
141+
.Value(Constants::TOKEN_ENDPOINT_AUTH_METHOD,
142+
&IntrospectAppResult::SetTokenEndpointAuthMethod)
143+
.Value(Constants::TOKEN_ENDPOINT_AUTH_METHOD_REASON,
144+
&IntrospectAppResult::SetTokenEndpointAuthMethodReason)
145+
.Value(Constants::DOB_REQUIRED, &IntrospectAppResult::SetDobRequired)
146+
.Value(Constants::TOKEN_DURATION, &IntrospectAppResult::SetTokenDuration)
147+
.Array(Constants::REFERRERS, &IntrospectAppResult::SetReferrers)
148+
.Value(Constants::STATUS, &IntrospectAppResult::SetStatus)
149+
.Value(Constants::APP_CODE_ENABLED,
150+
&IntrospectAppResult::SetAppCodeEnabled)
151+
.Value(Constants::CREATED_TIME, &IntrospectAppResult::SetCreatedTime)
152+
.Value(Constants::REALM, &IntrospectAppResult::SetRealm)
153+
.Value(Constants::TYPE, &IntrospectAppResult::SetType)
154+
.Array(Constants::RESPONSE_TYPES, &IntrospectAppResult::SetResponseTypes)
155+
.Value(Constants::TIER, &IntrospectAppResult::SetTier)
156+
.Value(Constants::HRN, &IntrospectAppResult::SetHrn)
157+
.Finish();
194158
}
195159

196160
DecisionType GetDecision(const std::string& str) {
@@ -204,28 +168,21 @@ std::vector<ActionResult> GetDiagnostics(rapidjson::Document& doc) {
204168
for (auto& element : array) {
205169
ActionResult action;
206170
if (element.HasMember(Constants::DECISION)) {
207-
action.SetDecision(
208-
GetDecision(element[Constants::DECISION].GetString()));
171+
action.SetDecision(GetDecision(element[Constants::DECISION].GetString()));
209172
// get permissions if avialible
210173
if (element.HasMember(Constants::PERMISSIONS) &&
211174
element[Constants::PERMISSIONS].IsArray()) {
212175
std::vector<Permission> permissions;
213176
const auto& permissions_array =
214177
element[Constants::PERMISSIONS].GetArray();
215178
for (auto& permission_element : permissions_array) {
216-
Permission permission;
217-
if (permission_element.HasMember(Constants::ACTION)) {
218-
permission.SetAction(
219-
permission_element[Constants::ACTION].GetString());
220-
}
221-
if (permission_element.HasMember(Constants::DECISION)) {
222-
permission.SetDecision(GetDecision(
223-
permission_element[Constants::DECISION].GetString()));
224-
}
225-
if (permission_element.HasMember(Constants::RESOURCE)) {
226-
permission.SetResource(
227-
permission_element[Constants::RESOURCE].GetString());
228-
}
179+
Permission permission =
180+
ResponseFromJsonBuilder<Permission>::Build(permission_element)
181+
.Value(Constants::ACTION, &Permission::SetAction)
182+
.Value(Constants::DECISION, &Permission::SetDecision,
183+
&GetDecision)
184+
.Value(Constants::RESOURCE, &Permission::SetResource)
185+
.Finish();
229186
permissions.push_back(std::move(permission));
230187
}
231188

@@ -262,6 +219,42 @@ AuthorizeResult GetAuthorizeResult(rapidjson::Document& doc) {
262219
return result;
263220
}
264221

222+
UserAccountInfoResponse GetUserAccountInfoResponse(
223+
client::HttpResponse& http_response) {
224+
using UserAccountInfo = model::UserAccountInfoResponse;
225+
226+
const auto parse_response = Parse(http_response);
227+
if (!parse_response.IsSuccessful()) {
228+
return parse_response.GetError();
229+
}
230+
231+
const auto& document = parse_response.GetResult();
232+
233+
return ResponseFromJsonBuilder<UserAccountInfo>::Build(document)
234+
.Value(Constants::USER_ID, &UserAccountInfo::SetUserId)
235+
.Value(Constants::REALM, &UserAccountInfo::SetRealm)
236+
.Value(Constants::FACEBOOK_ID, &UserAccountInfo::SetFacebookId)
237+
.Value(Constants::FIRSTNAME, &UserAccountInfo::SetFirstname)
238+
.Value(Constants::LASTNAME, &UserAccountInfo::SetLastname)
239+
.Value(Constants::EMAIL, &UserAccountInfo::SetEmail)
240+
.Value(Constants::RECOVERY_EMAIL, &UserAccountInfo::SetRecoveryEmail)
241+
.Value(Constants::DOB, &UserAccountInfo::SetDob)
242+
.Value(Constants::COUNTRY_CODE, &UserAccountInfo::SetCountryCode)
243+
.Value(Constants::LANGUAGE, &UserAccountInfo::SetLanguage)
244+
.Value(Constants::EMAIL_VERIFIED, &UserAccountInfo::SetEmailVerified)
245+
.Value(Constants::PHONE_NUMBER, &UserAccountInfo::SetPhoneNumber)
246+
.Value(Constants::PHONE_NUMBER_VERIFIED,
247+
&UserAccountInfo::SetPhoneNumberVerified)
248+
.Value(Constants::MARKETING_ENABLED,
249+
&UserAccountInfo::SetMarketingEnabled)
250+
.Value(Constants::CREATED_TIME, &UserAccountInfo::SetCreatedTime)
251+
.Value(Constants::UPDATED_TIME, &UserAccountInfo::SetUpdatedTime)
252+
.Value(Constants::STATE, &UserAccountInfo::SetState)
253+
.Value(Constants::HRN, &UserAccountInfo::SetHrn)
254+
.Value(Constants::ACCOUNT_TYPE, &UserAccountInfo::SetAccountType)
255+
.Finish();
256+
}
257+
265258
client::OlpClient CreateOlpClient(
266259
const AuthenticationSettings& auth_settings,
267260
boost::optional<client::AuthenticationSettings> authentication_settings) {
@@ -286,17 +279,17 @@ std::string GenerateAuthorizationHeader(
286279
std::stringstream query;
287280

288281
query << kOauthConsumerKey << kParamEquals << credentials.GetKey()
289-
<< kParamAdd << kOauthNonce << kParamEquals << nonce << kParamAdd
290-
<< kOauthSignatureMethod << kParamEquals << kHmac << kParamAdd
291-
<< kOauthTimestamp << kParamEquals << timestamp_str << kParamAdd
292-
<< kOauthVersion << kParamEquals << kVersion;
282+
<< kParamAdd << kOauthNonce << kParamEquals << nonce << kParamAdd
283+
<< kOauthSignatureMethod << kParamEquals << kHmac << kParamAdd
284+
<< kOauthTimestamp << kParamEquals << timestamp_str << kParamAdd
285+
<< kOauthVersion << kParamEquals << kVersion;
293286

294287
const auto encoded_query = utils::Url::Encode(query.str());
295288

296289
std::stringstream signature_base;
297290

298-
signature_base << kOauthPost << kParamAdd << utils::Url::Encode(url) << kParamAdd
299-
<< encoded_query;
291+
signature_base << kOauthPost << kParamAdd << utils::Url::Encode(url)
292+
<< kParamAdd << encoded_query;
300293

301294
const std::string encode_key = credentials.GetSecret() + kParamAdd;
302295
auto hmac_result = Crypto::HmacSha256(encode_key, signature_base.str());

0 commit comments

Comments
 (0)