Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / supervised_user / child_accounts / family_info_fetcher.cc
blob47c6899a24d3022fd366e1a74e0b95bfb8f23f1d
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"
13 #include "url/gurl.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[] = {
37 "headOfHousehold",
38 "parent",
39 "member",
40 "child"
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),
65 role(role),
66 display_name(display_name),
67 email(email),
68 profile_url(profile_url),
69 profile_image_url(profile_image_url) {
72 FamilyInfoFetcher::FamilyMember::~FamilyMember() {
75 FamilyInfoFetcher::FamilyInfoFetcher(
76 Consumer* consumer,
77 const std::string& account_id,
78 OAuth2TokenService* token_service,
79 net::URLRequestContextGetter* request_context)
80 : OAuth2TokenService::Consumer("family_info_fetcher"),
81 consumer_(consumer),
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);
95 // static
96 std::string FamilyInfoFetcher::RoleToString(FamilyMemberRole role) {
97 return kFamilyMemberRoleStrings[role];
100 // static
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);
107 return true;
110 return false;
113 void FamilyInfoFetcher::StartGetFamilyProfile() {
114 request_suffix_ = kGetFamilyProfileApiSuffix;
115 request_type_ = net::URLFetcher::GET;
116 StartFetching();
119 void FamilyInfoFetcher::StartGetFamilyMembers() {
120 request_suffix_ = kGetFamilyMembersApiSuffix;
121 request_type_ = net::URLFetcher::GET;
122 StartFetching();
125 void FamilyInfoFetcher::StartFetching() {
126 if (token_service_->RefreshTokenIsAvailable(account_id_)) {
127 StartFetchingAccessToken();
128 } else {
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_)
145 return;
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_);
169 const int id = 0;
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);
196 return;
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_);
206 StartFetching();
207 return;
210 if (response_code != net::HTTP_OK) {
211 DLOG(WARNING) << "HTTP error " << response_code;
212 consumer_->OnFailure(NETWORK_ERROR);
213 return;
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);
223 } else {
224 NOTREACHED();
228 // static
229 bool FamilyInfoFetcher::ParseMembers(const base::ListValue* list,
230 std::vector<FamilyMember>* members) {
231 for (base::ListValue::const_iterator it = list->begin();
232 it != list->end();
233 it++) {
234 FamilyMember member;
235 base::DictionaryValue* dict = NULL;
236 if (!(*it)->GetAsDictionary(&dict) || !ParseMember(dict, &member)) {
237 return false;
239 members->push_back(member);
241 return true;
244 // static
245 bool FamilyInfoFetcher::ParseMember(const base::DictionaryValue* dict,
246 FamilyMember* member) {
247 if (!dict->GetString(kIdUserId, &member->obfuscated_gaia_id))
248 return false;
249 std::string role_str;
250 if (!dict->GetString(kIdRole, &role_str))
251 return false;
252 if (!StringToRole(role_str, &member->role))
253 return false;
254 const base::DictionaryValue* profile_dict = NULL;
255 if (dict->GetDictionary(kIdProfile, &profile_dict))
256 ParseProfile(profile_dict, member);
257 return true;
260 // static
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);
276 return;
278 const base::DictionaryValue* family_dict = NULL;
279 if (!dict->GetDictionary(kIdFamily, &family_dict)) {
280 consumer_->OnFailure(SERVICE_ERROR);
281 return;
283 FamilyProfile family;
284 if (!family_dict->GetStringWithoutPathExpansion(kIdFamilyId, &family.id)) {
285 consumer_->OnFailure(SERVICE_ERROR);
286 return;
288 const base::DictionaryValue* profile_dict = NULL;
289 if (!family_dict->GetDictionary(kIdProfile, &profile_dict)) {
290 consumer_->OnFailure(SERVICE_ERROR);
291 return;
293 if (!profile_dict->GetStringWithoutPathExpansion(kIdFamilyName,
294 &family.name)) {
295 consumer_->OnFailure(SERVICE_ERROR);
296 return;
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);
306 return;
308 const base::ListValue* members_list = NULL;
309 if (!dict->GetList(kIdMembers, &members_list)) {
310 consumer_->OnFailure(SERVICE_ERROR);
311 return;
313 std::vector<FamilyMember> members;
314 if (!ParseMembers(members_list, &members)){
315 consumer_->OnFailure(SERVICE_ERROR);
316 return;
318 consumer_->OnGetFamilyMembersSuccess(members);