diff options
author | munjal@chromium.org <munjal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-06 06:40:40 +0000 |
---|---|---|
committer | munjal@chromium.org <munjal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-06 06:40:40 +0000 |
commit | 992f31daff8f7c8e4f1a37eea22196dbbb716fb0 (patch) | |
tree | 2f84214a81b242298869c8e2e40a1ff80f00e86e | |
parent | 4c5efadb9fc23637f82ccc2be85b1312f6d515ae (diff) | |
download | chromium_src-992f31daff8f7c8e4f1a37eea22196dbbb716fb0.zip chromium_src-992f31daff8f7c8e4f1a37eea22196dbbb716fb0.tar.gz chromium_src-992f31daff8f7c8e4f1a37eea22196dbbb716fb0.tar.bz2 |
Add OAuth2 revocation fetcher that is right now used for notifications.
Add unit tests.
Review URL: http://codereview.chromium.org/8803029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113132 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/chrome_common.gypi | 3 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/net/gaia/oauth2_revocation_consumer.h | 23 | ||||
-rw-r--r-- | chrome/common/net/gaia/oauth2_revocation_fetcher.cc | 163 | ||||
-rw-r--r-- | chrome/common/net/gaia/oauth2_revocation_fetcher.h | 95 | ||||
-rw-r--r-- | chrome/common/net/gaia/oauth2_revocation_fetcher_unittest.cc | 121 | ||||
-rw-r--r-- | chrome/common/net/http_return.h | 1 |
7 files changed, 407 insertions, 0 deletions
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 764b6c8..ddb7bb0 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -384,6 +384,9 @@ 'common/net/gaia/oauth2_access_token_consumer.h', 'common/net/gaia/oauth2_access_token_fetcher.cc', 'common/net/gaia/oauth2_access_token_fetcher.h', + 'common/net/gaia/oauth2_revocation_consumer.h', + 'common/net/gaia/oauth2_revocation_fetcher.cc', + 'common/net/gaia/oauth2_revocation_fetcher.h', 'common/net/x509_certificate_model.cc', 'common/net/x509_certificate_model_nss.cc', 'common/net/x509_certificate_model_openssl.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 6a1600e..247f414 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1983,6 +1983,7 @@ 'common/net/gaia/google_service_auth_error_unittest.cc', 'common/net/gaia/oauth_request_signer_unittest.cc', 'common/net/gaia/oauth2_access_token_fetcher_unittest.cc', + 'common/net/gaia/oauth2_revocation_fetcher_unittest.cc', 'common/random_unittest.cc', 'common/service_process_util_unittest.cc', 'common/string_ordinal_unittest.cc', diff --git a/chrome/common/net/gaia/oauth2_revocation_consumer.h b/chrome/common/net/gaia/oauth2_revocation_consumer.h new file mode 100644 index 0000000..4e8f995 --- /dev/null +++ b/chrome/common/net/gaia/oauth2_revocation_consumer.h @@ -0,0 +1,23 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NET_GAIA_OAUTH2_REVOCATION_CONSUMER_H_ +#define CHROME_COMMON_NET_GAIA_OAUTH2_REVOCATION_CONSUMER_H_ +#pragma once + +#include <string> + +class GoogleServiceAuthError; + +// An interface that defines the callbacks for consumers to which +// OAuth2RevocationFetcher can return results. +class OAuth2RevocationConsumer { + public: + virtual ~OAuth2RevocationConsumer() {} + + virtual void OnRevocationSuccess() {} + virtual void OnRevocationFailure(const GoogleServiceAuthError& error) {} +}; + +#endif // CHROME_COMMON_NET_GAIA_OAUTH2_REVOCATION_CONSUMER_H_ diff --git a/chrome/common/net/gaia/oauth2_revocation_fetcher.cc b/chrome/common/net/gaia/oauth2_revocation_fetcher.cc new file mode 100644 index 0000000..8164e77 --- /dev/null +++ b/chrome/common/net/gaia/oauth2_revocation_fetcher.cc @@ -0,0 +1,163 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/net/gaia/oauth2_revocation_fetcher.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/json/json_reader.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "base/values.h" +#include "chrome/common/net/gaia/gaia_urls.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" +#include "chrome/common/net/http_return.h" +#include "net/base/escape.h" +#include "net/base/load_flags.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_status.h" + +using content::URLFetcher; +using content::URLFetcherDelegate; +using net::ResponseCookies; +using net::URLRequestContextGetter; +using net::URLRequestStatus; + +namespace { +static const char kOAuth2RevokeTokenURL[] = + "https://www.googleapis.com/oauth2/v2/RevokeToken"; + +static const char kAuthorizationHeaderFormat[] = + "Authorization: Bearer %s"; + +static const char kRevocationBodyFormat[] = + "client_id=%s&origin=%s"; + +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()); + } +} + +static URLFetcher* CreateFetcher(URLRequestContextGetter* getter, + const GURL& url, + const std::string& header, + const std::string& body, + URLFetcherDelegate* delegate) { + bool empty_body = body.empty(); + URLFetcher* result = URLFetcher::Create( + 0, url, + empty_body ? URLFetcher::GET : URLFetcher::POST, + delegate); + + result->SetRequestContext(getter); + result->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES); + if (!header.empty()) + result->SetExtraRequestHeaders(header); + + if (!empty_body) + result->SetUploadData("application/x-www-form-urlencoded", body); + + return result; +} +} // namespace + +OAuth2RevocationFetcher::OAuth2RevocationFetcher( + OAuth2RevocationConsumer* consumer, + URLRequestContextGetter* getter) + : consumer_(consumer), + getter_(getter), + state_(INITIAL) { } + +OAuth2RevocationFetcher::~OAuth2RevocationFetcher() { } + +void OAuth2RevocationFetcher::CancelRequest() { + fetcher_.reset(); +} + +void OAuth2RevocationFetcher::Start(const std::string& access_token, + const std::string& client_id, + const std::string& origin) { + access_token_ = access_token; + client_id_ = client_id; + origin_ = origin; + StartRevocation(); +} + +void OAuth2RevocationFetcher::StartRevocation() { + CHECK_EQ(INITIAL, state_); + state_ = REVOCATION_STARTED; + fetcher_.reset(CreateFetcher( + getter_, + MakeRevocationUrl(), + MakeRevocationHeader(access_token_), + MakeRevocationBody(client_id_, origin_), + this)); + fetcher_->Start(); // OnURLFetchComplete will be called. +} + +void OAuth2RevocationFetcher::EndRevocation(const URLFetcher* source) { + CHECK_EQ(REVOCATION_STARTED, state_); + state_ = REVOCATION_DONE; + + URLRequestStatus status = source->GetStatus(); + if (!status.is_success()) { + OnRevocationFailure(CreateAuthError(status)); + return; + } + + if (source->GetResponseCode() != RC_REQUEST_OK_EMPTY_BODY) { + OnRevocationFailure(GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + return; + } + + OnRevocationSuccess(); +} + +void OAuth2RevocationFetcher::OnRevocationSuccess() { + consumer_->OnRevocationSuccess(); +} + +void OAuth2RevocationFetcher::OnRevocationFailure( + GoogleServiceAuthError error) { + state_ = ERROR_STATE; + consumer_->OnRevocationFailure(error); +} + +void OAuth2RevocationFetcher::OnURLFetchComplete(const URLFetcher* source) { + CHECK(source); + EndRevocation(source); +} + +// static +GURL OAuth2RevocationFetcher::MakeRevocationUrl() { + return GURL(kOAuth2RevokeTokenURL); +} + +// static +std::string OAuth2RevocationFetcher::MakeRevocationHeader( + const std::string& access_token) { + return StringPrintf(kAuthorizationHeaderFormat, access_token.c_str()); +} + +// static +std::string OAuth2RevocationFetcher::MakeRevocationBody( + const std::string& client_id, + const std::string& origin) { + std::string enc_client_id = net::EscapeUrlEncodedData(client_id, true); + std::string enc_origin = net::EscapeUrlEncodedData(origin, true); + return StringPrintf( + kRevocationBodyFormat, + enc_client_id.c_str(), + enc_origin.c_str()); +} diff --git a/chrome/common/net/gaia/oauth2_revocation_fetcher.h b/chrome/common/net/gaia/oauth2_revocation_fetcher.h new file mode 100644 index 0000000..190e379 --- /dev/null +++ b/chrome/common/net/gaia/oauth2_revocation_fetcher.h @@ -0,0 +1,95 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NET_GAIA_OAUTH2_REVOCATION_FETCHER_H_ +#define CHROME_COMMON_NET_GAIA_OAUTH2_REVOCATION_FETCHER_H_ +#pragma once + +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/common/net/gaia/oauth2_revocation_consumer.h" +#include "content/public/common/url_fetcher.h" +#include "content/public/common/url_fetcher_delegate.h" +#include "googleurl/src/gurl.h" + +class OAuth2RevocationFetcherTest; + +namespace net { +class URLRequestContextGetter; +class URLRequestStatus; +} + +// Abstracts the details to perform OAuth2 grant revocation. +// +// This class should be used on a single thread, but it can be whichever thread +// that you like. +// Also, do not reuse the same instance. Once Start() is called, the instance +// should not be reused. +// +// Usage: +// * Create an instance with a consumer. +// * Call Start() +// * The consumer passed in the constructor will be called on the same +// thread Start was called with the results. +// +// This class can handle one request at a time. To parallelize requests, +// create multiple instances. +class OAuth2RevocationFetcher : public content::URLFetcherDelegate { + public: + OAuth2RevocationFetcher(OAuth2RevocationConsumer* consumer, + net::URLRequestContextGetter* getter); + virtual ~OAuth2RevocationFetcher(); + + // Starts the flow with the given parameters. + // |access_token| should be an OAuth2 login scoped access token. + void Start(const std::string& access_token, + const std::string& client_id, + const std::string& origin); + + void CancelRequest(); + + // Implementation of content::URLFetcherDelegate + virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE; + + private: + enum State { + INITIAL, + REVOCATION_STARTED, + REVOCATION_DONE, + ERROR_STATE, + }; + + // Helper methods for the flow. + void StartRevocation(); + void EndRevocation(const content::URLFetcher* source); + + // Helper mehtods for reporting back results. + void OnRevocationSuccess(); + void OnRevocationFailure(GoogleServiceAuthError error); + + // Other helpers. + static GURL MakeRevocationUrl(); + static std::string MakeRevocationHeader(const std::string& access_token); + static std::string MakeRevocationBody(const std::string& client_id, + const std::string& origin); + + // State that is set during construction. + OAuth2RevocationConsumer* const consumer_; + net::URLRequestContextGetter* const getter_; + State state_; + + // While a fetch is in progress. + scoped_ptr<content::URLFetcher> fetcher_; + std::string access_token_; + std::string client_id_; + std::string origin_; + + friend class OAuth2RevocationFetcherTest; + + DISALLOW_COPY_AND_ASSIGN(OAuth2RevocationFetcher); +}; + +#endif // CHROME_COMMON_NET_GAIA_OAUTH2_REVOCATION_FETCHER_H_ diff --git a/chrome/common/net/gaia/oauth2_revocation_fetcher_unittest.cc b/chrome/common/net/gaia/oauth2_revocation_fetcher_unittest.cc new file mode 100644 index 0000000..2b03cda --- /dev/null +++ b/chrome/common/net/gaia/oauth2_revocation_fetcher_unittest.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A complete set of unit tests for OAuth2RevocationFetcher. + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "chrome/common/net/gaia/gaia_urls.h" +#include "chrome/common/net/gaia/google_service_auth_error.h" +#include "chrome/common/net/http_return.h" +#include "chrome/common/net/gaia/oauth2_revocation_consumer.h" +#include "chrome/common/net/gaia/oauth2_revocation_fetcher.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/common/url_fetcher.h" +#include "content/public/common/url_fetcher_delegate.h" +#include "content/public/common/url_fetcher_factory.h" +#include "content/test/test_browser_thread.h" +#include "content/test/test_url_fetcher_factory.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_status.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::BrowserThread; +using content::URLFetcher; +using content::URLFetcherDelegate; +using content::URLFetcherFactory; +using net::ResponseCookies; +using net::URLRequestStatus; +using testing::_; +using testing::Return; + +namespace { + +class MockUrlFetcherFactory : public ScopedURLFetcherFactory, + public URLFetcherFactory { +public: + MockUrlFetcherFactory() + : ScopedURLFetcherFactory(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + } + virtual ~MockUrlFetcherFactory() {} + + MOCK_METHOD4( + CreateURLFetcher, + URLFetcher* (int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcherDelegate* d)); +}; + +class MockOAuth2RevocationConsumer : public OAuth2RevocationConsumer { + public: + MockOAuth2RevocationConsumer() {} + ~MockOAuth2RevocationConsumer() {} + + MOCK_METHOD0(OnRevocationSuccess, void()); + MOCK_METHOD1(OnRevocationFailure, + void(const GoogleServiceAuthError& error)); +}; + +} + +class OAuth2RevocationFetcherTest : public testing::Test { + public: + OAuth2RevocationFetcherTest() + : ui_thread_(BrowserThread::UI, &message_loop_), + fetcher_(&consumer_, profile_.GetRequestContext()) { + } + + virtual ~OAuth2RevocationFetcherTest() { } + + virtual TestURLFetcher* SetupRevocation( + bool fetch_succeeds, int response_code) { + GURL url = OAuth2RevocationFetcher::MakeRevocationUrl(); + TestURLFetcher* url_fetcher = new TestURLFetcher(0, url, &fetcher_); + URLRequestStatus::Status status = + fetch_succeeds ? URLRequestStatus::SUCCESS : URLRequestStatus::FAILED; + url_fetcher->set_status(URLRequestStatus(status, 0)); + + if (response_code != 0) + url_fetcher->set_response_code(response_code); + + EXPECT_CALL(factory_, CreateURLFetcher(_, url, _, _)) + .WillOnce(Return(url_fetcher)); + return url_fetcher; + } + + protected: + MessageLoop message_loop_; + content::TestBrowserThread ui_thread_; + MockUrlFetcherFactory factory_; + MockOAuth2RevocationConsumer consumer_; + TestingProfile profile_; + OAuth2RevocationFetcher fetcher_; +}; + +TEST_F(OAuth2RevocationFetcherTest, RequestFailure) { + TestURLFetcher* url_fetcher = SetupRevocation(false, 0); + EXPECT_CALL(consumer_, OnRevocationFailure(_)).Times(1); + fetcher_.Start("access_token", "client_id", "origin"); + fetcher_.OnURLFetchComplete(url_fetcher); +} + +TEST_F(OAuth2RevocationFetcherTest, ResponseCodeFailure) { + TestURLFetcher* url_fetcher = SetupRevocation(true, RC_FORBIDDEN); + EXPECT_CALL(consumer_, OnRevocationFailure(_)).Times(1); + fetcher_.Start("access_token", "client_id", "origin"); + fetcher_.OnURLFetchComplete(url_fetcher); +} + +TEST_F(OAuth2RevocationFetcherTest, Success) { + TestURLFetcher* url_fetcher = SetupRevocation( + true, RC_REQUEST_OK_EMPTY_BODY); + EXPECT_CALL(consumer_, OnRevocationSuccess()).Times(1); + fetcher_.Start("access_token", "client_id", "origin"); + fetcher_.OnURLFetchComplete(url_fetcher); +} diff --git a/chrome/common/net/http_return.h b/chrome/common/net/http_return.h index f2059cb..783627f 100644 --- a/chrome/common/net/http_return.h +++ b/chrome/common/net/http_return.h @@ -10,6 +10,7 @@ // contains a few HTTP return codes. Add more HTTP return codes. enum HTTPReturnCode { RC_REQUEST_OK = 200, + RC_REQUEST_OK_EMPTY_BODY = 204, RC_BAD_REQUEST = 400, RC_UNAUTHORIZED = 401, RC_FORBIDDEN = 403, |