// Copyright (c) 2013 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/browser/extensions/api/identity/gaia_web_auth_flow.h" #include #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "content/public/test/test_browser_thread.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace extensions { class FakeWebAuthFlow : public WebAuthFlow { public: explicit FakeWebAuthFlow(WebAuthFlow::Delegate* delegate) : WebAuthFlow(delegate, NULL, GURL(), WebAuthFlow::INTERACTIVE) {} void Start() override {} }; class TestGaiaWebAuthFlow : public GaiaWebAuthFlow { public: TestGaiaWebAuthFlow(GaiaWebAuthFlow::Delegate* delegate, const ExtensionTokenKey* token_key, const std::string oauth2_client_id, GoogleServiceAuthError::State ubertoken_error_state) : GaiaWebAuthFlow(delegate, NULL, token_key, oauth2_client_id, "en-us"), ubertoken_error_(ubertoken_error_state) {} void Start() override { if (ubertoken_error_.state() == GoogleServiceAuthError::NONE) OnUbertokenSuccess("fake_ubertoken"); else OnUbertokenFailure(ubertoken_error_); } private: scoped_ptr CreateWebAuthFlow(GURL url) override { return scoped_ptr(new FakeWebAuthFlow(this)); } GoogleServiceAuthError ubertoken_error_; }; class MockGaiaWebAuthFlowDelegate : public GaiaWebAuthFlow::Delegate { public: MOCK_METHOD3(OnGaiaFlowFailure, void(GaiaWebAuthFlow::Failure failure, GoogleServiceAuthError service_error, const std::string& oauth_error)); MOCK_METHOD2(OnGaiaFlowCompleted, void(const std::string& access_token, const std::string& expiration)); }; class IdentityGaiaWebAuthFlowTest : public testing::Test { public: IdentityGaiaWebAuthFlowTest() : ubertoken_error_state_(GoogleServiceAuthError::NONE), fake_ui_thread_(content::BrowserThread::UI, &message_loop_) {} virtual void TearDown() { testing::Test::TearDown(); base::RunLoop loop; loop.RunUntilIdle(); // Run tasks so FakeWebAuthFlows get deleted. } scoped_ptr CreateTestFlow() { ExtensionTokenKey token_key( "extension_id", "account_id", std::set()); return scoped_ptr(new TestGaiaWebAuthFlow( &delegate_, &token_key, "fake.client.id", ubertoken_error_state_)); } std::string GetFinalTitle(const std::string& fragment) { return std::string("Loading id.client.fake:/extension_id#") + fragment; } GoogleServiceAuthError GetNoneServiceError() { return GoogleServiceAuthError(GoogleServiceAuthError::NONE); } void set_ubertoken_error( GoogleServiceAuthError::State ubertoken_error_state) { ubertoken_error_state_ = ubertoken_error_state; } protected: testing::StrictMock delegate_; GoogleServiceAuthError::State ubertoken_error_state_; base::MessageLoop message_loop_; content::TestBrowserThread fake_ui_thread_; }; TEST_F(IdentityGaiaWebAuthFlowTest, OAuthError) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL(delegate_, OnGaiaFlowFailure( GaiaWebAuthFlow::OAUTH_ERROR, GoogleServiceAuthError(GoogleServiceAuthError::NONE), "access_denied")); flow->OnAuthFlowTitleChange(GetFinalTitle("error=access_denied")); } TEST_F(IdentityGaiaWebAuthFlowTest, Token) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "")); flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token")); } TEST_F(IdentityGaiaWebAuthFlowTest, TokenAndExpiration) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "3600")); flow->OnAuthFlowTitleChange( GetFinalTitle("access_token=fake_access_token&expires_in=3600")); } TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersSuccess) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "3600")); flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&" "expires_in=3600&" "chaff2=and&" "nonerror=fake_error&" "chaff3=nonsense&" "access_token=fake_access_token&" "chaff4=")); } TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersError) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL(delegate_, OnGaiaFlowFailure( GaiaWebAuthFlow::OAUTH_ERROR, GoogleServiceAuthError(GoogleServiceAuthError::NONE), "fake_error")); flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&" "expires_in=3600&" "chaff2=and&" "error=fake_error&" "chaff3=nonsense&" "access_token=fake_access_token&" "chaff4=")); } TEST_F(IdentityGaiaWebAuthFlowTest, TitleSpam) { scoped_ptr flow = CreateTestFlow(); flow->Start(); flow->OnAuthFlowTitleChange( "Loading https://extension_id.chromiumapp.org/#error=non_final_title"); flow->OnAuthFlowTitleChange("I'm feeling entitled."); flow->OnAuthFlowTitleChange(""); flow->OnAuthFlowTitleChange( "Loading id.client.fake:/bad_extension_id#error=non_final_title"); flow->OnAuthFlowTitleChange( "Loading bad.id.client.fake:/extension_id#error=non_final_title"); EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "")); flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token")); } TEST_F(IdentityGaiaWebAuthFlowTest, EmptyFragment) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL( delegate_, OnGaiaFlowFailure( GaiaWebAuthFlow::INVALID_REDIRECT, GoogleServiceAuthError(GoogleServiceAuthError::NONE), "")); flow->OnAuthFlowTitleChange(GetFinalTitle("")); } TEST_F(IdentityGaiaWebAuthFlowTest, JunkFragment) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL( delegate_, OnGaiaFlowFailure( GaiaWebAuthFlow::INVALID_REDIRECT, GoogleServiceAuthError(GoogleServiceAuthError::NONE), "")); flow->OnAuthFlowTitleChange(GetFinalTitle("thisisjustabunchofjunk")); } TEST_F(IdentityGaiaWebAuthFlowTest, NoFragment) { scoped_ptr flow = CreateTestFlow(); flow->Start(); // This won't be recognized as an interesting title. flow->OnAuthFlowTitleChange("Loading id.client.fake:/extension_id"); } TEST_F(IdentityGaiaWebAuthFlowTest, Host) { scoped_ptr flow = CreateTestFlow(); flow->Start(); // These won't be recognized as interesting titles. flow->OnAuthFlowTitleChange( "Loading id.client.fake://extension_id#access_token=fake_access_token"); flow->OnAuthFlowTitleChange( "Loading id.client.fake://extension_id/#access_token=fake_access_token"); flow->OnAuthFlowTitleChange( "Loading " "id.client.fake://host/extension_id/#access_token=fake_access_token"); } TEST_F(IdentityGaiaWebAuthFlowTest, UbertokenFailure) { set_ubertoken_error(GoogleServiceAuthError::CONNECTION_FAILED); scoped_ptr flow = CreateTestFlow(); EXPECT_CALL( delegate_, OnGaiaFlowFailure( GaiaWebAuthFlow::SERVICE_AUTH_ERROR, GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED), "")); flow->Start(); } TEST_F(IdentityGaiaWebAuthFlowTest, AuthFlowFailure) { scoped_ptr flow = CreateTestFlow(); flow->Start(); EXPECT_CALL( delegate_, OnGaiaFlowFailure( GaiaWebAuthFlow::WINDOW_CLOSED, GoogleServiceAuthError(GoogleServiceAuthError::NONE), "")); flow->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); } } // namespace extensions