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/run_loop.h"
10 #include "content/public/browser/storage_partition.h"
11 #include "content/public/test/browser_task_environment.h"
12 #include "content/public/test/test_storage_partition.h"
13 #include "services/network/test/test_cookie_manager.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace extensions {
18 
19 class DeleteCookiesTestCookieManager : public network::TestCookieManager {
20  public:
DeleteCookies(network::mojom::CookieDeletionFilterPtr filter,DeleteCookiesCallback callback)21   void DeleteCookies(network::mojom::CookieDeletionFilterPtr filter,
22                      DeleteCookiesCallback callback) override {
23     cookies_deleted_ = true;
24     std::move(callback).Run(0U);
25   }
cookies_deleted()26   bool cookies_deleted() { return cookies_deleted_; }
27 
28  private:
29   bool cookies_deleted_ = false;
30 };
31 
32 class FakeWebAuthFlow : public WebAuthFlow {
33  public:
FakeWebAuthFlow(WebAuthFlow::Delegate * delegate)34   explicit FakeWebAuthFlow(WebAuthFlow::Delegate* delegate)
35       : WebAuthFlow(delegate, nullptr, GURL(), WebAuthFlow::INTERACTIVE) {
36     storage_partition.set_cookie_manager_for_browser_process(&cookie_manager);
37   }
38 
Start()39   void Start() override { EXPECT_TRUE(cookie_manager.cookies_deleted()); }
40 
GetGuestPartition()41   content::StoragePartition* GetGuestPartition() override {
42     return &storage_partition;
43   }
44 
45  private:
46   content::TestStoragePartition storage_partition;
47   DeleteCookiesTestCookieManager cookie_manager;
48 };
49 
50 class TestGaiaWebAuthFlow : public GaiaWebAuthFlow {
51  public:
TestGaiaWebAuthFlow(GaiaWebAuthFlow::Delegate * delegate,const ExtensionTokenKey * token_key,const std::string oauth2_client_id,GoogleServiceAuthError::State ubertoken_error_state)52   TestGaiaWebAuthFlow(GaiaWebAuthFlow::Delegate* delegate,
53                       const ExtensionTokenKey* token_key,
54                       const std::string oauth2_client_id,
55                       GoogleServiceAuthError::State ubertoken_error_state)
56       : GaiaWebAuthFlow(delegate, NULL, token_key, oauth2_client_id, "en-us"),
57         ubertoken_error_(ubertoken_error_state) {}
58 
Start()59   void Start() override {
60     OnUbertokenFetchComplete(
61         ubertoken_error_,
62         ubertoken_error_.state() == GoogleServiceAuthError::NONE
63             ? "fake_ubertoken"
64             : std::string());
65   }
66 
67  private:
CreateWebAuthFlow(GURL url)68   std::unique_ptr<WebAuthFlow> CreateWebAuthFlow(GURL url) override {
69     return std::unique_ptr<WebAuthFlow>(new FakeWebAuthFlow(this));
70   }
71 
72   GoogleServiceAuthError ubertoken_error_;
73 };
74 
75 class MockGaiaWebAuthFlowDelegate : public GaiaWebAuthFlow::Delegate {
76  public:
77   MOCK_METHOD3(OnGaiaFlowFailure,
78                void(GaiaWebAuthFlow::Failure failure,
79                     GoogleServiceAuthError service_error,
80                     const std::string& oauth_error));
81   MOCK_METHOD2(OnGaiaFlowCompleted,
82                void(const std::string& access_token,
83                     const std::string& expiration));
84 };
85 
86 class IdentityGaiaWebAuthFlowTest : public testing::Test {
87  public:
IdentityGaiaWebAuthFlowTest()88   IdentityGaiaWebAuthFlowTest()
89       : ubertoken_error_state_(GoogleServiceAuthError::NONE) {}
90 
TearDown()91   void TearDown() override {
92     testing::Test::TearDown();
93     base::RunLoop loop;
94     loop.RunUntilIdle();  // Run tasks so FakeWebAuthFlows get deleted.
95   }
96 
CreateTestFlow()97   std::unique_ptr<TestGaiaWebAuthFlow> CreateTestFlow() {
98     ExtensionTokenKey token_key("extension_id", CoreAccountId("account_id"),
99                                 std::set<std::string>());
100     return std::unique_ptr<TestGaiaWebAuthFlow>(new TestGaiaWebAuthFlow(
101         &delegate_, &token_key, "fake.client.id", ubertoken_error_state_));
102   }
103 
GetFinalTitle(const std::string & fragment)104   std::string GetFinalTitle(const std::string& fragment) {
105     return std::string("Loading id.client.fake:/extension_id#") + fragment;
106   }
107 
GetNoneServiceError()108   GoogleServiceAuthError GetNoneServiceError() {
109     return GoogleServiceAuthError(GoogleServiceAuthError::NONE);
110   }
111 
set_ubertoken_error(GoogleServiceAuthError::State ubertoken_error_state)112   void set_ubertoken_error(
113       GoogleServiceAuthError::State ubertoken_error_state) {
114     ubertoken_error_state_ = ubertoken_error_state;
115   }
116 
117  protected:
118   testing::StrictMock<MockGaiaWebAuthFlowDelegate> delegate_;
119   GoogleServiceAuthError::State ubertoken_error_state_;
120   content::BrowserTaskEnvironment task_environment_;
121 };
122 
TEST_F(IdentityGaiaWebAuthFlowTest,OAuthError)123 TEST_F(IdentityGaiaWebAuthFlowTest, OAuthError) {
124   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
125   flow->Start();
126   EXPECT_CALL(delegate_, OnGaiaFlowFailure(
127           GaiaWebAuthFlow::OAUTH_ERROR,
128           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
129           "access_denied"));
130   flow->OnAuthFlowTitleChange(GetFinalTitle("error=access_denied"));
131 }
132 
TEST_F(IdentityGaiaWebAuthFlowTest,Token)133 TEST_F(IdentityGaiaWebAuthFlowTest, Token) {
134   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
135   flow->Start();
136   EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", ""));
137   flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token"));
138 }
139 
TEST_F(IdentityGaiaWebAuthFlowTest,TokenAndExpiration)140 TEST_F(IdentityGaiaWebAuthFlowTest, TokenAndExpiration) {
141   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
142   flow->Start();
143   EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "3600"));
144   flow->OnAuthFlowTitleChange(
145       GetFinalTitle("access_token=fake_access_token&expires_in=3600"));
146 }
147 
TEST_F(IdentityGaiaWebAuthFlowTest,ExtraFragmentParametersSuccess)148 TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersSuccess) {
149   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
150   flow->Start();
151   EXPECT_CALL(delegate_,
152               OnGaiaFlowCompleted("fake_access_token", "3600"));
153   flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&"
154                                             "expires_in=3600&"
155                                             "chaff2=and&"
156                                             "nonerror=fake_error&"
157                                             "chaff3=nonsense&"
158                                             "access_token=fake_access_token&"
159                                             "chaff4="));
160 }
161 
TEST_F(IdentityGaiaWebAuthFlowTest,ExtraFragmentParametersError)162 TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersError) {
163   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
164   flow->Start();
165   EXPECT_CALL(delegate_, OnGaiaFlowFailure(
166           GaiaWebAuthFlow::OAUTH_ERROR,
167           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
168           "fake_error"));
169   flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&"
170                                             "expires_in=3600&"
171                                             "chaff2=and&"
172                                             "error=fake_error&"
173                                             "chaff3=nonsense&"
174                                             "access_token=fake_access_token&"
175                                             "chaff4="));
176 }
177 
TEST_F(IdentityGaiaWebAuthFlowTest,TitleSpam)178 TEST_F(IdentityGaiaWebAuthFlowTest, TitleSpam) {
179   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
180   flow->Start();
181   flow->OnAuthFlowTitleChange(
182       "Loading https://extension_id.chromiumapp.org/#error=non_final_title");
183   flow->OnAuthFlowTitleChange("I'm feeling entitled.");
184   flow->OnAuthFlowTitleChange("");
185   flow->OnAuthFlowTitleChange(
186       "Loading id.client.fake:/bad_extension_id#error=non_final_title");
187   flow->OnAuthFlowTitleChange(
188       "Loading bad.id.client.fake:/extension_id#error=non_final_title");
189   EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", ""));
190   flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token"));
191 }
192 
TEST_F(IdentityGaiaWebAuthFlowTest,EmptyFragment)193 TEST_F(IdentityGaiaWebAuthFlowTest, EmptyFragment) {
194   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
195   flow->Start();
196   EXPECT_CALL(
197       delegate_,
198       OnGaiaFlowFailure(
199           GaiaWebAuthFlow::INVALID_REDIRECT,
200           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
201           ""));
202   flow->OnAuthFlowTitleChange(GetFinalTitle(""));
203 }
204 
TEST_F(IdentityGaiaWebAuthFlowTest,JunkFragment)205 TEST_F(IdentityGaiaWebAuthFlowTest, JunkFragment) {
206   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
207   flow->Start();
208   EXPECT_CALL(
209       delegate_,
210       OnGaiaFlowFailure(
211           GaiaWebAuthFlow::INVALID_REDIRECT,
212           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
213           ""));
214   flow->OnAuthFlowTitleChange(GetFinalTitle("thisisjustabunchofjunk"));
215 }
216 
TEST_F(IdentityGaiaWebAuthFlowTest,NoFragment)217 TEST_F(IdentityGaiaWebAuthFlowTest, NoFragment) {
218   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
219   flow->Start();
220   // This won't be recognized as an interesting title.
221   flow->OnAuthFlowTitleChange("Loading id.client.fake:/extension_id");
222 }
223 
TEST_F(IdentityGaiaWebAuthFlowTest,Host)224 TEST_F(IdentityGaiaWebAuthFlowTest, Host) {
225   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
226   flow->Start();
227   // These won't be recognized as interesting titles.
228   flow->OnAuthFlowTitleChange(
229       "Loading id.client.fake://extension_id#access_token=fake_access_token");
230   flow->OnAuthFlowTitleChange(
231       "Loading id.client.fake://extension_id/#access_token=fake_access_token");
232   flow->OnAuthFlowTitleChange(
233       "Loading "
234       "id.client.fake://host/extension_id/#access_token=fake_access_token");
235 }
236 
TEST_F(IdentityGaiaWebAuthFlowTest,UbertokenFailure)237 TEST_F(IdentityGaiaWebAuthFlowTest, UbertokenFailure) {
238   set_ubertoken_error(GoogleServiceAuthError::CONNECTION_FAILED);
239   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
240   EXPECT_CALL(
241       delegate_,
242       OnGaiaFlowFailure(
243           GaiaWebAuthFlow::SERVICE_AUTH_ERROR,
244           GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
245           ""));
246   flow->Start();
247 }
248 
TEST_F(IdentityGaiaWebAuthFlowTest,AuthFlowFailure)249 TEST_F(IdentityGaiaWebAuthFlowTest, AuthFlowFailure) {
250   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
251   flow->Start();
252   EXPECT_CALL(
253       delegate_,
254       OnGaiaFlowFailure(
255           GaiaWebAuthFlow::WINDOW_CLOSED,
256           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
257           ""));
258   flow->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
259 }
260 
261 }  // namespace extensions
262