safe_browsing_token_fetcher_impl_unittest.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // Copyright 2021 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. #include "weblayer/browser/safe_browsing/safe_browsing_token_fetcher_impl.h"
  5. #include "content/public/test/browser_task_environment.h"
  6. #include "google_apis/gaia/gaia_constants.h"
  7. #include "testing/gtest/include/gtest/gtest.h"
  8. #include "weblayer/public/google_account_access_token_fetch_delegate.h"
  9. namespace weblayer {
  10. namespace {
  11. // Callback passed to SafeBrowsingTokenFetcherImpl to be invoked on
  12. // access token fetch completion.
  13. void OnAccessTokenFetched(base::OnceClosure quit_closure,
  14. std::string* target_token,
  15. const std::string& received_token) {
  16. *target_token = received_token;
  17. std::move(quit_closure).Run();
  18. }
  19. // Test implementation of GoogleAccountAccessTokenFetchDelegate.
  20. class TestAccessTokenFetchDelegate
  21. : public GoogleAccountAccessTokenFetchDelegate {
  22. public:
  23. TestAccessTokenFetchDelegate() {}
  24. ~TestAccessTokenFetchDelegate() override {}
  25. TestAccessTokenFetchDelegate(const TestAccessTokenFetchDelegate&) = delete;
  26. TestAccessTokenFetchDelegate& operator=(const TestAccessTokenFetchDelegate&) =
  27. delete;
  28. // GoogleAccountAccessTokenFetchDelegate:
  29. void FetchAccessToken(const std::set<std::string>& scopes,
  30. OnTokenFetchedCallback callback) override {
  31. most_recent_request_id_++;
  32. // All access token requests made by SafeBrowsingTokenFetcherImpl should be
  33. // for the safe browsing scope.
  34. std::set<std::string> expected_scopes = {
  35. GaiaConstants::kChromeSafeBrowsingOAuth2Scope};
  36. EXPECT_EQ(expected_scopes, scopes);
  37. outstanding_callbacks_[most_recent_request_id_] = std::move(callback);
  38. }
  39. void OnAccessTokenIdentifiedAsInvalid(const std::set<std::string>& scopes,
  40. const std::string& token) override {
  41. // All invalid token notifications originating from
  42. // SafeBrowsingTokenFetcherImpl should be for the safe browsing scope.
  43. std::set<std::string> expected_scopes = {
  44. GaiaConstants::kChromeSafeBrowsingOAuth2Scope};
  45. EXPECT_EQ(expected_scopes, scopes);
  46. invalid_token_ = token;
  47. }
  48. int get_num_outstanding_requests() { return outstanding_callbacks_.size(); }
  49. int get_most_recent_request_id() { return most_recent_request_id_; }
  50. const std::string& get_most_recent_invalid_token() { return invalid_token_; }
  51. void RespondWithTokenForRequest(int request_id, const std::string& token) {
  52. ASSERT_TRUE(outstanding_callbacks_.count(request_id));
  53. auto callback = std::move(outstanding_callbacks_[request_id]);
  54. outstanding_callbacks_.erase(request_id);
  55. std::move(callback).Run(token);
  56. }
  57. private:
  58. int most_recent_request_id_ = 0;
  59. std::map<int, OnTokenFetchedCallback> outstanding_callbacks_;
  60. std::string invalid_token_;
  61. };
  62. } // namespace
  63. class SafeBrowsingTokenFetcherImplTest : public testing::Test {
  64. public:
  65. SafeBrowsingTokenFetcherImplTest() = default;
  66. SafeBrowsingTokenFetcherImplTest(const SafeBrowsingTokenFetcherImplTest&) =
  67. delete;
  68. SafeBrowsingTokenFetcherImplTest& operator=(
  69. const SafeBrowsingTokenFetcherImplTest&) = delete;
  70. protected:
  71. content::BrowserTaskEnvironment* task_environment() {
  72. return &task_environment_;
  73. }
  74. private:
  75. content::BrowserTaskEnvironment task_environment_{
  76. content::BrowserTaskEnvironment::TimeSource::MOCK_TIME};
  77. };
  78. // Tests that SafeBrowsingTokenFetcherImpl responds with an empty token when
  79. // there is no delegate available to fetch tokens from.
  80. TEST_F(SafeBrowsingTokenFetcherImplTest, NoDelegate) {
  81. base::RunLoop run_loop;
  82. std::string access_token = "dummy";
  83. SafeBrowsingTokenFetcherImpl fetcher(base::BindRepeating(
  84. []() -> GoogleAccountAccessTokenFetchDelegate* { return nullptr; }));
  85. fetcher.Start(base::BindOnce(&OnAccessTokenFetched, run_loop.QuitClosure(),
  86. &access_token));
  87. run_loop.Run();
  88. EXPECT_EQ("", access_token);
  89. }
  90. TEST_F(SafeBrowsingTokenFetcherImplTest, SuccessfulTokenFetch) {
  91. TestAccessTokenFetchDelegate delegate;
  92. base::RunLoop run_loop;
  93. std::string access_token = "";
  94. std::string kTokenFromResponse = "token";
  95. SafeBrowsingTokenFetcherImpl fetcher(base::BindRepeating(
  96. [](TestAccessTokenFetchDelegate* delegate)
  97. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  98. &delegate));
  99. fetcher.Start(base::BindOnce(&OnAccessTokenFetched, run_loop.QuitClosure(),
  100. &access_token));
  101. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  102. EXPECT_EQ("", access_token);
  103. delegate.RespondWithTokenForRequest(delegate.get_most_recent_request_id(),
  104. kTokenFromResponse);
  105. run_loop.Run();
  106. EXPECT_EQ(kTokenFromResponse, access_token);
  107. }
  108. // Verifies that destruction of a SafeBrowsingTokenFetcherImpl instance from
  109. // within the client callback that the token was fetched doesn't cause a crash.
  110. TEST_F(SafeBrowsingTokenFetcherImplTest,
  111. FetcherDestroyedFromWithinOnTokenFetchedCallback) {
  112. TestAccessTokenFetchDelegate delegate;
  113. base::RunLoop run_loop;
  114. std::string access_token = "";
  115. std::string kTokenFromResponse = "token";
  116. // Destroyed in the token fetch callback.
  117. auto* fetcher = new SafeBrowsingTokenFetcherImpl(base::BindRepeating(
  118. [](TestAccessTokenFetchDelegate* delegate)
  119. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  120. &delegate));
  121. fetcher->Start(base::BindOnce(
  122. [](base::OnceClosure quit_closure, std::string* target_token,
  123. SafeBrowsingTokenFetcherImpl* fetcher, const std::string& token) {
  124. *target_token = token;
  125. delete fetcher;
  126. std::move(quit_closure).Run();
  127. },
  128. run_loop.QuitClosure(), &access_token, fetcher));
  129. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  130. EXPECT_EQ("", access_token);
  131. delegate.RespondWithTokenForRequest(delegate.get_most_recent_request_id(),
  132. kTokenFromResponse);
  133. run_loop.Run();
  134. EXPECT_EQ(kTokenFromResponse, access_token);
  135. }
  136. // Tests correct operation in the case of concurrent requests to
  137. // SafeBrowsingTokenFetcherImpl.
  138. TEST_F(SafeBrowsingTokenFetcherImplTest, ConcurrentRequests) {
  139. TestAccessTokenFetchDelegate delegate;
  140. base::RunLoop run_loop1;
  141. base::RunLoop run_loop2;
  142. std::string access_token1 = "";
  143. std::string access_token2 = "";
  144. std::string kTokenFromResponse1 = "token1";
  145. std::string kTokenFromResponse2 = "token2";
  146. SafeBrowsingTokenFetcherImpl fetcher(base::BindRepeating(
  147. [](TestAccessTokenFetchDelegate* delegate)
  148. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  149. &delegate));
  150. fetcher.Start(base::BindOnce(&OnAccessTokenFetched, run_loop1.QuitClosure(),
  151. &access_token1));
  152. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  153. int request_id1 = delegate.get_most_recent_request_id();
  154. EXPECT_EQ("", access_token1);
  155. EXPECT_EQ("", access_token2);
  156. fetcher.Start(base::BindOnce(&OnAccessTokenFetched, run_loop2.QuitClosure(),
  157. &access_token2));
  158. EXPECT_EQ(2, delegate.get_num_outstanding_requests());
  159. int request_id2 = delegate.get_most_recent_request_id();
  160. EXPECT_EQ("", access_token1);
  161. EXPECT_EQ("", access_token2);
  162. delegate.RespondWithTokenForRequest(request_id2, kTokenFromResponse2);
  163. run_loop2.Run();
  164. EXPECT_EQ("", access_token1);
  165. EXPECT_EQ(kTokenFromResponse2, access_token2);
  166. delegate.RespondWithTokenForRequest(request_id1, kTokenFromResponse1);
  167. run_loop1.Run();
  168. EXPECT_EQ(kTokenFromResponse1, access_token1);
  169. EXPECT_EQ(kTokenFromResponse2, access_token2);
  170. }
  171. TEST_F(SafeBrowsingTokenFetcherImplTest, TokenFetchTimeout) {
  172. TestAccessTokenFetchDelegate delegate;
  173. base::RunLoop run_loop;
  174. std::string access_token = "dummy";
  175. std::string kTokenFromResponse = "token";
  176. SafeBrowsingTokenFetcherImpl fetcher(base::BindRepeating(
  177. [](TestAccessTokenFetchDelegate* delegate)
  178. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  179. &delegate));
  180. fetcher.Start(base::BindOnce(&OnAccessTokenFetched, run_loop.QuitClosure(),
  181. &access_token));
  182. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  183. EXPECT_EQ("dummy", access_token);
  184. // Fast-forward to trigger the token fetch timeout.
  185. task_environment()->FastForwardBy(base::Milliseconds(
  186. safe_browsing::kTokenFetchTimeoutDelayFromMilliseconds));
  187. // Even though the delegate has not yet responded,
  188. // SafeBrowsingTokenFetcherImpl should have responded to its request with the
  189. // empty token.
  190. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  191. EXPECT_EQ("", access_token);
  192. // Check that the delegate responding at this point has no adverse effect.
  193. delegate.RespondWithTokenForRequest(delegate.get_most_recent_request_id(),
  194. kTokenFromResponse);
  195. base::RunLoop().RunUntilIdle();
  196. EXPECT_EQ("", access_token);
  197. }
  198. // Verifies that destruction of a SafeBrowsingTokenFetcherImpl instance from
  199. // within the client callback that the token was fetched doesn't cause a crash
  200. // when invoked due to the token fetch timing out.
  201. TEST_F(SafeBrowsingTokenFetcherImplTest,
  202. FetcherDestroyedFromWithinOnTokenFetchedCallbackInvokedOnTimeout) {
  203. TestAccessTokenFetchDelegate delegate;
  204. std::string access_token;
  205. bool callback_invoked = false;
  206. // Destroyed in the token fetch callback, which is invoked on timeout.
  207. auto* fetcher = new SafeBrowsingTokenFetcherImpl(base::BindRepeating(
  208. [](TestAccessTokenFetchDelegate* delegate)
  209. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  210. &delegate));
  211. fetcher->Start(base::BindOnce(
  212. [](bool* on_invoked_flag, std::string* target_token,
  213. SafeBrowsingTokenFetcherImpl* fetcher, const std::string& token) {
  214. *on_invoked_flag = true;
  215. *target_token = token;
  216. delete fetcher;
  217. },
  218. &callback_invoked, &access_token, fetcher));
  219. // Trigger a timeout of the fetch, which will invoke the client callback
  220. // passed to the fetcher.
  221. task_environment()->FastForwardBy(base::Milliseconds(
  222. safe_browsing::kTokenFetchTimeoutDelayFromMilliseconds));
  223. ASSERT_TRUE(callback_invoked);
  224. ASSERT_TRUE(access_token.empty());
  225. }
  226. TEST_F(SafeBrowsingTokenFetcherImplTest, FetcherDestroyedBeforeFetchReturns) {
  227. TestAccessTokenFetchDelegate delegate;
  228. base::RunLoop run_loop;
  229. std::string access_token = "dummy";
  230. std::string kTokenFromResponse = "token";
  231. auto fetcher =
  232. std::make_unique<SafeBrowsingTokenFetcherImpl>(base::BindRepeating(
  233. [](TestAccessTokenFetchDelegate* delegate)
  234. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  235. &delegate));
  236. fetcher->Start(base::BindOnce(&OnAccessTokenFetched, run_loop.QuitClosure(),
  237. &access_token));
  238. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  239. EXPECT_EQ("dummy", access_token);
  240. fetcher.reset();
  241. // The fetcher should have responded to the outstanding request with the empty
  242. // token on its destruction.
  243. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  244. EXPECT_EQ("", access_token);
  245. // Check that the delegate responding at this point has no adverse effect.
  246. delegate.RespondWithTokenForRequest(delegate.get_most_recent_request_id(),
  247. kTokenFromResponse);
  248. base::RunLoop().RunUntilIdle();
  249. EXPECT_EQ("", access_token);
  250. }
  251. // Tests correct operation in the case of concurrent requests to
  252. // SafeBrowsingTokenFetcherImpl made at different times, with an earlier one
  253. // timing out and a later one being fulfilled.
  254. TEST_F(SafeBrowsingTokenFetcherImplTest, ConcurrentRequestsAtDifferentTimes) {
  255. TestAccessTokenFetchDelegate delegate;
  256. base::RunLoop run_loop1;
  257. base::RunLoop run_loop2;
  258. std::string access_token1 = "dummy";
  259. std::string access_token2 = "dummy";
  260. std::string kTokenFromResponse1 = "token1";
  261. std::string kTokenFromResponse2 = "token2";
  262. int delay_before_second_request_from_ms =
  263. safe_browsing::kTokenFetchTimeoutDelayFromMilliseconds / 2;
  264. SafeBrowsingTokenFetcherImpl fetcher(base::BindRepeating(
  265. [](TestAccessTokenFetchDelegate* delegate)
  266. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  267. &delegate));
  268. fetcher.Start(base::BindOnce(&OnAccessTokenFetched, run_loop1.QuitClosure(),
  269. &access_token1));
  270. EXPECT_EQ(1, delegate.get_num_outstanding_requests());
  271. int request_id1 = delegate.get_most_recent_request_id();
  272. EXPECT_EQ("dummy", access_token1);
  273. EXPECT_EQ("dummy", access_token2);
  274. task_environment()->FastForwardBy(
  275. base::Milliseconds(delay_before_second_request_from_ms));
  276. fetcher.Start(base::BindOnce(&OnAccessTokenFetched, run_loop2.QuitClosure(),
  277. &access_token2));
  278. EXPECT_EQ(2, delegate.get_num_outstanding_requests());
  279. int request_id2 = delegate.get_most_recent_request_id();
  280. EXPECT_EQ("dummy", access_token1);
  281. EXPECT_EQ("dummy", access_token2);
  282. // Fast-forward to trigger the first request's timeout threshold, but not the
  283. // second.
  284. int time_to_trigger_first_timeout_from_ms =
  285. safe_browsing::kTokenFetchTimeoutDelayFromMilliseconds -
  286. delay_before_second_request_from_ms;
  287. task_environment()->FastForwardBy(
  288. base::Milliseconds(time_to_trigger_first_timeout_from_ms));
  289. // Verify that the first request's timeout was handled by
  290. // SafeBrowsingTokenFetcherImpl.
  291. EXPECT_EQ(2, delegate.get_num_outstanding_requests());
  292. EXPECT_EQ("", access_token1);
  293. EXPECT_EQ("dummy", access_token2);
  294. // Verify that the second request can still be fulfilled and that there is no
  295. // adverse effect from the delegate now responding to the first request.
  296. delegate.RespondWithTokenForRequest(request_id1, kTokenFromResponse1);
  297. delegate.RespondWithTokenForRequest(request_id2, kTokenFromResponse2);
  298. run_loop2.Run();
  299. EXPECT_EQ("", access_token1);
  300. EXPECT_EQ(kTokenFromResponse2, access_token2);
  301. }
  302. // Tests that the fetcher calls through to GoogleAccountAccessTokenFetchDelegate
  303. // on being notified of an invalid token.
  304. TEST_F(SafeBrowsingTokenFetcherImplTest, OnInvalidAccessToken) {
  305. TestAccessTokenFetchDelegate delegate;
  306. const std::string kInvalidToken = "dummy";
  307. SafeBrowsingTokenFetcherImpl fetcher(base::BindRepeating(
  308. [](TestAccessTokenFetchDelegate* delegate)
  309. -> GoogleAccountAccessTokenFetchDelegate* { return delegate; },
  310. &delegate));
  311. EXPECT_EQ("", delegate.get_most_recent_invalid_token());
  312. fetcher.OnInvalidAccessToken(kInvalidToken);
  313. EXPECT_EQ(kInvalidToken, delegate.get_most_recent_invalid_token());
  314. }
  315. } // namespace weblayer