| OLD | NEW |
| (Empty) | |
| 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. |
| 4 |
| 5 #include "chrome/browser/supervised_user/child_accounts/family_info_fetcher.h" |
| 6 |
| 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" |
| 14 |
| 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"; |
| 19 const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s"; |
| 20 const int kNumRetries = 1; |
| 21 |
| 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 |
| 34 // These correspond to enum FamilyInfoFetcher::FamilyMemberRole, in order. |
| 35 const char* kFamilyMemberRoleStrings[] = { |
| 36 "headOfHousehold", |
| 37 "parent", |
| 38 "member", |
| 39 "child" |
| 40 }; |
| 41 |
| 42 FamilyInfoFetcher::FamilyProfile::FamilyProfile() { |
| 43 } |
| 44 |
| 45 FamilyInfoFetcher::FamilyProfile::FamilyProfile(const std::string& id, |
| 46 const std::string& name) |
| 47 : id(id), name(name) { |
| 48 } |
| 49 |
| 50 FamilyInfoFetcher::FamilyProfile::~FamilyProfile() { |
| 51 } |
| 52 |
| 53 FamilyInfoFetcher::FamilyMember::FamilyMember() { |
| 54 } |
| 55 |
| 56 FamilyInfoFetcher::FamilyMember::FamilyMember( |
| 57 const std::string& obfuscated_gaia_id, |
| 58 FamilyMemberRole role, |
| 59 const std::string& display_name, |
| 60 const std::string& email, |
| 61 const std::string& profile_url, |
| 62 const std::string& profile_image_url) |
| 63 : obfuscated_gaia_id(obfuscated_gaia_id), |
| 64 role(role), |
| 65 display_name(display_name), |
| 66 email(email), |
| 67 profile_url(profile_url), |
| 68 profile_image_url(profile_image_url) { |
| 69 } |
| 70 |
| 71 FamilyInfoFetcher::FamilyMember::~FamilyMember() { |
| 72 } |
| 73 |
| 74 FamilyInfoFetcher::FamilyInfoFetcher( |
| 75 Consumer* consumer, |
| 76 const std::string& account_id, |
| 77 OAuth2TokenService* token_service, |
| 78 net::URLRequestContextGetter* request_context) |
| 79 : OAuth2TokenService::Consumer("family_info_fetcher"), |
| 80 consumer_(consumer), |
| 81 account_id_(account_id), |
| 82 token_service_(token_service), |
| 83 request_context_(request_context), |
| 84 request_type_(net::URLFetcher::GET), |
| 85 access_token_expired_(false) { |
| 86 } |
| 87 |
| 88 FamilyInfoFetcher::~FamilyInfoFetcher() { |
| 89 // Ensures O2TS observation is cleared when FamilyInfoFetcher is destructed |
| 90 // before refresh token is available. |
| 91 token_service_->RemoveObserver(this); |
| 92 } |
| 93 |
| 94 // static |
| 95 std::string FamilyInfoFetcher::RoleToString(FamilyMemberRole role) { |
| 96 return kFamilyMemberRoleStrings[role]; |
| 97 } |
| 98 |
| 99 // static |
| 100 bool FamilyInfoFetcher::StringToRole( |
| 101 const std::string& str, |
| 102 FamilyInfoFetcher::FamilyMemberRole* role) { |
| 103 for (size_t i = 0; i < arraysize(kFamilyMemberRoleStrings); i++) { |
| 104 if (str == kFamilyMemberRoleStrings[i]) { |
| 105 *role = FamilyMemberRole(i); |
| 106 return true; |
| 107 } |
| 108 } |
| 109 return false; |
| 110 } |
| 111 |
| 112 void FamilyInfoFetcher::StartGetFamilyProfile() { |
| 113 request_suffix_ = kGetFamilyProfileApiSuffix; |
| 114 request_type_ = net::URLFetcher::GET; |
| 115 StartFetching(); |
| 116 } |
| 117 |
| 118 void FamilyInfoFetcher::StartGetFamilyMembers() { |
| 119 request_suffix_ = kGetFamilyMembersApiSuffix; |
| 120 request_type_ = net::URLFetcher::GET; |
| 121 StartFetching(); |
| 122 } |
| 123 |
| 124 void FamilyInfoFetcher::StartFetching() { |
| 125 if (token_service_->RefreshTokenIsAvailable(account_id_)) { |
| 126 StartFetchingAccessToken(); |
| 127 } else { |
| 128 // Wait until we get a refresh token. |
| 129 token_service_->AddObserver(this); |
| 130 } |
| 131 } |
| 132 |
| 133 void FamilyInfoFetcher::StartFetchingAccessToken() { |
| 134 OAuth2TokenService::ScopeSet scopes; |
| 135 scopes.insert(kScope); |
| 136 access_token_request_ = token_service_->StartRequest( |
| 137 account_id_, scopes, this); |
| 138 } |
| 139 |
| 140 void FamilyInfoFetcher::OnRefreshTokenAvailable( |
| 141 const std::string& account_id) { |
| 142 // Wait until we get a refresh token for the requested account. |
| 143 if (account_id != account_id_) |
| 144 return; |
| 145 |
| 146 token_service_->RemoveObserver(this); |
| 147 |
| 148 StartFetchingAccessToken(); |
| 149 } |
| 150 |
| 151 void FamilyInfoFetcher::OnRefreshTokensLoaded() { |
| 152 token_service_->RemoveObserver(this); |
| 153 |
| 154 // The PO2TS has loaded all tokens, but we didn't get one for the account we |
| 155 // want. We probably won't get one any time soon, so report an error. |
| 156 DLOG(WARNING) << "Did not get a refresh token for account " << account_id_; |
| 157 consumer_->OnFailure(TOKEN_ERROR); |
| 158 } |
| 159 |
| 160 void FamilyInfoFetcher::OnGetTokenSuccess( |
| 161 const OAuth2TokenService::Request* request, |
| 162 const std::string& access_token, |
| 163 const base::Time& expiration_time) { |
| 164 DCHECK_EQ(access_token_request_.get(), request); |
| 165 access_token_ = access_token; |
| 166 |
| 167 GURL url(kFamilyApiUrl + request_suffix_); |
| 168 const int id = 0; |
| 169 url_fetcher_.reset( |
| 170 net::URLFetcher::Create(id, url, request_type_, this)); |
| 171 |
| 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())); |
| 178 |
| 179 url_fetcher_->Start(); |
| 180 } |
| 181 |
| 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); |
| 188 } |
| 189 |
| 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; |
| 197 } |
| 198 |
| 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_->InvalidateToken(account_id_, scopes, access_token_); |
| 206 StartFetching(); |
| 207 return; |
| 208 } |
| 209 |
| 210 if (response_code != net::HTTP_OK) { |
| 211 DLOG(WARNING) << "HTTP error " << response_code; |
| 212 consumer_->OnFailure(NETWORK_ERROR); |
| 213 return; |
| 214 } |
| 215 |
| 216 std::string response_body; |
| 217 source->GetResponseAsString(&response_body); |
| 218 |
| 219 if (request_suffix_ == kGetFamilyProfileApiSuffix) { |
| 220 FamilyProfileFetched(response_body); |
| 221 } else if (request_suffix_ == kGetFamilyMembersApiSuffix) { |
| 222 FamilyMembersFetched(response_body); |
| 223 } else { |
| 224 NOTREACHED(); |
| 225 } |
| 226 } |
| 227 |
| 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; |
| 238 } |
| 239 members->push_back(member); |
| 240 } |
| 241 return true; |
| 242 } |
| 243 |
| 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; |
| 258 } |
| 259 |
| 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 } |
| 268 |
| 269 void FamilyInfoFetcher::FamilyProfileFetched(const std::string& response) { |
| 270 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); |
| 271 const base::DictionaryValue* dict = NULL; |
| 272 if (!value || !value->GetAsDictionary(&dict)) { |
| 273 consumer_->OnFailure(SERVICE_ERROR); |
| 274 return; |
| 275 } |
| 276 const base::DictionaryValue* family_dict = NULL; |
| 277 if (!dict->GetDictionary(kIdFamily, &family_dict)) { |
| 278 consumer_->OnFailure(SERVICE_ERROR); |
| 279 return; |
| 280 } |
| 281 FamilyProfile family; |
| 282 if (!family_dict->GetStringWithoutPathExpansion(kIdFamilyId, &family.id)) { |
| 283 consumer_->OnFailure(SERVICE_ERROR); |
| 284 return; |
| 285 } |
| 286 const base::DictionaryValue* profile_dict = NULL; |
| 287 if (!family_dict->GetDictionary(kIdProfile, &profile_dict)) { |
| 288 consumer_->OnFailure(SERVICE_ERROR); |
| 289 return; |
| 290 } |
| 291 if (!profile_dict->GetStringWithoutPathExpansion(kIdFamilyName, |
| 292 &family.name)) { |
| 293 consumer_->OnFailure(SERVICE_ERROR); |
| 294 return; |
| 295 } |
| 296 consumer_->OnGetFamilyProfileSuccess(family); |
| 297 } |
| 298 |
| 299 void FamilyInfoFetcher::FamilyMembersFetched(const std::string& response) { |
| 300 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); |
| 301 const base::DictionaryValue* dict = NULL; |
| 302 if (!value || !value->GetAsDictionary(&dict)) { |
| 303 consumer_->OnFailure(SERVICE_ERROR); |
| 304 return; |
| 305 } |
| 306 const base::ListValue* members_list = NULL; |
| 307 if (!dict->GetList(kIdMembers, &members_list)) { |
| 308 consumer_->OnFailure(SERVICE_ERROR); |
| 309 return; |
| 310 } |
| 311 std::vector<FamilyMember> members; |
| 312 if (!ParseMembers(members_list, &members)){ |
| 313 consumer_->OnFailure(SERVICE_ERROR); |
| 314 return; |
| 315 } |
| 316 consumer_->OnGetFamilyMembersSuccess(members); |
| 317 } |
| OLD | NEW |