service_resolver_64: Correctly check all the bytes of the service code.
[chromium-blink-merge.git] / google_apis / gaia / oauth2_mint_token_flow.cc
blob9818f089ab791a1bf1c01bfb097485f23317dbea
1 // Copyright (c) 2012 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 "google_apis/gaia/oauth2_mint_token_flow.h"
7 #include <string>
8 #include <vector>
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/json/json_reader.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "google_apis/gaia/gaia_urls.h"
21 #include "google_apis/gaia/google_service_auth_error.h"
22 #include "net/base/escape.h"
23 #include "net/url_request/url_fetcher.h"
24 #include "net/url_request/url_request_context_getter.h"
25 #include "net/url_request/url_request_status.h"
27 using net::URLFetcher;
28 using net::URLRequestContextGetter;
29 using net::URLRequestStatus;
31 namespace {
33 const char kForceValueFalse[] = "false";
34 const char kForceValueTrue[] = "true";
35 const char kResponseTypeValueNone[] = "none";
36 const char kResponseTypeValueToken[] = "token";
38 const char kOAuth2IssueTokenBodyFormat[] =
39 "force=%s"
40 "&response_type=%s"
41 "&scope=%s"
42 "&client_id=%s"
43 "&origin=%s";
44 // TODO(pavely): lib_ver is passed to differentiate IssueToken requests from
45 // different code locations. Remove once device_id mismatch is understood.
46 // (crbug.com/481596)
47 const char kOAuth2IssueTokenBodyFormatDeviceIdAddendum[] =
48 "&device_id=%s&device_type=chrome&lib_ver=extension";
49 const char kIssueAdviceKey[] = "issueAdvice";
50 const char kIssueAdviceValueConsent[] = "consent";
51 const char kAccessTokenKey[] = "token";
52 const char kConsentKey[] = "consent";
53 const char kExpiresInKey[] = "expiresIn";
54 const char kScopesKey[] = "scopes";
55 const char kDescriptionKey[] = "description";
56 const char kDetailKey[] = "detail";
57 const char kDetailSeparators[] = "\n";
58 const char kError[] = "error";
59 const char kMessage[] = "message";
61 static GoogleServiceAuthError CreateAuthError(const net::URLFetcher* source) {
62 URLRequestStatus status = source->GetStatus();
63 if (status.status() == URLRequestStatus::CANCELED) {
64 return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
66 if (status.status() == URLRequestStatus::FAILED) {
67 DLOG(WARNING) << "Server returned error: errno " << status.error();
68 return GoogleServiceAuthError::FromConnectionError(status.error());
71 std::string response_body;
72 source->GetResponseAsString(&response_body);
73 scoped_ptr<base::Value> value = base::JSONReader::Read(response_body);
74 base::DictionaryValue* response;
75 if (!value.get() || !value->GetAsDictionary(&response)) {
76 return GoogleServiceAuthError::FromUnexpectedServiceResponse(
77 base::StringPrintf(
78 "Not able to parse a JSON object from a service response. "
79 "HTTP Status of the response is: %d", source->GetResponseCode()));
81 base::DictionaryValue* error;
82 if (!response->GetDictionary(kError, &error)) {
83 return GoogleServiceAuthError::FromUnexpectedServiceResponse(
84 "Not able to find a detailed error in a service response.");
86 std::string message;
87 if (!error->GetString(kMessage, &message)) {
88 return GoogleServiceAuthError::FromUnexpectedServiceResponse(
89 "Not able to find an error message within a service error.");
91 return GoogleServiceAuthError::FromServiceError(message);
94 } // namespace
96 IssueAdviceInfoEntry::IssueAdviceInfoEntry() {}
97 IssueAdviceInfoEntry::~IssueAdviceInfoEntry() {}
99 bool IssueAdviceInfoEntry::operator ==(const IssueAdviceInfoEntry& rhs) const {
100 return description == rhs.description && details == rhs.details;
103 OAuth2MintTokenFlow::Parameters::Parameters() : mode(MODE_ISSUE_ADVICE) {}
105 OAuth2MintTokenFlow::Parameters::Parameters(
106 const std::string& eid,
107 const std::string& cid,
108 const std::vector<std::string>& scopes_arg,
109 const std::string& device_id,
110 Mode mode_arg)
111 : extension_id(eid),
112 client_id(cid),
113 scopes(scopes_arg),
114 device_id(device_id),
115 mode(mode_arg) {
118 OAuth2MintTokenFlow::Parameters::~Parameters() {}
120 OAuth2MintTokenFlow::OAuth2MintTokenFlow(Delegate* delegate,
121 const Parameters& parameters)
122 : delegate_(delegate), parameters_(parameters), weak_factory_(this) {
125 OAuth2MintTokenFlow::~OAuth2MintTokenFlow() { }
127 void OAuth2MintTokenFlow::ReportSuccess(const std::string& access_token,
128 int time_to_live) {
129 if (delegate_)
130 delegate_->OnMintTokenSuccess(access_token, time_to_live);
132 // |this| may already be deleted.
135 void OAuth2MintTokenFlow::ReportIssueAdviceSuccess(
136 const IssueAdviceInfo& issue_advice) {
137 if (delegate_)
138 delegate_->OnIssueAdviceSuccess(issue_advice);
140 // |this| may already be deleted.
143 void OAuth2MintTokenFlow::ReportFailure(
144 const GoogleServiceAuthError& error) {
145 if (delegate_)
146 delegate_->OnMintTokenFailure(error);
148 // |this| may already be deleted.
151 GURL OAuth2MintTokenFlow::CreateApiCallUrl() {
152 return GaiaUrls::GetInstance()->oauth2_issue_token_url();
155 std::string OAuth2MintTokenFlow::CreateApiCallBody() {
156 const char* force_value =
157 (parameters_.mode == MODE_MINT_TOKEN_FORCE ||
158 parameters_.mode == MODE_RECORD_GRANT)
159 ? kForceValueTrue : kForceValueFalse;
160 const char* response_type_value =
161 (parameters_.mode == MODE_MINT_TOKEN_NO_FORCE ||
162 parameters_.mode == MODE_MINT_TOKEN_FORCE)
163 ? kResponseTypeValueToken : kResponseTypeValueNone;
164 std::string body = base::StringPrintf(
165 kOAuth2IssueTokenBodyFormat,
166 net::EscapeUrlEncodedData(force_value, true).c_str(),
167 net::EscapeUrlEncodedData(response_type_value, true).c_str(),
168 net::EscapeUrlEncodedData(
169 JoinString(parameters_.scopes, ' '), true).c_str(),
170 net::EscapeUrlEncodedData(parameters_.client_id, true).c_str(),
171 net::EscapeUrlEncodedData(parameters_.extension_id, true).c_str());
172 if (!parameters_.device_id.empty()) {
173 body.append(base::StringPrintf(
174 kOAuth2IssueTokenBodyFormatDeviceIdAddendum,
175 net::EscapeUrlEncodedData(parameters_.device_id, true).c_str()));
177 return body;
180 void OAuth2MintTokenFlow::ProcessApiCallSuccess(
181 const net::URLFetcher* source) {
182 std::string response_body;
183 source->GetResponseAsString(&response_body);
184 scoped_ptr<base::Value> value = base::JSONReader::Read(response_body);
185 base::DictionaryValue* dict = NULL;
186 if (!value.get() || !value->GetAsDictionary(&dict)) {
187 ReportFailure(GoogleServiceAuthError::FromUnexpectedServiceResponse(
188 "Not able to parse a JSON object from a service response."));
189 return;
192 std::string issue_advice_value;
193 if (!dict->GetString(kIssueAdviceKey, &issue_advice_value)) {
194 ReportFailure(GoogleServiceAuthError::FromUnexpectedServiceResponse(
195 "Not able to find an issueAdvice in a service response."));
196 return;
198 if (issue_advice_value == kIssueAdviceValueConsent) {
199 IssueAdviceInfo issue_advice;
200 if (ParseIssueAdviceResponse(dict, &issue_advice))
201 ReportIssueAdviceSuccess(issue_advice);
202 else
203 ReportFailure(GoogleServiceAuthError::FromUnexpectedServiceResponse(
204 "Not able to parse the contents of consent "
205 "from a service response."));
206 } else {
207 std::string access_token;
208 int time_to_live;
209 if (ParseMintTokenResponse(dict, &access_token, &time_to_live))
210 ReportSuccess(access_token, time_to_live);
211 else
212 ReportFailure(GoogleServiceAuthError::FromUnexpectedServiceResponse(
213 "Not able to parse the contents of access token "
214 "from a service response."));
217 // |this| may be deleted!
220 void OAuth2MintTokenFlow::ProcessApiCallFailure(
221 const net::URLFetcher* source) {
222 ReportFailure(CreateAuthError(source));
225 // static
226 bool OAuth2MintTokenFlow::ParseMintTokenResponse(
227 const base::DictionaryValue* dict, std::string* access_token,
228 int* time_to_live) {
229 CHECK(dict);
230 CHECK(access_token);
231 CHECK(time_to_live);
232 std::string ttl_string;
233 return dict->GetString(kExpiresInKey, &ttl_string) &&
234 base::StringToInt(ttl_string, time_to_live) &&
235 dict->GetString(kAccessTokenKey, access_token);
238 // static
239 bool OAuth2MintTokenFlow::ParseIssueAdviceResponse(
240 const base::DictionaryValue* dict, IssueAdviceInfo* issue_advice) {
241 CHECK(dict);
242 CHECK(issue_advice);
244 const base::DictionaryValue* consent_dict = NULL;
245 if (!dict->GetDictionary(kConsentKey, &consent_dict))
246 return false;
248 const base::ListValue* scopes_list = NULL;
249 if (!consent_dict->GetList(kScopesKey, &scopes_list))
250 return false;
252 bool success = true;
253 for (size_t index = 0; index < scopes_list->GetSize(); ++index) {
254 const base::DictionaryValue* scopes_entry = NULL;
255 IssueAdviceInfoEntry entry;
256 base::string16 detail;
257 if (!scopes_list->GetDictionary(index, &scopes_entry) ||
258 !scopes_entry->GetString(kDescriptionKey, &entry.description) ||
259 !scopes_entry->GetString(kDetailKey, &detail)) {
260 success = false;
261 break;
264 base::TrimWhitespace(entry.description, base::TRIM_ALL, &entry.description);
265 static const base::string16 detail_separators =
266 base::ASCIIToUTF16(kDetailSeparators);
267 Tokenize(detail, detail_separators, &entry.details);
268 for (size_t i = 0; i < entry.details.size(); i++)
269 base::TrimWhitespace(entry.details[i], base::TRIM_ALL, &entry.details[i]);
270 issue_advice->push_back(entry);
273 if (!success)
274 issue_advice->clear();
276 return success;