1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h" 6 7 #include <vector> 8 9 #include "base/message_loop/message_loop.h" 10 #include "base/run_loop.h" 11 #include "content/public/test/test_browser_thread.h" 12 #include "testing/gmock/include/gmock/gmock.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace extensions { 16 17 class FakeWebAuthFlow : public WebAuthFlow { 18 public: 19 explicit FakeWebAuthFlow(WebAuthFlow::Delegate* delegate) 20 : WebAuthFlow(delegate, 21 NULL, 22 GURL(), 23 WebAuthFlow::INTERACTIVE) {} 24 25 virtual void Start() OVERRIDE {} 26 }; 27 28 class TestGaiaWebAuthFlow : public GaiaWebAuthFlow { 29 public: 30 TestGaiaWebAuthFlow(GaiaWebAuthFlow::Delegate* delegate, 31 const ExtensionTokenKey* token_key, 32 const std::string oauth2_client_id, 33 GoogleServiceAuthError::State ubertoken_error_state) 34 : GaiaWebAuthFlow(delegate, NULL, token_key, oauth2_client_id, "en-us"), 35 ubertoken_error_(ubertoken_error_state) {} 36 37 virtual void Start() OVERRIDE { 38 if (ubertoken_error_.state() == GoogleServiceAuthError::NONE) 39 OnUbertokenSuccess("fake_ubertoken"); 40 else 41 OnUbertokenFailure(ubertoken_error_); 42 } 43 44 private: 45 virtual scoped_ptr<WebAuthFlow> CreateWebAuthFlow(GURL url) OVERRIDE { 46 return scoped_ptr<WebAuthFlow>(new FakeWebAuthFlow(this)); 47 } 48 49 GoogleServiceAuthError ubertoken_error_; 50 }; 51 52 class MockGaiaWebAuthFlowDelegate : public GaiaWebAuthFlow::Delegate { 53 public: 54 MOCK_METHOD3(OnGaiaFlowFailure, 55 void(GaiaWebAuthFlow::Failure failure, 56 GoogleServiceAuthError service_error, 57 const std::string& oauth_error)); 58 MOCK_METHOD2(OnGaiaFlowCompleted, 59 void(const std::string& access_token, 60 const std::string& expiration)); 61 }; 62 63 class IdentityGaiaWebAuthFlowTest : public testing::Test { 64 public: 65 IdentityGaiaWebAuthFlowTest() 66 : ubertoken_error_state_(GoogleServiceAuthError::NONE), 67 fake_ui_thread_(content::BrowserThread::UI, &message_loop_) {} 68 69 virtual void TearDown() { 70 testing::Test::TearDown(); 71 base::RunLoop loop; 72 loop.RunUntilIdle(); // Run tasks so FakeWebAuthFlows get deleted. 73 } 74 75 scoped_ptr<TestGaiaWebAuthFlow> CreateTestFlow() { 76 ExtensionTokenKey token_key( 77 "extension_id", "account_id", std::set<std::string>()); 78 return scoped_ptr<TestGaiaWebAuthFlow>(new TestGaiaWebAuthFlow( 79 &delegate_, &token_key, "fake.client.id", ubertoken_error_state_)); 80 } 81 82 std::string GetFinalTitle(const std::string& fragment) { 83 return std::string("Loading id.client.fake:/extension_id#") + fragment; 84 } 85 86 GoogleServiceAuthError GetNoneServiceError() { 87 return GoogleServiceAuthError(GoogleServiceAuthError::NONE); 88 } 89 90 void set_ubertoken_error( 91 GoogleServiceAuthError::State ubertoken_error_state) { 92 ubertoken_error_state_ = ubertoken_error_state; 93 } 94 95 protected: 96 testing::StrictMock<MockGaiaWebAuthFlowDelegate> delegate_; 97 GoogleServiceAuthError::State ubertoken_error_state_; 98 base::MessageLoop message_loop_; 99 content::TestBrowserThread fake_ui_thread_; 100 }; 101 102 TEST_F(IdentityGaiaWebAuthFlowTest, OAuthError) { 103 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 104 flow->Start(); 105 EXPECT_CALL(delegate_, OnGaiaFlowFailure( 106 GaiaWebAuthFlow::OAUTH_ERROR, 107 GoogleServiceAuthError(GoogleServiceAuthError::NONE), 108 "access_denied")); 109 flow->OnAuthFlowTitleChange(GetFinalTitle("error=access_denied")); 110 } 111 112 TEST_F(IdentityGaiaWebAuthFlowTest, Token) { 113 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 114 flow->Start(); 115 EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "")); 116 flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token")); 117 } 118 119 TEST_F(IdentityGaiaWebAuthFlowTest, TokenAndExpiration) { 120 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 121 flow->Start(); 122 EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "3600")); 123 flow->OnAuthFlowTitleChange( 124 GetFinalTitle("access_token=fake_access_token&expires_in=3600")); 125 } 126 127 TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersSuccess) { 128 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 129 flow->Start(); 130 EXPECT_CALL(delegate_, 131 OnGaiaFlowCompleted("fake_access_token", "3600")); 132 flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&" 133 "expires_in=3600&" 134 "chaff2=and&" 135 "nonerror=fake_error&" 136 "chaff3=nonsense&" 137 "access_token=fake_access_token&" 138 "chaff4=")); 139 } 140 141 TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersError) { 142 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 143 flow->Start(); 144 EXPECT_CALL(delegate_, OnGaiaFlowFailure( 145 GaiaWebAuthFlow::OAUTH_ERROR, 146 GoogleServiceAuthError(GoogleServiceAuthError::NONE), 147 "fake_error")); 148 flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&" 149 "expires_in=3600&" 150 "chaff2=and&" 151 "error=fake_error&" 152 "chaff3=nonsense&" 153 "access_token=fake_access_token&" 154 "chaff4=")); 155 } 156 157 TEST_F(IdentityGaiaWebAuthFlowTest, TitleSpam) { 158 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 159 flow->Start(); 160 flow->OnAuthFlowTitleChange( 161 "Loading https://extension_id.chromiumapp.org/#error=non_final_title"); 162 flow->OnAuthFlowTitleChange("I'm feeling entitled."); 163 flow->OnAuthFlowTitleChange(""); 164 flow->OnAuthFlowTitleChange( 165 "Loading id.client.fake:/bad_extension_id#error=non_final_title"); 166 flow->OnAuthFlowTitleChange( 167 "Loading bad.id.client.fake:/extension_id#error=non_final_title"); 168 EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "")); 169 flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token")); 170 } 171 172 TEST_F(IdentityGaiaWebAuthFlowTest, EmptyFragment) { 173 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 174 flow->Start(); 175 EXPECT_CALL( 176 delegate_, 177 OnGaiaFlowFailure( 178 GaiaWebAuthFlow::INVALID_REDIRECT, 179 GoogleServiceAuthError(GoogleServiceAuthError::NONE), 180 "")); 181 flow->OnAuthFlowTitleChange(GetFinalTitle("")); 182 } 183 184 TEST_F(IdentityGaiaWebAuthFlowTest, JunkFragment) { 185 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 186 flow->Start(); 187 EXPECT_CALL( 188 delegate_, 189 OnGaiaFlowFailure( 190 GaiaWebAuthFlow::INVALID_REDIRECT, 191 GoogleServiceAuthError(GoogleServiceAuthError::NONE), 192 "")); 193 flow->OnAuthFlowTitleChange(GetFinalTitle("thisisjustabunchofjunk")); 194 } 195 196 TEST_F(IdentityGaiaWebAuthFlowTest, NoFragment) { 197 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 198 flow->Start(); 199 // This won't be recognized as an interesting title. 200 flow->OnAuthFlowTitleChange("Loading id.client.fake:/extension_id"); 201 } 202 203 TEST_F(IdentityGaiaWebAuthFlowTest, Host) { 204 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 205 flow->Start(); 206 // These won't be recognized as interesting titles. 207 flow->OnAuthFlowTitleChange( 208 "Loading id.client.fake://extension_id#access_token=fake_access_token"); 209 flow->OnAuthFlowTitleChange( 210 "Loading id.client.fake://extension_id/#access_token=fake_access_token"); 211 flow->OnAuthFlowTitleChange( 212 "Loading " 213 "id.client.fake://host/extension_id/#access_token=fake_access_token"); 214 } 215 216 TEST_F(IdentityGaiaWebAuthFlowTest, UbertokenFailure) { 217 set_ubertoken_error(GoogleServiceAuthError::CONNECTION_FAILED); 218 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 219 EXPECT_CALL( 220 delegate_, 221 OnGaiaFlowFailure( 222 GaiaWebAuthFlow::SERVICE_AUTH_ERROR, 223 GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED), 224 "")); 225 flow->Start(); 226 } 227 228 TEST_F(IdentityGaiaWebAuthFlowTest, AuthFlowFailure) { 229 scoped_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow(); 230 flow->Start(); 231 EXPECT_CALL( 232 delegate_, 233 OnGaiaFlowFailure( 234 GaiaWebAuthFlow::WINDOW_CLOSED, 235 GoogleServiceAuthError(GoogleServiceAuthError::NONE), 236 "")); 237 flow->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); 238 } 239 240 } // namespace extensions 241