1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/supervised_user/child_accounts/family_info_fetcher.h"
7 #include "base/json/json_reader.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/values.h"
10 #include "net/base/load_flags.h"
11 #include "net/http/http_status_code.h"
12 #include "net/url_request/url_request_status.h"
15 const char kFamilyApiUrl
[] = "https://www.googleapis.com/kidsmanagement/v1/";
16 const char kGetFamilyProfileApiSuffix
[] = "families/mine?alt=json";
17 const char kGetFamilyMembersApiSuffix
[] = "families/mine/members?alt=json";
18 const char kScope
[] = "https://www.googleapis.com/auth/kid.family.readonly";
19 const char kAuthorizationHeaderFormat
[] = "Authorization: Bearer %s";
20 const int kNumRetries
= 1;
22 const char kIdFamily
[] = "family";
23 const char kIdFamilyId
[] = "familyId";
24 const char kIdProfile
[] = "profile";
25 const char kIdFamilyName
[] = "name";
26 const char kIdMembers
[] = "members";
27 const char kIdUserId
[] = "userId";
28 const char kIdRole
[] = "role";
29 const char kIdDisplayName
[] = "displayName";
30 const char kIdEmail
[] = "email";
31 const char kIdProfileUrl
[] = "profileUrl";
32 const char kIdProfileImageUrl
[] = "profileImageUrl";
33 const char kIdDefaultProfileImageUrl
[] = "defaultProfileImageUrl";
35 // These correspond to enum FamilyInfoFetcher::FamilyMemberRole, in order.
36 const char* kFamilyMemberRoleStrings
[] = {
43 FamilyInfoFetcher::FamilyProfile::FamilyProfile() {
46 FamilyInfoFetcher::FamilyProfile::FamilyProfile(const std::string
& id
,
47 const std::string
& name
)
48 : id(id
), name(name
) {
51 FamilyInfoFetcher::FamilyProfile::~FamilyProfile() {
54 FamilyInfoFetcher::FamilyMember::FamilyMember() {
57 FamilyInfoFetcher::FamilyMember::FamilyMember(
58 const std::string
& obfuscated_gaia_id
,
59 FamilyMemberRole role
,
60 const std::string
& display_name
,
61 const std::string
& email
,
62 const std::string
& profile_url
,
63 const std::string
& profile_image_url
)
64 : obfuscated_gaia_id(obfuscated_gaia_id
),
66 display_name(display_name
),
68 profile_url(profile_url
),
69 profile_image_url(profile_image_url
) {
72 FamilyInfoFetcher::FamilyMember::~FamilyMember() {
75 FamilyInfoFetcher::FamilyInfoFetcher(
77 const std::string
& account_id
,
78 OAuth2TokenService
* token_service
,
79 net::URLRequestContextGetter
* request_context
)
80 : OAuth2TokenService::Consumer("family_info_fetcher"),
82 account_id_(account_id
),
83 token_service_(token_service
),
84 request_context_(request_context
),
85 request_type_(net::URLFetcher::GET
),
86 access_token_expired_(false) {
89 FamilyInfoFetcher::~FamilyInfoFetcher() {
90 // Ensures O2TS observation is cleared when FamilyInfoFetcher is destructed
91 // before refresh token is available.
92 token_service_
->RemoveObserver(this);
96 std::string
FamilyInfoFetcher::RoleToString(FamilyMemberRole role
) {
97 return kFamilyMemberRoleStrings
[role
];
101 bool FamilyInfoFetcher::StringToRole(
102 const std::string
& str
,
103 FamilyInfoFetcher::FamilyMemberRole
* role
) {
104 for (size_t i
= 0; i
< arraysize(kFamilyMemberRoleStrings
); i
++) {
105 if (str
== kFamilyMemberRoleStrings
[i
]) {
106 *role
= FamilyMemberRole(i
);
113 void FamilyInfoFetcher::StartGetFamilyProfile() {
114 request_suffix_
= kGetFamilyProfileApiSuffix
;
115 request_type_
= net::URLFetcher::GET
;
119 void FamilyInfoFetcher::StartGetFamilyMembers() {
120 request_suffix_
= kGetFamilyMembersApiSuffix
;
121 request_type_
= net::URLFetcher::GET
;
125 void FamilyInfoFetcher::StartFetching() {
126 if (token_service_
->RefreshTokenIsAvailable(account_id_
)) {
127 StartFetchingAccessToken();
129 // Wait until we get a refresh token.
130 token_service_
->AddObserver(this);
134 void FamilyInfoFetcher::StartFetchingAccessToken() {
135 OAuth2TokenService::ScopeSet scopes
;
136 scopes
.insert(kScope
);
137 access_token_request_
= token_service_
->StartRequest(
138 account_id_
, scopes
, this);
141 void FamilyInfoFetcher::OnRefreshTokenAvailable(
142 const std::string
& account_id
) {
143 // Wait until we get a refresh token for the requested account.
144 if (account_id
!= account_id_
)
147 token_service_
->RemoveObserver(this);
149 StartFetchingAccessToken();
152 void FamilyInfoFetcher::OnRefreshTokensLoaded() {
153 token_service_
->RemoveObserver(this);
155 // The PO2TS has loaded all tokens, but we didn't get one for the account we
156 // want. We probably won't get one any time soon, so report an error.
157 DLOG(WARNING
) << "Did not get a refresh token for account " << account_id_
;
158 consumer_
->OnFailure(TOKEN_ERROR
);
161 void FamilyInfoFetcher::OnGetTokenSuccess(
162 const OAuth2TokenService::Request
* request
,
163 const std::string
& access_token
,
164 const base::Time
& expiration_time
) {
165 DCHECK_EQ(access_token_request_
.get(), request
);
166 access_token_
= access_token
;
168 GURL
url(kFamilyApiUrl
+ request_suffix_
);
170 url_fetcher_
= net::URLFetcher::Create(id
, url
, request_type_
, this);
172 url_fetcher_
->SetRequestContext(request_context_
);
173 url_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
174 net::LOAD_DO_NOT_SAVE_COOKIES
);
175 url_fetcher_
->SetAutomaticallyRetryOnNetworkChanges(kNumRetries
);
176 url_fetcher_
->AddExtraRequestHeader(
177 base::StringPrintf(kAuthorizationHeaderFormat
, access_token
.c_str()));
179 url_fetcher_
->Start();
182 void FamilyInfoFetcher::OnGetTokenFailure(
183 const OAuth2TokenService::Request
* request
,
184 const GoogleServiceAuthError
& error
) {
185 DCHECK_EQ(access_token_request_
.get(), request
);
186 DLOG(WARNING
) << "Failed to get an access token: " << error
.ToString();
187 consumer_
->OnFailure(TOKEN_ERROR
);
190 void FamilyInfoFetcher::OnURLFetchComplete(
191 const net::URLFetcher
* source
) {
192 const net::URLRequestStatus
& status
= source
->GetStatus();
193 if (!status
.is_success()) {
194 DLOG(WARNING
) << "URLRequestStatus error " << status
.error();
195 consumer_
->OnFailure(NETWORK_ERROR
);
199 int response_code
= source
->GetResponseCode();
200 if (response_code
== net::HTTP_UNAUTHORIZED
&& !access_token_expired_
) {
201 DVLOG(1) << "Access token expired, retrying";
202 access_token_expired_
= true;
203 OAuth2TokenService::ScopeSet scopes
;
204 scopes
.insert(kScope
);
205 token_service_
->InvalidateAccessToken(account_id_
, scopes
, access_token_
);
210 if (response_code
!= net::HTTP_OK
) {
211 DLOG(WARNING
) << "HTTP error " << response_code
;
212 consumer_
->OnFailure(NETWORK_ERROR
);
216 std::string response_body
;
217 source
->GetResponseAsString(&response_body
);
219 if (request_suffix_
== kGetFamilyProfileApiSuffix
) {
220 FamilyProfileFetched(response_body
);
221 } else if (request_suffix_
== kGetFamilyMembersApiSuffix
) {
222 FamilyMembersFetched(response_body
);
229 bool FamilyInfoFetcher::ParseMembers(const base::ListValue
* list
,
230 std::vector
<FamilyMember
>* members
) {
231 for (base::ListValue::const_iterator it
= list
->begin();
235 base::DictionaryValue
* dict
= NULL
;
236 if (!(*it
)->GetAsDictionary(&dict
) || !ParseMember(dict
, &member
)) {
239 members
->push_back(member
);
245 bool FamilyInfoFetcher::ParseMember(const base::DictionaryValue
* dict
,
246 FamilyMember
* member
) {
247 if (!dict
->GetString(kIdUserId
, &member
->obfuscated_gaia_id
))
249 std::string role_str
;
250 if (!dict
->GetString(kIdRole
, &role_str
))
252 if (!StringToRole(role_str
, &member
->role
))
254 const base::DictionaryValue
* profile_dict
= NULL
;
255 if (dict
->GetDictionary(kIdProfile
, &profile_dict
))
256 ParseProfile(profile_dict
, member
);
261 void FamilyInfoFetcher::ParseProfile(const base::DictionaryValue
* dict
,
262 FamilyMember
* member
) {
263 dict
->GetString(kIdDisplayName
, &member
->display_name
);
264 dict
->GetString(kIdEmail
, &member
->email
);
265 dict
->GetString(kIdProfileUrl
, &member
->profile_url
);
266 dict
->GetString(kIdProfileImageUrl
, &member
->profile_image_url
);
267 if (member
->profile_image_url
.empty())
268 dict
->GetString(kIdDefaultProfileImageUrl
, &member
->profile_image_url
);
271 void FamilyInfoFetcher::FamilyProfileFetched(const std::string
& response
) {
272 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(response
);
273 const base::DictionaryValue
* dict
= NULL
;
274 if (!value
|| !value
->GetAsDictionary(&dict
)) {
275 consumer_
->OnFailure(SERVICE_ERROR
);
278 const base::DictionaryValue
* family_dict
= NULL
;
279 if (!dict
->GetDictionary(kIdFamily
, &family_dict
)) {
280 consumer_
->OnFailure(SERVICE_ERROR
);
283 FamilyProfile family
;
284 if (!family_dict
->GetStringWithoutPathExpansion(kIdFamilyId
, &family
.id
)) {
285 consumer_
->OnFailure(SERVICE_ERROR
);
288 const base::DictionaryValue
* profile_dict
= NULL
;
289 if (!family_dict
->GetDictionary(kIdProfile
, &profile_dict
)) {
290 consumer_
->OnFailure(SERVICE_ERROR
);
293 if (!profile_dict
->GetStringWithoutPathExpansion(kIdFamilyName
,
295 consumer_
->OnFailure(SERVICE_ERROR
);
298 consumer_
->OnGetFamilyProfileSuccess(family
);
301 void FamilyInfoFetcher::FamilyMembersFetched(const std::string
& response
) {
302 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(response
);
303 const base::DictionaryValue
* dict
= NULL
;
304 if (!value
|| !value
->GetAsDictionary(&dict
)) {
305 consumer_
->OnFailure(SERVICE_ERROR
);
308 const base::ListValue
* members_list
= NULL
;
309 if (!dict
->GetList(kIdMembers
, &members_list
)) {
310 consumer_
->OnFailure(SERVICE_ERROR
);
313 std::vector
<FamilyMember
> members
;
314 if (!ParseMembers(members_list
, &members
)){
315 consumer_
->OnFailure(SERVICE_ERROR
);
318 consumer_
->OnGetFamilyMembersSuccess(members
);