Chromium Code Reviews| 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; |
| } |