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