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