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