Index: chrome/common/net/gaia/oauth2_mint_token_flow.cc |
=================================================================== |
--- chrome/common/net/gaia/oauth2_mint_token_flow.cc (revision 130985) |
+++ chrome/common/net/gaia/oauth2_mint_token_flow.cc (working copy) |
@@ -10,15 +10,57 @@ |
#include "base/basictypes.h" |
#include "base/bind.h" |
#include "base/command_line.h" |
+#include "base/json/json_reader.h" |
#include "base/message_loop.h" |
+#include "base/string_util.h" |
+#include "base/stringprintf.h" |
+#include "base/values.h" |
#include "chrome/common/chrome_switches.h" |
#include "chrome/common/net/gaia/gaia_urls.h" |
#include "chrome/common/net/gaia/google_service_auth_error.h" |
+#include "content/public/common/url_fetcher.h" |
+#include "net/base/escape.h" |
+#include "net/url_request/url_request_context_getter.h" |
+#include "net/url_request/url_request_status.h" |
+using content::URLFetcher; |
using net::URLRequestContextGetter; |
+using net::URLRequestStatus; |
namespace { |
+static const char kForceValueFalse[] = "false"; |
+static const char kForceValueTrue[] = "true"; |
+static const char kResponseTypeValueNone[] = "none"; |
+static const char kResponseTypeValueToken[] = "token"; |
+ |
+static const char kOAuth2IssueTokenBodyFormat[] = |
+ "force=%s" |
+ "&response_type=%s" |
+ "&scope=%s" |
+ "&client_id=%s" |
+ "&origin=%s"; |
+static const char kIssueAdviceKey[] = "issueAdvice"; |
+static const char kIssueAdviceValueAuto[] = "auto"; |
+static const char kIssueAdviceValueConsent[] = "consent"; |
+static const char kAccessTokenKey[] = "token"; |
+static const char kConsentKey[] = "consent"; |
+static const char kScopesKey[] = "scopes"; |
+static const char kDescriptionKey[] = "description"; |
+static const char kDetailKey[] = "detail"; |
+static const char kDetailSeparators[] = "\n"; |
+ |
+static GoogleServiceAuthError CreateAuthError(URLRequestStatus status) { |
+ CHECK(!status.is_success()); |
+ if (status.status() == URLRequestStatus::CANCELED) { |
+ return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); |
+ } else { |
+ DLOG(WARNING) << "Could not reach Google Accounts servers: errno " |
+ << status.error(); |
+ return GoogleServiceAuthError::FromConnectionError(status.error()); |
+ } |
+} |
+ |
OAuth2MintTokenFlow::InterceptorForTests* g_interceptor_for_tests = NULL; |
} // namespace |
@@ -33,24 +75,19 @@ |
OAuth2MintTokenFlow::OAuth2MintTokenFlow( |
URLRequestContextGetter* context, |
- Delegate* delegate) |
- : context_(context), |
+ Delegate* delegate, |
+ const Parameters& parameters) |
+ : OAuth2ApiCallFlow( |
+ context, parameters.login_refresh_token, |
+ "", std::vector<std::string>()), |
+ context_(context), |
delegate_(delegate), |
- state_(INITIAL) { |
+ parameters_(parameters) { |
} |
OAuth2MintTokenFlow::~OAuth2MintTokenFlow() { } |
-void OAuth2MintTokenFlow::Start( |
- const std::string& login_refresh_token, |
- const std::string& extension_id, |
- const std::string& client_id, |
- const std::vector<std::string>& scopes) { |
- login_refresh_token_ = login_refresh_token; |
- extension_id_ = extension_id; |
- client_id_ = client_id; |
- scopes_ = scopes; |
- |
+void OAuth2MintTokenFlow::Start() { |
if (g_interceptor_for_tests) { |
std::string auth_token; |
GoogleServiceAuthError error = GoogleServiceAuthError::None(); |
@@ -72,100 +109,157 @@ |
return; |
} |
- BeginGetLoginAccessToken(); |
+ OAuth2ApiCallFlow::Start(); |
} |
-void OAuth2MintTokenFlow::OnGetTokenSuccess( |
- const std::string& access_token) { |
- login_access_token_ = access_token; |
- EndGetLoginAccessToken(NULL); |
+void OAuth2MintTokenFlow::ReportSuccess(const std::string& access_token) { |
+ if (delegate_) { |
+ delegate_->OnMintTokenSuccess(access_token); |
+ } |
} |
-void OAuth2MintTokenFlow::OnGetTokenFailure( |
+void OAuth2MintTokenFlow::ReportSuccess(const IssueAdviceInfo& issue_advice) { |
+ if (delegate_) { |
+ delegate_->OnIssueAdviceSuccess(issue_advice); |
+ } |
+} |
+ |
+void OAuth2MintTokenFlow::ReportFailure( |
const GoogleServiceAuthError& error) { |
- EndGetLoginAccessToken(&error); |
+ if (delegate_) { |
+ delegate_->OnMintTokenFailure(error); |
+ } |
} |
-void OAuth2MintTokenFlow::BeginGetLoginAccessToken() { |
- CHECK_EQ(INITIAL, state_); |
- state_ = FETCH_LOGIN_ACCESS_TOKEN_STARTED; |
+GURL OAuth2MintTokenFlow::CreateApiCallUrl() { |
+ return GURL(GaiaUrls::GetInstance()->oauth2_issue_token_url()); |
+} |
- oauth2_access_token_fetcher_.reset(CreateAccessTokenFetcher()); |
- oauth2_access_token_fetcher_->Start( |
- GaiaUrls::GetInstance()->oauth2_chrome_client_id(), |
- GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), |
- login_refresh_token_, |
- std::vector<std::string>()); |
+std::string OAuth2MintTokenFlow::CreateApiCallBody() { |
+ const char* force_value = |
+ (parameters_.mode == MODE_MINT_TOKEN_FORCE || |
+ parameters_.mode == MODE_RECORD_GRANT) |
+ ? kForceValueTrue : kForceValueFalse; |
+ const char* response_type_value = |
+ (parameters_.mode == MODE_MINT_TOKEN_NO_FORCE || |
+ parameters_.mode == MODE_MINT_TOKEN_FORCE) |
+ ? kResponseTypeValueToken : kResponseTypeValueNone; |
+ return StringPrintf( |
+ kOAuth2IssueTokenBodyFormat, |
+ net::EscapeUrlEncodedData(force_value, true).c_str(), |
+ net::EscapeUrlEncodedData(response_type_value, true).c_str(), |
+ net::EscapeUrlEncodedData( |
+ JoinString(parameters_.scopes, ' '), true).c_str(), |
+ net::EscapeUrlEncodedData(parameters_.client_id, true).c_str(), |
+ net::EscapeUrlEncodedData(parameters_.extension_id, true).c_str()); |
} |
-void OAuth2MintTokenFlow::EndGetLoginAccessToken( |
- const GoogleServiceAuthError* error) { |
- CHECK_EQ(FETCH_LOGIN_ACCESS_TOKEN_STARTED, state_); |
- if (!error) { |
- state_ = FETCH_LOGIN_ACCESS_TOKEN_DONE; |
- BeginMintAccessToken(); |
+void OAuth2MintTokenFlow::ProcessApiCallSuccess( |
+ const content::URLFetcher* source) { |
+ // TODO(munjal): Change error code paths in this method to report an |
+ // internal error. |
+ scoped_ptr<base::DictionaryValue> dict(ParseResponse(source)); |
+ if (!dict.get()) { |
+ ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); |
+ return; |
+ } |
+ std::string issue_advice; |
+ if (!dict->GetString(kIssueAdviceKey, &issue_advice)) { |
+ ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); |
+ return; |
+ } |
+ if (issue_advice == kIssueAdviceValueConsent) { |
+ IssueAdviceInfo issue_advice; |
+ if (ParseIssueAdviceResponse(dict.get(), &issue_advice)) |
+ ReportSuccess(issue_advice); |
+ else |
+ ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); |
} else { |
- state_ = ERROR_STATE; |
- ReportFailure(*error); |
+ std::string access_token; |
+ if (ParseMintTokenResponse(dict.get(), &access_token)) |
+ ReportSuccess(access_token); |
+ else |
+ ReportFailure(GoogleServiceAuthError::FromConnectionError(101)); |
} |
} |
-void OAuth2MintTokenFlow::OnMintTokenSuccess( |
+void OAuth2MintTokenFlow::ProcessApiCallFailure( |
+ const content::URLFetcher* source) { |
+ ReportFailure(CreateAuthError(source->GetStatus())); |
+} |
+void OAuth2MintTokenFlow::ProcessNewAccessToken( |
const std::string& access_token) { |
- app_access_token_ = access_token; |
- EndMintAccessToken(NULL); |
+ // We don't currently store new access tokens. We generate one every time. |
+ // So we have nothing to do here. |
+ return; |
} |
-void OAuth2MintTokenFlow::OnMintTokenFailure( |
+void OAuth2MintTokenFlow::ProcessMintAccessTokenFailure( |
const GoogleServiceAuthError& error) { |
- EndMintAccessToken(&error); |
+ ReportFailure(error); |
} |
-void OAuth2MintTokenFlow::BeginMintAccessToken() { |
- CHECK_EQ(FETCH_LOGIN_ACCESS_TOKEN_DONE, state_); |
- state_ = MINT_ACCESS_TOKEN_STARTED; |
+// static |
+base::DictionaryValue* OAuth2MintTokenFlow::ParseResponse( |
+ const URLFetcher* url_fetcher) { |
+ CHECK(url_fetcher); |
+ std::string response_body; |
+ url_fetcher->GetResponseAsString(&response_body); |
+ base::JSONReader reader; |
jstritar
2012/04/11 19:14:29
Do we need to be parsing this in a sandboxed utili
Munjal (Google)
2012/04/11 19:37:55
I don't think we need sandboxing here. There is al
|
+ scoped_ptr<base::Value> value(reader.Read(response_body, false)); |
+ DictionaryValue* result = NULL; |
+ if (!value.get() || !value->GetAsDictionary(&result)) |
+ return NULL; |
- oauth2_mint_token_fetcher_.reset(CreateMintTokenFetcher()); |
- oauth2_mint_token_fetcher_->Start( |
- login_access_token_, |
- client_id_, |
- scopes_, |
- extension_id_); |
+ value.release(); |
+ return result; |
} |
-void OAuth2MintTokenFlow::EndMintAccessToken( |
- const GoogleServiceAuthError* error) { |
- CHECK_EQ(MINT_ACCESS_TOKEN_STARTED, state_); |
- |
- if (!error) { |
- state_ = MINT_ACCESS_TOKEN_DONE; |
- ReportSuccess(); |
- } else { |
- state_ = ERROR_STATE; |
- ReportFailure(*error); |
- } |
+// static |
+bool OAuth2MintTokenFlow::ParseMintTokenResponse( |
+ const base::DictionaryValue* dict, std::string* access_token) { |
+ CHECK(dict); |
+ CHECK(access_token); |
+ return dict->GetString(kAccessTokenKey, access_token); |
} |
-void OAuth2MintTokenFlow::ReportSuccess() { |
- CHECK_EQ(MINT_ACCESS_TOKEN_DONE, state_); |
+// static |
+bool OAuth2MintTokenFlow::ParseIssueAdviceResponse( |
+ const base::DictionaryValue* dict, IssueAdviceInfo* issue_advice) { |
+ CHECK(dict); |
+ CHECK(issue_advice); |
- if (delegate_) { |
- delegate_->OnMintTokenSuccess(app_access_token_); |
- } |
-} |
+ base::DictionaryValue* consent_dict = NULL; |
+ if (!dict->GetDictionary(kConsentKey, &consent_dict)) |
+ return false; |
-void OAuth2MintTokenFlow::ReportFailure( |
- const GoogleServiceAuthError& error) { |
- CHECK_EQ(ERROR_STATE, state_); |
+ base::ListValue* scopes_list = NULL; |
+ if (!consent_dict->GetList(kScopesKey, &scopes_list)) |
+ return false; |
- if (delegate_) { |
- delegate_->OnMintTokenFailure(error); |
+ bool success = true; |
+ for (size_t index = 0; index < scopes_list->GetSize(); ++index) { |
+ base::DictionaryValue* scopes_entry = NULL; |
+ if (!scopes_list->GetDictionary(index, &scopes_entry)) { |
jstritar
2012/04/11 19:14:29
Not required, but you could compact this a bit by
Munjal (Google)
2012/04/11 19:37:55
Done.
I like it. I think I have a tendency to do
|
+ success = false; |
+ break; |
+ } |
+ IssueAdviceInfoEntry entry; |
+ if (!scopes_entry->GetString(kDescriptionKey, &entry.description)) { |
+ success = false; |
+ break; |
+ } |
+ std::string detail; |
+ if (!scopes_entry->GetString(kDetailKey, &detail)) { |
+ success = false; |
+ break; |
+ } |
+ |
+ Tokenize(detail, kDetailSeparators, &entry.details); |
+ issue_advice->push_back(entry); |
} |
-} |
-OAuth2AccessTokenFetcher* OAuth2MintTokenFlow::CreateAccessTokenFetcher() { |
- return new OAuth2AccessTokenFetcher(this, context_); |
-} |
+ if (!success) |
+ issue_advice->clear(); |
-OAuth2MintTokenFetcher* OAuth2MintTokenFlow::CreateMintTokenFetcher() { |
- return new OAuth2MintTokenFetcher(this, context_, "OAuth2MintTokenFlow"); |
+ return success; |
} |