OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/net/gaia/oauth2_mint_token_flow.h" | 5 #include "chrome/common/net/gaia/oauth2_mint_token_flow.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/json/json_reader.h" | |
13 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
15 #include "base/string_util.h" | |
16 #include "base/stringprintf.h" | |
17 #include "base/values.h" | |
14 #include "chrome/common/chrome_switches.h" | 18 #include "chrome/common/chrome_switches.h" |
15 #include "chrome/common/net/gaia/gaia_urls.h" | 19 #include "chrome/common/net/gaia/gaia_urls.h" |
16 #include "chrome/common/net/gaia/google_service_auth_error.h" | 20 #include "chrome/common/net/gaia/google_service_auth_error.h" |
21 #include "content/public/common/url_fetcher.h" | |
22 #include "net/base/escape.h" | |
23 #include "net/url_request/url_request_context_getter.h" | |
24 #include "net/url_request/url_request_status.h" | |
17 | 25 |
26 using content::URLFetcher; | |
18 using net::URLRequestContextGetter; | 27 using net::URLRequestContextGetter; |
28 using net::URLRequestStatus; | |
19 | 29 |
20 namespace { | 30 namespace { |
21 | 31 |
32 static const char kForceValueFalse[] = "false"; | |
33 static const char kForceValueTrue[] = "true"; | |
34 static const char kResponseTypeValueNone[] = "none"; | |
35 static const char kResponseTypeValueToken[] = "token"; | |
36 | |
37 static const char kOAuth2IssueTokenBodyFormat[] = | |
38 "force=%s" | |
39 "&response_type=%s" | |
40 "&scope=%s" | |
41 "&client_id=%s" | |
42 "&origin=%s"; | |
43 static const char kIssueAdviceKey[] = "issueAdvice"; | |
44 static const char kIssueAdviceValueAuto[] = "auto"; | |
45 static const char kIssueAdviceValueConsent[] = "consent"; | |
46 static const char kAccessTokenKey[] = "token"; | |
47 static const char kConsentKey[] = "consent"; | |
48 static const char kScopesKey[] = "scopes"; | |
49 static const char kDescriptionKey[] = "description"; | |
50 static const char kDetailKey[] = "detail"; | |
51 static const char kDetailSeparators[] = "\n"; | |
52 | |
53 static GoogleServiceAuthError CreateAuthError(URLRequestStatus status) { | |
54 CHECK(!status.is_success()); | |
55 if (status.status() == URLRequestStatus::CANCELED) { | |
56 return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); | |
57 } else { | |
58 DLOG(WARNING) << "Could not reach Google Accounts servers: errno " | |
59 << status.error(); | |
60 return GoogleServiceAuthError::FromConnectionError(status.error()); | |
61 } | |
62 } | |
63 | |
22 OAuth2MintTokenFlow::InterceptorForTests* g_interceptor_for_tests = NULL; | 64 OAuth2MintTokenFlow::InterceptorForTests* g_interceptor_for_tests = NULL; |
23 | 65 |
24 } // namespace | 66 } // namespace |
25 | 67 |
26 // static | 68 // static |
27 void OAuth2MintTokenFlow::SetInterceptorForTests( | 69 void OAuth2MintTokenFlow::SetInterceptorForTests( |
28 OAuth2MintTokenFlow::InterceptorForTests* interceptor) { | 70 OAuth2MintTokenFlow::InterceptorForTests* interceptor) { |
29 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)); | 71 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)); |
30 CHECK(NULL == g_interceptor_for_tests); // Only one at a time. | 72 CHECK(NULL == g_interceptor_for_tests); // Only one at a time. |
31 g_interceptor_for_tests = interceptor; | 73 g_interceptor_for_tests = interceptor; |
32 } | 74 } |
33 | 75 |
34 OAuth2MintTokenFlow::OAuth2MintTokenFlow( | 76 OAuth2MintTokenFlow::OAuth2MintTokenFlow( |
35 URLRequestContextGetter* context, | 77 URLRequestContextGetter* context, |
36 Delegate* delegate) | 78 Delegate* delegate, |
37 : context_(context), | 79 const Parameters& parameters) |
80 : OAuth2ApiCallFlow( | |
81 context, parameters.login_refresh_token, | |
82 "", std::vector<std::string>()), | |
83 context_(context), | |
38 delegate_(delegate), | 84 delegate_(delegate), |
39 state_(INITIAL) { | 85 parameters_(parameters) { |
40 } | 86 } |
41 | 87 |
42 OAuth2MintTokenFlow::~OAuth2MintTokenFlow() { } | 88 OAuth2MintTokenFlow::~OAuth2MintTokenFlow() { } |
43 | 89 |
44 void OAuth2MintTokenFlow::Start( | 90 void OAuth2MintTokenFlow::Start() { |
45 const std::string& login_refresh_token, | |
46 const std::string& extension_id, | |
47 const std::string& client_id, | |
48 const std::vector<std::string>& scopes) { | |
49 login_refresh_token_ = login_refresh_token; | |
50 extension_id_ = extension_id; | |
51 client_id_ = client_id; | |
52 scopes_ = scopes; | |
53 | |
54 if (g_interceptor_for_tests) { | 91 if (g_interceptor_for_tests) { |
55 std::string auth_token; | 92 std::string auth_token; |
56 GoogleServiceAuthError error = GoogleServiceAuthError::None(); | 93 GoogleServiceAuthError error = GoogleServiceAuthError::None(); |
57 | 94 |
58 // We use PostTask, instead of calling the delegate directly, because the | 95 // We use PostTask, instead of calling the delegate directly, because the |
59 // message loop will run a few times before we notify the delegate in the | 96 // message loop will run a few times before we notify the delegate in the |
60 // real implementation. | 97 // real implementation. |
61 if (g_interceptor_for_tests->DoIntercept(this, &auth_token, &error)) { | 98 if (g_interceptor_for_tests->DoIntercept(this, &auth_token, &error)) { |
62 MessageLoop::current()->PostTask( | 99 MessageLoop::current()->PostTask( |
63 FROM_HERE, | 100 FROM_HERE, |
64 base::Bind(&OAuth2MintTokenFlow::Delegate::OnMintTokenSuccess, | 101 base::Bind(&OAuth2MintTokenFlow::Delegate::OnMintTokenSuccess, |
65 base::Unretained(delegate_), auth_token)); | 102 base::Unretained(delegate_), auth_token)); |
66 } else { | 103 } else { |
67 MessageLoop::current()->PostTask( | 104 MessageLoop::current()->PostTask( |
68 FROM_HERE, | 105 FROM_HERE, |
69 base::Bind(&OAuth2MintTokenFlow::Delegate::OnMintTokenFailure, | 106 base::Bind(&OAuth2MintTokenFlow::Delegate::OnMintTokenFailure, |
70 base::Unretained(delegate_), error)); | 107 base::Unretained(delegate_), error)); |
71 } | 108 } |
72 return; | 109 return; |
73 } | 110 } |
74 | 111 |
75 BeginGetLoginAccessToken(); | 112 OAuth2ApiCallFlow::Start(); |
76 } | 113 } |
77 | 114 |
78 void OAuth2MintTokenFlow::OnGetTokenSuccess( | 115 void OAuth2MintTokenFlow::ReportSuccess(const std::string& access_token) { |
79 const std::string& access_token) { | 116 if (delegate_) { |
80 login_access_token_ = access_token; | 117 delegate_->OnMintTokenSuccess(access_token); |
81 EndGetLoginAccessToken(NULL); | |
82 } | |
83 | |
84 void OAuth2MintTokenFlow::OnGetTokenFailure( | |
85 const GoogleServiceAuthError& error) { | |
86 EndGetLoginAccessToken(&error); | |
87 } | |
88 | |
89 void OAuth2MintTokenFlow::BeginGetLoginAccessToken() { | |
90 CHECK_EQ(INITIAL, state_); | |
91 state_ = FETCH_LOGIN_ACCESS_TOKEN_STARTED; | |
92 | |
93 oauth2_access_token_fetcher_.reset(CreateAccessTokenFetcher()); | |
94 oauth2_access_token_fetcher_->Start( | |
95 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), | |
96 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), | |
97 login_refresh_token_, | |
98 std::vector<std::string>()); | |
99 } | |
100 | |
101 void OAuth2MintTokenFlow::EndGetLoginAccessToken( | |
102 const GoogleServiceAuthError* error) { | |
103 CHECK_EQ(FETCH_LOGIN_ACCESS_TOKEN_STARTED, state_); | |
104 if (!error) { | |
105 state_ = FETCH_LOGIN_ACCESS_TOKEN_DONE; | |
106 BeginMintAccessToken(); | |
107 } else { | |
108 state_ = ERROR_STATE; | |
109 ReportFailure(*error); | |
110 } | 118 } |
111 } | 119 } |
112 | 120 |
113 void OAuth2MintTokenFlow::OnMintTokenSuccess( | 121 void OAuth2MintTokenFlow::ReportSuccess(const IssueAdviceInfo& issue_advice) { |
114 const std::string& access_token) { | |
115 app_access_token_ = access_token; | |
116 EndMintAccessToken(NULL); | |
117 } | |
118 void OAuth2MintTokenFlow::OnMintTokenFailure( | |
119 const GoogleServiceAuthError& error) { | |
120 EndMintAccessToken(&error); | |
121 } | |
122 | |
123 void OAuth2MintTokenFlow::BeginMintAccessToken() { | |
124 CHECK_EQ(FETCH_LOGIN_ACCESS_TOKEN_DONE, state_); | |
125 state_ = MINT_ACCESS_TOKEN_STARTED; | |
126 | |
127 oauth2_mint_token_fetcher_.reset(CreateMintTokenFetcher()); | |
128 oauth2_mint_token_fetcher_->Start( | |
129 login_access_token_, | |
130 client_id_, | |
131 scopes_, | |
132 extension_id_); | |
133 } | |
134 | |
135 void OAuth2MintTokenFlow::EndMintAccessToken( | |
136 const GoogleServiceAuthError* error) { | |
137 CHECK_EQ(MINT_ACCESS_TOKEN_STARTED, state_); | |
138 | |
139 if (!error) { | |
140 state_ = MINT_ACCESS_TOKEN_DONE; | |
141 ReportSuccess(); | |
142 } else { | |
143 state_ = ERROR_STATE; | |
144 ReportFailure(*error); | |
145 } | |
146 } | |
147 | |
148 void OAuth2MintTokenFlow::ReportSuccess() { | |
149 CHECK_EQ(MINT_ACCESS_TOKEN_DONE, state_); | |
150 | |
151 if (delegate_) { | 122 if (delegate_) { |
152 delegate_->OnMintTokenSuccess(app_access_token_); | 123 delegate_->OnIssueAdviceSuccess(issue_advice); |
153 } | 124 } |
154 } | 125 } |
155 | 126 |
156 void OAuth2MintTokenFlow::ReportFailure( | 127 void OAuth2MintTokenFlow::ReportFailure( |
157 const GoogleServiceAuthError& error) { | 128 const GoogleServiceAuthError& error) { |
158 CHECK_EQ(ERROR_STATE, state_); | |
159 | |
160 if (delegate_) { | 129 if (delegate_) { |
161 delegate_->OnMintTokenFailure(error); | 130 delegate_->OnMintTokenFailure(error); |
162 } | 131 } |
163 } | 132 } |
164 | 133 |
165 OAuth2AccessTokenFetcher* OAuth2MintTokenFlow::CreateAccessTokenFetcher() { | 134 GURL OAuth2MintTokenFlow::CreateApiCallUrl() { |
166 return new OAuth2AccessTokenFetcher(this, context_); | 135 return GURL(GaiaUrls::GetInstance()->oauth2_issue_token_url()); |
167 } | 136 } |
168 | 137 |
169 OAuth2MintTokenFetcher* OAuth2MintTokenFlow::CreateMintTokenFetcher() { | 138 std::string OAuth2MintTokenFlow::CreateApiCallBody() { |
170 return new OAuth2MintTokenFetcher(this, context_, "OAuth2MintTokenFlow"); | 139 const char* force_value = |
140 (parameters_.mode == MODE_MINT_TOKEN_FORCE || | |
141 parameters_.mode == MODE_RECORD_GRANT) | |
142 ? kForceValueTrue : kForceValueFalse; | |
143 const char* response_type_value = | |
144 (parameters_.mode == MODE_MINT_TOKEN_NO_FORCE || | |
145 parameters_.mode == MODE_MINT_TOKEN_FORCE) | |
146 ? kResponseTypeValueToken : kResponseTypeValueNone; | |
147 return StringPrintf( | |
148 kOAuth2IssueTokenBodyFormat, | |
149 net::EscapeUrlEncodedData(force_value, true).c_str(), | |
150 net::EscapeUrlEncodedData(response_type_value, true).c_str(), | |
151 net::EscapeUrlEncodedData( | |
152 JoinString(parameters_.scopes, ' '), true).c_str(), | |
153 net::EscapeUrlEncodedData(parameters_.client_id, true).c_str(), | |
154 net::EscapeUrlEncodedData(parameters_.extension_id, true).c_str()); | |
171 } | 155 } |
156 | |
157 void OAuth2MintTokenFlow::ProcessApiCallSuccess( | |
158 const content::URLFetcher* source) { | |
159 // TODO(munjal): Change error code paths in this method to report an | |
160 // internal error. | |
161 scoped_ptr<base::DictionaryValue> dict(ParseResponse(source)); | |
162 if (!dict.get()) { | |
163 ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); | |
164 return; | |
165 } | |
166 std::string issue_advice; | |
167 if (!dict->GetString(kIssueAdviceKey, &issue_advice)) { | |
168 ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); | |
169 return; | |
170 } | |
171 if (issue_advice == kIssueAdviceValueConsent) { | |
172 IssueAdviceInfo issue_advice; | |
173 if (ParseIssueAdviceResponse(dict.get(), &issue_advice)) | |
174 ReportSuccess(issue_advice); | |
175 else | |
176 ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); | |
177 } else { | |
178 std::string access_token; | |
179 if (ParseMintTokenResponse(dict.get(), &access_token)) | |
180 ReportSuccess(access_token); | |
181 else | |
182 ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); | |
183 } | |
184 } | |
185 | |
186 void OAuth2MintTokenFlow::ProcessApiCallFailure( | |
187 const content::URLFetcher* source) { | |
188 ReportFailure(CreateAuthError(source->GetStatus())); | |
189 } | |
190 void OAuth2MintTokenFlow::ProcessNewAccessToken( | |
191 const std::string& access_token) { | |
192 // We don't currently store new access tokens. We generate one every time. | |
193 // So we have nothing to do here. | |
194 return; | |
195 } | |
196 void OAuth2MintTokenFlow::ProcessMintAccessTokenFailure( | |
197 const GoogleServiceAuthError& error) { | |
198 ReportFailure(error); | |
199 } | |
200 | |
201 // static | |
202 base::DictionaryValue* OAuth2MintTokenFlow::ParseResponse( | |
203 const URLFetcher* url_fetcher) { | |
204 CHECK(url_fetcher); | |
205 std::string response_body; | |
206 url_fetcher->GetResponseAsString(&response_body); | |
207 base::JSONReader reader; | |
208 scoped_ptr<base::Value> value(reader.Read(response_body, false)); | |
209 if (!value.get() || value->GetType() != Value::TYPE_DICTIONARY) | |
210 return NULL; | |
211 | |
212 return static_cast<base::DictionaryValue*>(value.release()); | |
asargent_no_longer_on_chrome
2012/04/10 22:47:25
nit: I think you can use Value::GetAsDictionary he
Munjal (Google)
2012/04/10 23:15:35
Done.
Note that it did not simplify a lot of the
| |
213 } | |
214 | |
215 // static | |
216 bool OAuth2MintTokenFlow::ParseMintTokenResponse( | |
217 const base::DictionaryValue* dict, std::string* access_token) { | |
218 CHECK(dict); | |
219 CHECK(access_token); | |
220 return dict->GetString(kAccessTokenKey, access_token); | |
221 } | |
222 | |
223 // static | |
224 bool OAuth2MintTokenFlow::ParseIssueAdviceResponse( | |
225 const base::DictionaryValue* dict, IssueAdviceInfo* issue_advice) { | |
226 CHECK(dict); | |
227 CHECK(issue_advice); | |
228 | |
229 base::DictionaryValue* consent_dict = NULL; | |
230 if (!dict->GetDictionary(kConsentKey, &consent_dict)) | |
231 return false; | |
232 | |
233 base::ListValue* scopes_list = NULL; | |
234 if (!consent_dict->GetList(kScopesKey, &scopes_list)) | |
235 return false; | |
236 | |
237 bool success = true; | |
238 for (size_t index = 0; index < scopes_list->GetSize(); ++index) { | |
239 base::DictionaryValue* scopes_entry = NULL; | |
240 if (!scopes_list->GetDictionary(index, &scopes_entry)) { | |
241 success = false; | |
242 break; | |
243 } | |
244 IssueAdviceInfoEntry entry; | |
245 if (!scopes_entry->GetString(kDescriptionKey, &entry.description)) { | |
246 success = false; | |
247 break; | |
248 } | |
249 std::string detail; | |
250 if (!scopes_entry->GetString(kDetailKey, &detail)) { | |
251 success = false; | |
252 break; | |
253 } | |
254 | |
255 Tokenize(detail, kDetailSeparators, &entry.details); | |
256 issue_advice->push_back(entry); | |
257 } | |
258 | |
259 if (!success) | |
260 issue_advice->clear(); | |
261 | |
262 return success; | |
263 } | |
OLD | NEW |