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/permission_request_creator_apiary.h" 6 7#include "base/callback.h" 8#include "base/command_line.h" 9#include "base/json/json_reader.h" 10#include "base/json/json_writer.h" 11#include "base/logging.h" 12#include "base/strings/stringprintf.h" 13#include "base/values.h" 14#include "chrome/browser/profiles/profile.h" 15#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 16#include "chrome/browser/signin/signin_manager_factory.h" 17#include "chrome/browser/sync/supervised_user_signin_manager_wrapper.h" 18#include "chrome/common/chrome_switches.h" 19#include "components/signin/core/browser/profile_oauth2_token_service.h" 20#include "components/signin/core/browser/signin_manager.h" 21#include "components/signin/core/browser/signin_manager_base.h" 22#include "google_apis/gaia/google_service_auth_error.h" 23#include "net/base/load_flags.h" 24#include "net/base/net_errors.h" 25#include "net/http/http_status_code.h" 26#include "net/url_request/url_fetcher.h" 27#include "net/url_request/url_request_status.h" 28 29using net::URLFetcher; 30 31const int kNumRetries = 1; 32const char kIdKey[] = "id"; 33const char kNamespace[] = "CHROME"; 34const char kState[] = "PENDING"; 35 36static const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s"; 37 38struct PermissionRequestCreatorApiary::Request { 39 Request(const GURL& url_requested, const base::Closure& callback); 40 ~Request(); 41 42 GURL url_requested; 43 base::Closure callback; 44 scoped_ptr<OAuth2TokenService::Request> access_token_request; 45 std::string access_token; 46 bool access_token_expired; 47 scoped_ptr<net::URLFetcher> url_fetcher; 48}; 49 50PermissionRequestCreatorApiary::Request::Request(const GURL& url_requested, 51 const base::Closure& callback) 52 : url_requested(url_requested), 53 callback(callback), 54 access_token_expired(false) { 55} 56 57PermissionRequestCreatorApiary::Request::~Request() {} 58 59PermissionRequestCreatorApiary::PermissionRequestCreatorApiary( 60 OAuth2TokenService* oauth2_token_service, 61 scoped_ptr<SupervisedUserSigninManagerWrapper> signin_wrapper, 62 net::URLRequestContextGetter* context) 63 : OAuth2TokenService::Consumer("permissions_creator"), 64 oauth2_token_service_(oauth2_token_service), 65 signin_wrapper_(signin_wrapper.Pass()), 66 context_(context) {} 67 68PermissionRequestCreatorApiary::~PermissionRequestCreatorApiary() {} 69 70// static 71scoped_ptr<PermissionRequestCreator> 72PermissionRequestCreatorApiary::CreateWithProfile(Profile* profile) { 73 ProfileOAuth2TokenService* token_service = 74 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 75 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile); 76 scoped_ptr<SupervisedUserSigninManagerWrapper> signin_wrapper( 77 new SupervisedUserSigninManagerWrapper(profile, signin)); 78 scoped_ptr<PermissionRequestCreator> creator( 79 new PermissionRequestCreatorApiary( 80 token_service, signin_wrapper.Pass(), profile->GetRequestContext())); 81 return creator.Pass(); 82} 83 84void PermissionRequestCreatorApiary::CreatePermissionRequest( 85 const GURL& url_requested, 86 const base::Closure& callback) { 87 requests_.push_back(new Request(url_requested, callback)); 88 StartFetching(requests_.back()); 89} 90 91std::string PermissionRequestCreatorApiary::GetApiScopeToUse() const { 92 if (CommandLine::ForCurrentProcess()->HasSwitch( 93 switches::kPermissionRequestApiScope)) { 94 return CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 95 switches::kPermissionRequestApiScope); 96 } else { 97 return signin_wrapper_->GetSyncScopeToUse(); 98 } 99} 100 101void PermissionRequestCreatorApiary::StartFetching(Request* request) { 102 OAuth2TokenService::ScopeSet scopes; 103 scopes.insert(GetApiScopeToUse()); 104 request->access_token_request = oauth2_token_service_->StartRequest( 105 signin_wrapper_->GetAccountIdToUse(), scopes, this); 106} 107 108void PermissionRequestCreatorApiary::OnGetTokenSuccess( 109 const OAuth2TokenService::Request* request, 110 const std::string& access_token, 111 const base::Time& expiration_time) { 112 RequestIterator it = requests_.begin(); 113 while (it != requests_.end()) { 114 if (request == (*it)->access_token_request.get()) 115 break; 116 ++it; 117 } 118 DCHECK(it != requests_.end()); 119 (*it)->access_token = access_token; 120 GURL url(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 121 switches::kPermissionRequestApiUrl)); 122 const int id = 0; 123 124 (*it)->url_fetcher.reset(URLFetcher::Create(id, url, URLFetcher::POST, this)); 125 126 (*it)->url_fetcher->SetRequestContext(context_); 127 (*it)->url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | 128 net::LOAD_DO_NOT_SAVE_COOKIES); 129 (*it)->url_fetcher->SetAutomaticallyRetryOnNetworkChanges(kNumRetries); 130 (*it)->url_fetcher->AddExtraRequestHeader( 131 base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str())); 132 133 base::DictionaryValue dict; 134 dict.SetStringWithoutPathExpansion("namespace", kNamespace); 135 dict.SetStringWithoutPathExpansion("objectRef", (*it)->url_requested.spec()); 136 dict.SetStringWithoutPathExpansion("state", kState); 137 std::string body; 138 base::JSONWriter::Write(&dict, &body); 139 (*it)->url_fetcher->SetUploadData("application/json", body); 140 141 (*it)->url_fetcher->Start(); 142} 143 144void PermissionRequestCreatorApiary::OnGetTokenFailure( 145 const OAuth2TokenService::Request* request, 146 const GoogleServiceAuthError& error) { 147 RequestIterator it = requests_.begin(); 148 while (it != requests_.end()) { 149 if (request == (*it)->access_token_request.get()) 150 break; 151 ++it; 152 } 153 DCHECK(it != requests_.end()); 154 (*it)->callback.Run(); 155 requests_.erase(it); 156} 157 158void PermissionRequestCreatorApiary::OnURLFetchComplete( 159 const URLFetcher* source) { 160 RequestIterator it = requests_.begin(); 161 while (it != requests_.end()) { 162 if (source == (*it)->url_fetcher.get()) 163 break; 164 ++it; 165 } 166 DCHECK(it != requests_.end()); 167 168 const net::URLRequestStatus& status = source->GetStatus(); 169 if (!status.is_success()) { 170 DispatchNetworkError(it, status.error()); 171 return; 172 } 173 174 int response_code = source->GetResponseCode(); 175 if (response_code == net::HTTP_UNAUTHORIZED && !(*it)->access_token_expired) { 176 (*it)->access_token_expired = true; 177 OAuth2TokenService::ScopeSet scopes; 178 scopes.insert(GetApiScopeToUse()); 179 oauth2_token_service_->InvalidateToken( 180 signin_wrapper_->GetAccountIdToUse(), scopes, (*it)->access_token); 181 StartFetching(*it); 182 return; 183 } 184 185 if (response_code != net::HTTP_OK) { 186 DLOG(WARNING) << "HTTP error " << response_code; 187 DispatchGoogleServiceAuthError( 188 it, GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); 189 return; 190 } 191 192 std::string response_body; 193 source->GetResponseAsString(&response_body); 194 scoped_ptr<base::Value> value(base::JSONReader::Read(response_body)); 195 base::DictionaryValue* dict = NULL; 196 if (!value || !value->GetAsDictionary(&dict)) { 197 DispatchNetworkError(it, net::ERR_INVALID_RESPONSE); 198 return; 199 } 200 std::string id; 201 if (!dict->GetString(kIdKey, &id)) { 202 DispatchNetworkError(it, net::ERR_INVALID_RESPONSE); 203 return; 204 } 205 (*it)->callback.Run(); 206 requests_.erase(it); 207} 208 209void PermissionRequestCreatorApiary::DispatchNetworkError(RequestIterator it, 210 int error_code) { 211 DispatchGoogleServiceAuthError( 212 it, GoogleServiceAuthError::FromConnectionError(error_code)); 213} 214 215void PermissionRequestCreatorApiary::DispatchGoogleServiceAuthError( 216 RequestIterator it, 217 const GoogleServiceAuthError& error) { 218 (*it)->callback.Run(); 219 requests_.erase(it); 220} 221