• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2023 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 "quiche/blind_sign_auth/cached_blind_sign_auth.h"
6 
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11 
12 #include "absl/status/status.h"
13 #include "absl/status/statusor.h"
14 #include "absl/strings/str_cat.h"
15 #include "absl/strings/str_format.h"
16 #include "absl/time/clock.h"
17 #include "absl/time/time.h"
18 #include "absl/types/span.h"
19 #include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
20 #include "quiche/blind_sign_auth/test_tools/mock_blind_sign_auth_interface.h"
21 #include "quiche/common/platform/api/quiche_mutex.h"
22 #include "quiche/common/platform/api/quiche_test.h"
23 #include "quiche/common/test_tools/quiche_test_utils.h"
24 
25 namespace quiche {
26 namespace test {
27 namespace {
28 
29 using ::testing::_;
30 using ::testing::InvokeArgument;
31 using ::testing::Unused;
32 
33 class CachedBlindSignAuthTest : public QuicheTest {
34  protected:
SetUp()35   void SetUp() override {
36     cached_blind_sign_auth_ =
37         std::make_unique<CachedBlindSignAuth>(&mock_blind_sign_auth_interface_);
38   }
39 
TearDown()40   void TearDown() override {
41     fake_tokens_.clear();
42     cached_blind_sign_auth_.reset();
43   }
44 
45  public:
MakeFakeTokens(int num_tokens)46   std::vector<BlindSignToken> MakeFakeTokens(int num_tokens) {
47     std::vector<BlindSignToken> fake_tokens;
48     for (int i = 0; i < kBlindSignAuthRequestMaxTokens; i++) {
49       fake_tokens.push_back(BlindSignToken{absl::StrCat("token:", i),
50                                            absl::Now() + absl::Hours(1)});
51     }
52     return fake_tokens;
53   }
54 
MakeExpiredTokens(int num_tokens)55   std::vector<BlindSignToken> MakeExpiredTokens(int num_tokens) {
56     std::vector<BlindSignToken> fake_tokens;
57     for (int i = 0; i < kBlindSignAuthRequestMaxTokens; i++) {
58       fake_tokens.push_back(BlindSignToken{absl::StrCat("token:", i),
59                                            absl::Now() - absl::Hours(1)});
60     }
61     return fake_tokens;
62   }
63 
64   MockBlindSignAuthInterface mock_blind_sign_auth_interface_;
65   std::unique_ptr<CachedBlindSignAuth> cached_blind_sign_auth_;
66   std::string oauth_token_ = "oauth_token";
67   std::vector<BlindSignToken> fake_tokens_;
68 };
69 
TEST_F(CachedBlindSignAuthTest,TestGetTokensOneCallSuccessful)70 TEST_F(CachedBlindSignAuthTest, TestGetTokensOneCallSuccessful) {
71   EXPECT_CALL(mock_blind_sign_auth_interface_,
72               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
73       .Times(1)
74       .WillOnce(
75           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
76             fake_tokens_ = MakeFakeTokens(num_tokens);
77             std::move(callback)(absl::MakeSpan(fake_tokens_));
78           });
79 
80   int num_tokens = 5;
81   QuicheNotification done;
82   SignedTokenCallback callback =
83       [num_tokens, &done](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
84         QUICHE_EXPECT_OK(tokens);
85         EXPECT_EQ(num_tokens, tokens->size());
86         for (int i = 0; i < num_tokens; i++) {
87           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
88         }
89         done.Notify();
90       };
91 
92   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
93                                      ProxyLayer::kProxyA, std::move(callback));
94   done.WaitForNotification();
95 }
96 
TEST_F(CachedBlindSignAuthTest,TestGetTokensMultipleRemoteCallsSuccessful)97 TEST_F(CachedBlindSignAuthTest, TestGetTokensMultipleRemoteCallsSuccessful) {
98   EXPECT_CALL(mock_blind_sign_auth_interface_,
99               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
100       .Times(2)
101       .WillRepeatedly(
102           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
103             fake_tokens_ = MakeFakeTokens(num_tokens);
104             std::move(callback)(absl::MakeSpan(fake_tokens_));
105           });
106 
107   int num_tokens = kBlindSignAuthRequestMaxTokens - 1;
108   QuicheNotification first;
109   SignedTokenCallback first_callback =
110       [num_tokens, &first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
111         QUICHE_EXPECT_OK(tokens);
112         EXPECT_EQ(num_tokens, tokens->size());
113         for (int i = 0; i < num_tokens; i++) {
114           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
115         }
116         first.Notify();
117       };
118 
119   cached_blind_sign_auth_->GetTokens(
120       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
121   first.WaitForNotification();
122 
123   QuicheNotification second;
124   SignedTokenCallback second_callback =
125       [num_tokens, &second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
126         QUICHE_EXPECT_OK(tokens);
127         EXPECT_EQ(num_tokens, tokens->size());
128         EXPECT_EQ(tokens->at(0).token,
129                   absl::StrCat("token:", kBlindSignAuthRequestMaxTokens - 1));
130         for (int i = 1; i < num_tokens; i++) {
131           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i - 1));
132         }
133         second.Notify();
134       };
135 
136   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
137                                      ProxyLayer::kProxyA,
138                                      std::move(second_callback));
139   second.WaitForNotification();
140 }
141 
TEST_F(CachedBlindSignAuthTest,TestGetTokensSecondRequestFilledFromCache)142 TEST_F(CachedBlindSignAuthTest, TestGetTokensSecondRequestFilledFromCache) {
143   EXPECT_CALL(mock_blind_sign_auth_interface_,
144               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
145       .Times(1)
146       .WillOnce(
147           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
148             fake_tokens_ = MakeFakeTokens(num_tokens);
149             std::move(callback)(absl::MakeSpan(fake_tokens_));
150           });
151 
152   int num_tokens = kBlindSignAuthRequestMaxTokens / 2;
153   QuicheNotification first;
154   SignedTokenCallback first_callback =
155       [num_tokens, &first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
156         QUICHE_EXPECT_OK(tokens);
157         EXPECT_EQ(num_tokens, tokens->size());
158         for (int i = 0; i < num_tokens; i++) {
159           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
160         }
161         first.Notify();
162       };
163 
164   cached_blind_sign_auth_->GetTokens(
165       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
166   first.WaitForNotification();
167 
168   QuicheNotification second;
169   SignedTokenCallback second_callback =
170       [num_tokens, &second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
171         QUICHE_EXPECT_OK(tokens);
172         EXPECT_EQ(num_tokens, tokens->size());
173         for (int i = 0; i < num_tokens; i++) {
174           EXPECT_EQ(tokens->at(i).token,
175                     absl::StrCat("token:", i + num_tokens));
176         }
177         second.Notify();
178       };
179 
180   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
181                                      ProxyLayer::kProxyA,
182                                      std::move(second_callback));
183   second.WaitForNotification();
184 }
185 
TEST_F(CachedBlindSignAuthTest,TestGetTokensThirdRequestRefillsCache)186 TEST_F(CachedBlindSignAuthTest, TestGetTokensThirdRequestRefillsCache) {
187   EXPECT_CALL(mock_blind_sign_auth_interface_,
188               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
189       .Times(2)
190       .WillRepeatedly(
191           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
192             fake_tokens_ = MakeFakeTokens(num_tokens);
193             std::move(callback)(absl::MakeSpan(fake_tokens_));
194           });
195 
196   int num_tokens = kBlindSignAuthRequestMaxTokens / 2;
197   QuicheNotification first;
198   SignedTokenCallback first_callback =
199       [num_tokens, &first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
200         QUICHE_EXPECT_OK(tokens);
201         EXPECT_EQ(num_tokens, tokens->size());
202         for (int i = 0; i < num_tokens; i++) {
203           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
204         }
205         first.Notify();
206       };
207 
208   cached_blind_sign_auth_->GetTokens(
209       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
210   first.WaitForNotification();
211 
212   QuicheNotification second;
213   SignedTokenCallback second_callback =
214       [num_tokens, &second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
215         QUICHE_EXPECT_OK(tokens);
216         EXPECT_EQ(num_tokens, tokens->size());
217         for (int i = 0; i < num_tokens; i++) {
218           EXPECT_EQ(tokens->at(i).token,
219                     absl::StrCat("token:", i + num_tokens));
220         }
221         second.Notify();
222       };
223 
224   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
225                                      ProxyLayer::kProxyA,
226                                      std::move(second_callback));
227   second.WaitForNotification();
228 
229   QuicheNotification third;
230   int third_request_tokens = 10;
231   SignedTokenCallback third_callback =
232       [third_request_tokens,
233        &third](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
234         QUICHE_EXPECT_OK(tokens);
235         EXPECT_EQ(third_request_tokens, tokens->size());
236         for (int i = 0; i < third_request_tokens; i++) {
237           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
238         }
239         third.Notify();
240       };
241 
242   cached_blind_sign_auth_->GetTokens(oauth_token_, third_request_tokens,
243                                      ProxyLayer::kProxyA,
244                                      std::move(third_callback));
245   third.WaitForNotification();
246 }
247 
TEST_F(CachedBlindSignAuthTest,TestGetTokensRequestTooLarge)248 TEST_F(CachedBlindSignAuthTest, TestGetTokensRequestTooLarge) {
249   EXPECT_CALL(mock_blind_sign_auth_interface_,
250               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
251       .Times(0);
252 
253   int num_tokens = kBlindSignAuthRequestMaxTokens + 1;
254   SignedTokenCallback callback =
255       [](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
256         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInvalidArgument);
257         EXPECT_THAT(
258             tokens.status().message(),
259             absl::StrFormat("Number of tokens requested exceeds maximum: %d",
260                             kBlindSignAuthRequestMaxTokens));
261       };
262 
263   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
264                                      ProxyLayer::kProxyA, std::move(callback));
265 }
266 
TEST_F(CachedBlindSignAuthTest,TestGetTokensRequestNegative)267 TEST_F(CachedBlindSignAuthTest, TestGetTokensRequestNegative) {
268   EXPECT_CALL(mock_blind_sign_auth_interface_,
269               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
270       .Times(0);
271 
272   int num_tokens = -1;
273   SignedTokenCallback callback =
274       [num_tokens](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
275         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInvalidArgument);
276         EXPECT_THAT(tokens.status().message(),
277                     absl::StrFormat("Negative number of tokens requested: %d",
278                                     num_tokens));
279       };
280 
281   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
282                                      ProxyLayer::kProxyA, std::move(callback));
283 }
284 
TEST_F(CachedBlindSignAuthTest,TestHandleGetTokensResponseErrorHandling)285 TEST_F(CachedBlindSignAuthTest, TestHandleGetTokensResponseErrorHandling) {
286   EXPECT_CALL(mock_blind_sign_auth_interface_,
287               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
288       .Times(2)
289       .WillOnce(
290           [](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
291             std::move(callback)(absl::InternalError("AuthAndSign failed"));
292           })
293       .WillOnce(
294           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
295             fake_tokens_ = MakeFakeTokens(num_tokens);
296             fake_tokens_.pop_back();
297             std::move(callback)(absl::MakeSpan(fake_tokens_));
298           });
299 
300   int num_tokens = kBlindSignAuthRequestMaxTokens;
301   QuicheNotification first;
302   SignedTokenCallback first_callback =
303       [&first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
304         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInternal);
305         EXPECT_THAT(tokens.status().message(), "AuthAndSign failed");
306         first.Notify();
307       };
308 
309   cached_blind_sign_auth_->GetTokens(
310       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
311   first.WaitForNotification();
312 
313   QuicheNotification second;
314   SignedTokenCallback second_callback =
315       [&second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
316         EXPECT_THAT(tokens.status().code(),
317                     absl::StatusCode::kResourceExhausted);
318         second.Notify();
319       };
320 
321   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
322                                      ProxyLayer::kProxyA,
323                                      std::move(second_callback));
324   second.WaitForNotification();
325 }
326 
TEST_F(CachedBlindSignAuthTest,TestGetTokensZeroTokensRequested)327 TEST_F(CachedBlindSignAuthTest, TestGetTokensZeroTokensRequested) {
328   EXPECT_CALL(mock_blind_sign_auth_interface_,
329               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
330       .Times(0);
331 
332   int num_tokens = 0;
333   SignedTokenCallback callback =
334       [](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
335         QUICHE_EXPECT_OK(tokens);
336         EXPECT_EQ(tokens->size(), 0);
337       };
338 
339   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
340                                      ProxyLayer::kProxyA, std::move(callback));
341 }
342 
TEST_F(CachedBlindSignAuthTest,TestExpiredTokensArePruned)343 TEST_F(CachedBlindSignAuthTest, TestExpiredTokensArePruned) {
344   EXPECT_CALL(mock_blind_sign_auth_interface_,
345               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
346       .Times(1)
347       .WillOnce(
348           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
349             fake_tokens_ = MakeExpiredTokens(num_tokens);
350             std::move(callback)(absl::MakeSpan(fake_tokens_));
351           });
352 
353   int num_tokens = kBlindSignAuthRequestMaxTokens;
354   QuicheNotification first;
355   SignedTokenCallback first_callback =
356       [&first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
357         EXPECT_THAT(tokens.status().code(),
358                     absl::StatusCode::kResourceExhausted);
359         first.Notify();
360       };
361 
362   cached_blind_sign_auth_->GetTokens(
363       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
364   first.WaitForNotification();
365 }
366 
TEST_F(CachedBlindSignAuthTest,TestClearCacheRemovesTokens)367 TEST_F(CachedBlindSignAuthTest, TestClearCacheRemovesTokens) {
368   EXPECT_CALL(mock_blind_sign_auth_interface_,
369               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
370       .Times(2)
371       .WillRepeatedly(
372           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
373             fake_tokens_ = MakeExpiredTokens(num_tokens);
374             std::move(callback)(absl::MakeSpan(fake_tokens_));
375           });
376 
377   int num_tokens = kBlindSignAuthRequestMaxTokens / 2;
378   QuicheNotification first;
379   SignedTokenCallback first_callback =
380       [&first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
381         EXPECT_THAT(tokens.status().code(),
382                     absl::StatusCode::kResourceExhausted);
383         first.Notify();
384       };
385 
386   cached_blind_sign_auth_->GetTokens(
387       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
388   first.WaitForNotification();
389 
390   cached_blind_sign_auth_->ClearCache();
391 
392   QuicheNotification second;
393   SignedTokenCallback second_callback =
394       [&second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
395         EXPECT_THAT(tokens.status().code(),
396                     absl::StatusCode::kResourceExhausted);
397         second.Notify();
398       };
399 
400   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
401                                      ProxyLayer::kProxyA,
402                                      std::move(second_callback));
403   second.WaitForNotification();
404 }
405 
406 }  // namespace
407 }  // namespace test
408 }  // namespace quiche
409