addressed review comments

This commit is contained in:
Markus Pilman 2022-07-08 11:19:14 -06:00
parent f268265d96
commit 2ae17233d8
3 changed files with 81 additions and 53 deletions

View File

@ -3146,6 +3146,8 @@ SpanContext generateSpanID(bool transactionTracingSample, SpanContext parentCont
deterministicRandom()->randomUniqueID(), deterministicRandom()->randomUInt64(), TraceFlags::unsampled);
}
FDB_DEFINE_BOOLEAN_PARAM(AllowInvalidTenantID);
TransactionState::TransactionState(Database cx,
Optional<TenantName> tenant,
TaskPriority taskID,
@ -3174,7 +3176,7 @@ Reference<TransactionState> TransactionState::cloneAndReset(Reference<Transactio
return newState;
}
TenantInfo TransactionState::getTenantInfo(bool allowInvalidId /* = false */) {
TenantInfo TransactionState::getTenantInfo(AllowInvalidTenantID allowInvalidId /* = false */) {
Optional<TenantName> const& t = tenant();
if (options.rawAccess) {

View File

@ -235,6 +235,8 @@ struct Watch : public ReferenceCounted<Watch>, NonCopyable {
void setWatch(Future<Void> watchFuture);
};
FDB_DECLARE_BOOLEAN_PARAM(AllowInvalidTenantID);
struct TransactionState : ReferenceCounted<TransactionState> {
Database cx;
int64_t tenantId = TenantInfo::INVALID_TENANT;
@ -270,7 +272,7 @@ struct TransactionState : ReferenceCounted<TransactionState> {
Reference<TransactionLogInfo> trLogInfo);
Reference<TransactionState> cloneAndReset(Reference<TransactionLogInfo> newTrLogInfo, bool generateNewSpan) const;
TenantInfo getTenantInfo(bool allowInvalidId = false);
TenantInfo getTenantInfo(AllowInvalidTenantID allowInvalidId = AllowInvalidTenantID::False);
Optional<TenantName> const& tenant();
bool hasTenant() const;

View File

@ -17,6 +17,7 @@ struct TokenCacheImpl {
TokenCacheImpl() : cache(FLOW_KNOBS->TOKEN_CACHE_SIZE) {}
bool validate(TenantNameRef tenant, StringRef token);
bool validateAndAdd(double currentTime, StringRef signature, StringRef token, NetworkAddress const& peer);
};
TokenCache::TokenCache() : impl(new TokenCacheImpl()) {}
@ -36,65 +37,88 @@ bool TokenCache::validate(TenantNameRef name, StringRef token) {
return impl->validate(name, token);
}
bool TokenCacheImpl::validateAndAdd(double currentTime,
StringRef signature,
StringRef token,
NetworkAddress const& peer) {
Arena arena;
authz::jwt::TokenRef t;
if (!authz::jwt::parseToken(arena, t, token)) {
TEST(true); // Token can't be parsed
return false;
}
if (!t.keyId.present()) {
TEST(true); // Token with no key id
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoKeyID");
return false;
}
auto key = FlowTransport::transport().getPublicKeyByName(t.keyId.get());
if (!key.present()) {
TEST(true); // Token referencing non-existing key
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "UnknownKey");
return false;
} else if (!t.expiresAtUnixTime.present()) {
TEST(true); // Token has no expiration time
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoExpirationTime");
return false;
} else if (double(t.expiresAtUnixTime.get()) <= currentTime) {
TEST(true); // Expired token
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "Expired");
return false;
} else if (!t.notBeforeUnixTime.present()) {
TEST(true); // Token has no not-before field
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoNotBefore");
return false;
} else if (double(t.notBeforeUnixTime.get()) > currentTime) {
TEST(true); // Token has no not-before field
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "TokenNotYetValid");
return false;
} else if (!t.tenants.present()) {
TEST(true); // Token with no tenants
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoTenants");
return false;
} else if (!authz::jwt::verifyToken(token, key.get())) {
TEST(true); // Token with invalid signature
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "InvalidSignature");
return false;
} else {
CacheEntry c;
c.expirationTime = double(t.expiresAtUnixTime.get());
for (auto tenant : t.tenants.get()) {
c.tenants.insert(StringRef(c.arena, tenant));
}
StringRef signature(c.arena, signature);
cache.insert(signature, c);
return true;
}
}
bool TokenCacheImpl::validate(TenantNameRef name, StringRef token) {
auto sig = authz::jwt::signaturePart(token);
auto cachedEntry = cache.get(sig);
double currentTime = g_network->timer();
NetworkAddress peer = FlowTransport::transport().currentDeliveryPeerAddress();
if (cachedEntry.has_value()) {
auto& entry = cachedEntry.get();
if (entry.expirationTime > currentTime) {
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "Expired");
throw permission_denied();
}
if (entry.tenants.count(name) == 0) {
TraceEvent(SevWarn, "TenantTokenMismatch").detail("From", peer).detail("Tenant", name.toString());
throw permission_denied();
}
return true;
} else {
Arena arena;
authz::jwt::TokenRef t;
if (!authz::jwt::parseToken(arena, t, token)) {
throw permission_denied();
}
if (!t.keyId.present()) {
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoKeyID");
return false;
}
auto key = FlowTransport::transport().getPublicKeyByName(t.keyId.get());
if (key.present() && authz::jwt::verifyToken(token, key.get())) {
if (!t.expiresAtUnixTime.present()) {
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoExpirationTime");
throw permission_denied();
} else if (double(t.expiresAtUnixTime.get()) <= currentTime) {
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "Expired");
return false;
}
if (!t.notBeforeUnixTime.present()) {
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoNotBefore");
return false;
} else if (double(t.notBeforeUnixTime.get()) > currentTime) {
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "TokenNotYetValid");
return false;
}
if (!t.tenants.present()) {
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "NoTenants");
return false;
}
CacheEntry c;
c.expirationTime = double(t.expiresAtUnixTime.get());
for (auto tenant : t.tenants.get()) {
c.tenants.insert(StringRef(c.arena, tenant));
}
StringRef signature(c.arena, sig);
return true;
if (!cachedEntry.has_value()) {
if (validateAndAdd(currentTime, sig, token, peer)) {
cachedEntry = cache.get(sig);
} else {
TraceEvent(SevWarn, "InvalidSignature")
.detail("From", peer)
.detail("Reason", key.present() ? "VerificationFailed" : "KeyNotFound");
return false;
}
}
ASSERT(cachedEntry.has_value());
auto& entry = cachedEntry.get();
if (entry.expirationTime < currentTime) {
TEST(true); // Read expired token from cache
TraceEvent(SevWarn, "InvalidToken").detail("From", peer).detail("Reason", "Expired");
return false;
}
if (entry.tenants.count(name) == 0) {
TEST(true); // Valid token doesn't reference tenant
TraceEvent(SevWarn, "TenantTokenMismatch").detail("From", peer).detail("Tenant", name.toString());
return false;
}
return true;
}