2022-07-07 10:58:17 +08:00
|
|
|
#include "fdbrpc/FlowTransport.h"
|
|
|
|
#include "fdbrpc/TokenCache.h"
|
|
|
|
#include "fdbrpc/TokenSign.h"
|
2022-07-18 20:09:59 +08:00
|
|
|
#include "fdbrpc/TenantInfo.h"
|
2022-07-11 19:40:51 +08:00
|
|
|
#include "flow/MkCert.h"
|
2022-07-11 22:20:58 +08:00
|
|
|
#include "flow/ScopeExit.h"
|
2022-07-09 05:54:09 +08:00
|
|
|
#include "flow/UnitTest.h"
|
2022-07-07 10:58:17 +08:00
|
|
|
#include "flow/network.h"
|
|
|
|
|
2022-07-09 05:54:09 +08:00
|
|
|
#include <boost/unordered_map.hpp>
|
|
|
|
|
2022-07-11 19:40:51 +08:00
|
|
|
#include <fmt/format.h>
|
2022-07-09 05:54:09 +08:00
|
|
|
#include <list>
|
|
|
|
#include <deque>
|
|
|
|
|
2022-07-30 02:47:55 +08:00
|
|
|
#include "flow/actorcompiler.h" // has to be last include
|
|
|
|
|
2022-07-09 05:54:09 +08:00
|
|
|
template <class Key, class Value>
|
|
|
|
class LRUCache {
|
|
|
|
public:
|
|
|
|
using key_type = Key;
|
|
|
|
using list_type = std::list<key_type>;
|
|
|
|
using mapped_type = Value;
|
|
|
|
using map_type = boost::unordered_map<key_type, std::pair<mapped_type, typename list_type::iterator>>;
|
|
|
|
using size_type = unsigned;
|
|
|
|
|
|
|
|
explicit LRUCache(size_type capacity) : _capacity(capacity) { _map.reserve(capacity); }
|
|
|
|
|
|
|
|
size_type size() const { return _map.size(); }
|
|
|
|
size_type capacity() const { return _capacity; }
|
|
|
|
bool empty() const { return _map.empty(); }
|
|
|
|
|
|
|
|
Optional<mapped_type*> get(key_type const& key) {
|
|
|
|
auto i = _map.find(key);
|
|
|
|
if (i == _map.end()) {
|
|
|
|
return Optional<mapped_type*>();
|
|
|
|
}
|
|
|
|
auto j = i->second.second;
|
|
|
|
if (j != _list.begin()) {
|
|
|
|
_list.erase(j);
|
|
|
|
_list.push_front(i->first);
|
|
|
|
i->second.second = _list.begin();
|
|
|
|
}
|
|
|
|
return &i->second.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class K, class V>
|
|
|
|
mapped_type* insert(K&& key, V&& value) {
|
|
|
|
auto iter = _map.find(key);
|
|
|
|
if (iter != _map.end()) {
|
|
|
|
return &iter->second.first;
|
|
|
|
}
|
|
|
|
if (size() == capacity()) {
|
|
|
|
auto i = --_list.end();
|
|
|
|
_map.erase(*i);
|
|
|
|
_list.erase(i);
|
|
|
|
}
|
2022-07-11 21:27:00 +08:00
|
|
|
_list.push_front(std::forward<K>(key));
|
2022-07-09 05:54:09 +08:00
|
|
|
std::tie(iter, std::ignore) =
|
2022-07-11 21:27:00 +08:00
|
|
|
_map.insert(std::make_pair(*_list.begin(), std::make_pair(std::forward<V>(value), _list.begin())));
|
2022-07-09 05:54:09 +08:00
|
|
|
return &iter->second.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const size_type _capacity;
|
|
|
|
map_type _map;
|
|
|
|
list_type _list;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_CASE("/fdbrpc/authz/LRUCache") {
|
2022-07-11 21:27:00 +08:00
|
|
|
auto& rng = *deterministicRandom();
|
2022-07-09 05:54:09 +08:00
|
|
|
{
|
|
|
|
// test very small LRU cache
|
2022-07-11 21:27:00 +08:00
|
|
|
LRUCache<int, StringRef> cache(rng.randomInt(2, 10));
|
2022-07-09 05:54:09 +08:00
|
|
|
for (int i = 0; i < 200; ++i) {
|
|
|
|
cache.insert(i, "val"_sr);
|
2022-07-11 21:27:00 +08:00
|
|
|
if (i >= cache.capacity()) {
|
|
|
|
for (auto j = 0; j <= i - cache.capacity(); j++)
|
|
|
|
ASSERT(!cache.get(j).present());
|
|
|
|
// ordering is important so as not to disrupt the LRU order
|
|
|
|
for (auto j = i - cache.capacity() + 1; j <= i; j++)
|
|
|
|
ASSERT(cache.get(j).present());
|
2022-07-09 05:54:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Test larger cache
|
|
|
|
LRUCache<int, StringRef> cache(1000);
|
2022-07-11 21:27:00 +08:00
|
|
|
for (auto i = 0; i < 1000; ++i) {
|
|
|
|
cache.insert(i, "value"_sr);
|
2022-07-09 05:54:09 +08:00
|
|
|
}
|
2022-07-11 21:27:00 +08:00
|
|
|
cache.insert(1000, "value"_sr); // should evict 0
|
|
|
|
ASSERT(!cache.get(0).present());
|
2022-07-09 05:54:09 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// memory test -- this is what the boost implementation didn't do correctly
|
|
|
|
LRUCache<StringRef, Standalone<StringRef>> cache(10);
|
|
|
|
std::deque<std::string> cachedStrings;
|
|
|
|
std::deque<std::string> evictedStrings;
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
2022-07-11 21:27:00 +08:00
|
|
|
auto str = rng.randomAlphaNumeric(rng.randomInt(100, 1024));
|
2022-07-09 05:54:09 +08:00
|
|
|
Standalone<StringRef> sref(str);
|
|
|
|
cache.insert(sref, sref);
|
|
|
|
cachedStrings.push_back(str);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
|
|
Standalone<StringRef> existingStr(cachedStrings.back());
|
|
|
|
auto cachedStr = cache.get(existingStr);
|
|
|
|
ASSERT(cachedStr.present());
|
|
|
|
ASSERT(*cachedStr.get() == existingStr);
|
|
|
|
if (!evictedStrings.empty()) {
|
2022-07-11 21:27:00 +08:00
|
|
|
Standalone<StringRef> nonexisting(evictedStrings.at(rng.randomInt(0, evictedStrings.size())));
|
2022-07-09 05:54:09 +08:00
|
|
|
ASSERT(!cache.get(nonexisting).present());
|
|
|
|
}
|
2022-07-11 21:27:00 +08:00
|
|
|
auto str = rng.randomAlphaNumeric(rng.randomInt(100, 1024));
|
2022-07-09 05:54:09 +08:00
|
|
|
Standalone<StringRef> sref(str);
|
|
|
|
evictedStrings.push_back(cachedStrings.front());
|
|
|
|
cachedStrings.pop_front();
|
|
|
|
cachedStrings.push_back(str);
|
|
|
|
cache.insert(sref, sref);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Void();
|
|
|
|
}
|
2022-07-07 10:58:17 +08:00
|
|
|
|
|
|
|
struct TokenCacheImpl {
|
|
|
|
struct CacheEntry {
|
|
|
|
Arena arena;
|
2022-07-09 04:38:43 +08:00
|
|
|
VectorRef<TenantNameRef> tenants;
|
2022-07-07 10:58:17 +08:00
|
|
|
double expirationTime = 0.0;
|
|
|
|
};
|
|
|
|
|
2022-07-09 05:54:09 +08:00
|
|
|
LRUCache<StringRef, CacheEntry> cache;
|
2022-07-07 10:58:17 +08:00
|
|
|
TokenCacheImpl() : cache(FLOW_KNOBS->TOKEN_CACHE_SIZE) {}
|
|
|
|
|
|
|
|
bool validate(TenantNameRef tenant, StringRef token);
|
2022-07-21 17:39:36 +08:00
|
|
|
bool validateAndAdd(double currentTime, StringRef token, NetworkAddress const& peer);
|
2022-07-07 10:58:17 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
TokenCache::TokenCache() : impl(new TokenCacheImpl()) {}
|
|
|
|
TokenCache::~TokenCache() {
|
|
|
|
delete impl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TokenCache::createInstance() {
|
|
|
|
g_network->setGlobal(INetwork::enTokenCache, new TokenCache());
|
|
|
|
}
|
|
|
|
|
|
|
|
TokenCache& TokenCache::instance() {
|
|
|
|
return *reinterpret_cast<TokenCache*>(g_network->global(INetwork::enTokenCache));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TokenCache::validate(TenantNameRef name, StringRef token) {
|
|
|
|
return impl->validate(name, token);
|
|
|
|
}
|
|
|
|
|
2022-07-18 19:07:00 +08:00
|
|
|
#define TRACE_INVALID_PARSED_TOKEN(reason, token) \
|
|
|
|
TraceEvent(SevWarn, "InvalidToken") \
|
|
|
|
.detail("From", peer) \
|
|
|
|
.detail("Reason", reason) \
|
2022-07-30 02:47:55 +08:00
|
|
|
.detail("CurrentTime", currentTime) \
|
2022-07-18 19:07:00 +08:00
|
|
|
.detail("Token", token.toStringRef(arena).toStringView())
|
|
|
|
|
2022-07-21 18:12:48 +08:00
|
|
|
bool TokenCacheImpl::validateAndAdd(double currentTime, StringRef token, NetworkAddress const& peer) {
|
2022-07-09 01:19:14 +08:00
|
|
|
Arena arena;
|
|
|
|
authz::jwt::TokenRef t;
|
|
|
|
if (!authz::jwt::parseToken(arena, t, token)) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Token can't be parsed");
|
2022-07-18 19:07:00 +08:00
|
|
|
TraceEvent(SevWarn, "InvalidToken")
|
|
|
|
.detail("From", peer)
|
|
|
|
.detail("Reason", "ParseError")
|
|
|
|
.detail("Token", token.toString());
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-14 02:21:27 +08:00
|
|
|
auto key = FlowTransport::transport().getPublicKeyByName(t.keyId);
|
2022-07-09 01:19:14 +08:00
|
|
|
if (!key.present()) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Token referencing non-existing key");
|
2022-07-18 19:07:00 +08:00
|
|
|
TRACE_INVALID_PARSED_TOKEN("UnknownKey", t);
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
2022-08-17 23:32:05 +08:00
|
|
|
} else if (!t.issuedAtUnixTime.present()) {
|
|
|
|
CODE_PROBE(true, "Token has no issued-at field");
|
|
|
|
TRACE_INVALID_PARSED_TOKEN("NoIssuedAt", t);
|
|
|
|
return false;
|
2022-07-09 01:19:14 +08:00
|
|
|
} else if (!t.expiresAtUnixTime.present()) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Token has no expiration time");
|
2022-07-18 19:07:00 +08:00
|
|
|
TRACE_INVALID_PARSED_TOKEN("NoExpirationTime", t);
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
2022-08-18 02:23:51 +08:00
|
|
|
} else if (t.expiresAtUnixTime.get() <= currentTime) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Expired token");
|
2022-07-18 19:07:00 +08:00
|
|
|
TRACE_INVALID_PARSED_TOKEN("Expired", t);
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
|
|
|
} else if (!t.notBeforeUnixTime.present()) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Token has no not-before field");
|
2022-07-18 19:07:00 +08:00
|
|
|
TRACE_INVALID_PARSED_TOKEN("NoNotBefore", t);
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
2022-08-18 02:23:51 +08:00
|
|
|
} else if (t.notBeforeUnixTime.get() > currentTime) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Tokens not-before is in the future");
|
2022-07-18 19:07:00 +08:00
|
|
|
TRACE_INVALID_PARSED_TOKEN("TokenNotYetValid", t);
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
|
|
|
} else if (!t.tenants.present()) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Token with no tenants");
|
2022-07-18 19:07:00 +08:00
|
|
|
TRACE_INVALID_PARSED_TOKEN("NoTenants", t);
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
|
|
|
} else if (!authz::jwt::verifyToken(token, key.get())) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Token with invalid signature");
|
2022-07-18 19:07:00 +08:00
|
|
|
TRACE_INVALID_PARSED_TOKEN("InvalidSignature", t);
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
CacheEntry c;
|
2022-08-18 02:23:51 +08:00
|
|
|
c.expirationTime = t.expiresAtUnixTime.get();
|
2022-07-18 19:07:00 +08:00
|
|
|
c.tenants.reserve(c.arena, t.tenants.get().size());
|
2022-07-09 01:19:14 +08:00
|
|
|
for (auto tenant : t.tenants.get()) {
|
2022-07-09 04:38:43 +08:00
|
|
|
c.tenants.push_back_deep(c.arena, tenant);
|
2022-07-09 01:19:14 +08:00
|
|
|
}
|
2022-07-21 17:39:36 +08:00
|
|
|
cache.insert(StringRef(c.arena, token), c);
|
2022-07-09 01:19:14 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-07 10:58:17 +08:00
|
|
|
bool TokenCacheImpl::validate(TenantNameRef name, StringRef token) {
|
2022-07-18 19:07:00 +08:00
|
|
|
NetworkAddress peer = FlowTransport::transport().currentDeliveryPeerAddress();
|
2022-07-21 17:39:36 +08:00
|
|
|
auto cachedEntry = cache.get(token);
|
2022-07-07 10:58:17 +08:00
|
|
|
double currentTime = g_network->timer();
|
|
|
|
|
2022-07-09 05:54:09 +08:00
|
|
|
if (!cachedEntry.present()) {
|
2022-07-21 17:39:36 +08:00
|
|
|
if (validateAndAdd(currentTime, token, peer)) {
|
2022-07-21 18:11:33 +08:00
|
|
|
cachedEntry = cache.get(token);
|
2022-07-07 10:58:17 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2022-07-09 01:19:14 +08:00
|
|
|
|
2022-07-09 05:54:09 +08:00
|
|
|
ASSERT(cachedEntry.present());
|
2022-07-09 01:19:14 +08:00
|
|
|
|
|
|
|
auto& entry = cachedEntry.get();
|
2022-07-09 05:54:09 +08:00
|
|
|
if (entry->expirationTime < currentTime) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Found expired token in cache");
|
2022-07-11 22:20:58 +08:00
|
|
|
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "ExpiredInCache");
|
2022-07-09 01:19:14 +08:00
|
|
|
return false;
|
|
|
|
}
|
2022-07-09 04:38:43 +08:00
|
|
|
bool tenantFound = false;
|
2022-07-09 05:54:09 +08:00
|
|
|
for (auto const& t : entry->tenants) {
|
2022-07-09 04:38:43 +08:00
|
|
|
if (t == name) {
|
|
|
|
tenantFound = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-07-11 22:20:58 +08:00
|
|
|
if (!tenantFound) {
|
2022-07-27 06:22:50 +08:00
|
|
|
CODE_PROBE(true, "Valid token doesn't reference tenant");
|
2022-07-09 01:19:14 +08:00
|
|
|
TraceEvent(SevWarn, "TenantTokenMismatch").detail("From", peer).detail("Tenant", name.toString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2022-07-07 10:58:17 +08:00
|
|
|
}
|
2022-07-11 19:40:51 +08:00
|
|
|
|
|
|
|
namespace authz::jwt {
|
|
|
|
extern TokenRef makeRandomTokenSpec(Arena&, IRandom&, authz::Algorithm);
|
|
|
|
}
|
|
|
|
|
2022-07-11 22:20:58 +08:00
|
|
|
TEST_CASE("/fdbrpc/authz/TokenCache/BadTokens") {
|
2022-07-11 19:40:51 +08:00
|
|
|
std::pair<void (*)(Arena&, IRandom&, authz::jwt::TokenRef&), char const*> badMutations[]{
|
2022-07-11 22:20:58 +08:00
|
|
|
{
|
|
|
|
[](Arena&, IRandom&, authz::jwt::TokenRef&) { FlowTransport::transport().removeAllPublicKeys(); },
|
|
|
|
"NoKeyWithSuchName",
|
|
|
|
},
|
2022-07-11 19:40:51 +08:00
|
|
|
{
|
|
|
|
[](Arena&, IRandom&, authz::jwt::TokenRef& token) { token.expiresAtUnixTime.reset(); },
|
|
|
|
"NoExpirationTime",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[](Arena&, IRandom& rng, authz::jwt::TokenRef& token) {
|
2022-08-18 02:23:51 +08:00
|
|
|
token.expiresAtUnixTime = std::max<double>(g_network->timer() - 10 - rng.random01() * 50, 0);
|
2022-07-11 19:40:51 +08:00
|
|
|
},
|
|
|
|
"ExpiredToken",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[](Arena&, IRandom&, authz::jwt::TokenRef& token) { token.notBeforeUnixTime.reset(); },
|
|
|
|
"NoNotBefore",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[](Arena&, IRandom& rng, authz::jwt::TokenRef& token) {
|
2022-08-18 02:23:51 +08:00
|
|
|
token.notBeforeUnixTime = g_network->timer() + 10 + rng.random01() * 50;
|
2022-07-11 19:40:51 +08:00
|
|
|
},
|
|
|
|
"TokenNotYetValid",
|
|
|
|
},
|
2022-08-17 23:32:05 +08:00
|
|
|
{
|
|
|
|
[](Arena&, IRandom&, authz::jwt::TokenRef& token) { token.issuedAtUnixTime.reset(); },
|
|
|
|
"NoIssuedAt",
|
|
|
|
},
|
|
|
|
|
2022-07-11 19:40:51 +08:00
|
|
|
{
|
|
|
|
[](Arena& arena, IRandom&, authz::jwt::TokenRef& token) { token.tenants.reset(); },
|
|
|
|
"NoTenants",
|
|
|
|
},
|
|
|
|
};
|
2022-07-11 22:20:58 +08:00
|
|
|
auto const pubKeyName = "somePublicKey"_sr;
|
|
|
|
auto privateKey = mkcert::makeEcP256();
|
2022-07-11 19:40:51 +08:00
|
|
|
auto const numBadMutations = sizeof(badMutations) / sizeof(badMutations[0]);
|
|
|
|
for (auto repeat = 0; repeat < 50; repeat++) {
|
|
|
|
auto arena = Arena();
|
|
|
|
auto& rng = *deterministicRandom();
|
|
|
|
auto validTokenSpec = authz::jwt::makeRandomTokenSpec(arena, rng, authz::Algorithm::ES256);
|
2022-07-11 22:20:58 +08:00
|
|
|
validTokenSpec.keyId = pubKeyName;
|
2022-07-11 19:40:51 +08:00
|
|
|
for (auto i = 0; i < numBadMutations; i++) {
|
2022-07-12 22:48:09 +08:00
|
|
|
FlowTransport::transport().addPublicKey(pubKeyName, privateKey.toPublic());
|
2022-07-11 22:20:58 +08:00
|
|
|
auto publicKeyClearGuard =
|
|
|
|
ScopeExit([pubKeyName]() { FlowTransport::transport().removePublicKey(pubKeyName); });
|
2022-07-11 19:40:51 +08:00
|
|
|
auto [mutationFn, mutationDesc] = badMutations[i];
|
|
|
|
auto tmpArena = Arena();
|
|
|
|
auto mutatedTokenSpec = validTokenSpec;
|
|
|
|
mutationFn(tmpArena, rng, mutatedTokenSpec);
|
|
|
|
auto signedToken = authz::jwt::signToken(tmpArena, mutatedTokenSpec, privateKey);
|
|
|
|
if (TokenCache::instance().validate(validTokenSpec.tenants.get()[0], signedToken)) {
|
|
|
|
fmt::print("Unexpected successful validation at mutation {}, token spec: {}\n",
|
|
|
|
mutationDesc,
|
|
|
|
mutatedTokenSpec.toStringRef(tmpArena).toStringView());
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-11 22:20:58 +08:00
|
|
|
if (TokenCache::instance().validate("TenantNameDontMatterHere"_sr, StringRef())) {
|
|
|
|
fmt::print("Unexpected successful validation of ill-formed token (no signature part)\n");
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
if (TokenCache::instance().validate("TenantNameDontMatterHere"_sr, "1111.22"_sr)) {
|
|
|
|
fmt::print("Unexpected successful validation of ill-formed token (no signature part)\n");
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
if (TokenCache::instance().validate("TenantNameDontMatterHere2"_sr, "////.////.////"_sr)) {
|
|
|
|
fmt::print("Unexpected successful validation of unparseable token\n");
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
fmt::print("TEST OK\n");
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE("/fdbrpc/authz/TokenCache/GoodTokens") {
|
|
|
|
// Don't repeat because token expiry is at seconds granularity and sleeps are costly in unit tests
|
2022-07-30 02:47:55 +08:00
|
|
|
state Arena arena;
|
|
|
|
state PrivateKey privateKey = mkcert::makeEcP256();
|
|
|
|
state StringRef pubKeyName = "somePublicKey"_sr;
|
|
|
|
state ScopeExit<std::function<void()>> publicKeyClearGuard(
|
|
|
|
[pubKeyName = pubKeyName]() { FlowTransport::transport().removePublicKey(pubKeyName); });
|
|
|
|
state authz::jwt::TokenRef tokenSpec =
|
|
|
|
authz::jwt::makeRandomTokenSpec(arena, *deterministicRandom(), authz::Algorithm::ES256);
|
|
|
|
state StringRef signedToken;
|
2022-07-12 22:48:09 +08:00
|
|
|
FlowTransport::transport().addPublicKey(pubKeyName, privateKey.toPublic());
|
2022-08-18 02:23:51 +08:00
|
|
|
tokenSpec.expiresAtUnixTime = g_network->timer() + 2.0;
|
2022-07-11 22:20:58 +08:00
|
|
|
tokenSpec.keyId = pubKeyName;
|
2022-07-30 02:47:55 +08:00
|
|
|
signedToken = authz::jwt::signToken(arena, tokenSpec, privateKey);
|
2022-07-11 22:20:58 +08:00
|
|
|
if (!TokenCache::instance().validate(tokenSpec.tenants.get()[0], signedToken)) {
|
|
|
|
fmt::print("Unexpected failed token validation, token spec: {}, now: {}\n",
|
|
|
|
tokenSpec.toStringRef(arena).toStringView(),
|
|
|
|
g_network->timer());
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
2022-07-30 02:47:55 +08:00
|
|
|
wait(delay(3.5));
|
2022-07-11 22:20:58 +08:00
|
|
|
if (TokenCache::instance().validate(tokenSpec.tenants.get()[0], signedToken)) {
|
|
|
|
fmt::print(
|
|
|
|
"Unexpected successful token validation after supposedly expiring in cache, token spec: {}, now: {}\n",
|
|
|
|
tokenSpec.toStringRef(arena).toStringView(),
|
|
|
|
g_network->timer());
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
2022-07-11 19:40:51 +08:00
|
|
|
fmt::print("TEST OK\n");
|
|
|
|
return Void();
|
|
|
|
}
|