summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormunjal@chromium.org <munjal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-06 06:40:40 +0000
committermunjal@chromium.org <munjal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-06 06:40:40 +0000
commit992f31daff8f7c8e4f1a37eea22196dbbb716fb0 (patch)
tree2f84214a81b242298869c8e2e40a1ff80f00e86e
parent4c5efadb9fc23637f82ccc2be85b1312f6d515ae (diff)
downloadchromium_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.gypi3
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/net/gaia/oauth2_revocation_consumer.h23
-rw-r--r--chrome/common/net/gaia/oauth2_revocation_fetcher.cc163
-rw-r--r--chrome/common/net/gaia/oauth2_revocation_fetcher.h95
-rw-r--r--chrome/common/net/gaia/oauth2_revocation_fetcher_unittest.cc121
-rw-r--r--chrome/common/net/http_return.h1
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,