// Tests for user NKey revocation on Account. // Go reference: accounts_test.go TestJWTUserRevocation, checkUserRevoked (~line 3202), // isRevoked with jwt.All global key (~line 2929). using NATS.Server.Auth; namespace NATS.Server.Tests.Auth; public class NKeyRevocationTests { // ── 1 ────────────────────────────────────────────────────────────────────── [Fact] public void RevokeUser_AddsToRevokedList() { var account = new Account("A"); account.RevokeUser("UNKEY1", 100L); account.RevokedUserCount.ShouldBe(1); } // ── 2 ────────────────────────────────────────────────────────────────────── [Fact] public void IsUserRevoked_Revoked_ReturnsTrue() { // A JWT issued at t=50 revoked when the revocation timestamp is 100 // means issuedAt (50) <= revokedAt (100) → revoked. // Go reference: accounts.go isRevoked — t < issuedAt ⇒ NOT revoked (inverted). var account = new Account("A"); account.RevokeUser("UNKEY1", 100L); account.IsUserRevoked("UNKEY1", 50L).ShouldBeTrue(); } // ── 3 ────────────────────────────────────────────────────────────────────── [Fact] public void IsUserRevoked_NotRevoked_ReturnsFalse() { // A JWT issued at t=200 with revocation timestamp 100 means // issuedAt (200) > revokedAt (100) → NOT revoked. var account = new Account("A"); account.RevokeUser("UNKEY1", 100L); account.IsUserRevoked("UNKEY1", 200L).ShouldBeFalse(); } // ── 4 ────────────────────────────────────────────────────────────────────── [Fact] public void RevokedUserCount_MatchesRevocations() { var account = new Account("A"); account.RevokedUserCount.ShouldBe(0); account.RevokeUser("UNKEY1", 1L); account.RevokedUserCount.ShouldBe(1); account.RevokeUser("UNKEY2", 2L); account.RevokedUserCount.ShouldBe(2); // Revoking the same key again does not increase count. account.RevokeUser("UNKEY1", 99L); account.RevokedUserCount.ShouldBe(2); } // ── 5 ────────────────────────────────────────────────────────────────────── [Fact] public void GetRevokedUsers_ReturnsAllKeys() { var account = new Account("A"); account.RevokeUser("UNKEY1", 1L); account.RevokeUser("UNKEY2", 2L); account.RevokeUser("UNKEY3", 3L); var keys = account.GetRevokedUsers(); keys.Count.ShouldBe(3); keys.ShouldContain("UNKEY1"); keys.ShouldContain("UNKEY2"); keys.ShouldContain("UNKEY3"); } // ── 6 ────────────────────────────────────────────────────────────────────── [Fact] public void UnrevokeUser_RemovesRevocation() { var account = new Account("A"); account.RevokeUser("UNKEY1", 100L); account.RevokedUserCount.ShouldBe(1); var removed = account.UnrevokeUser("UNKEY1"); removed.ShouldBeTrue(); account.RevokedUserCount.ShouldBe(0); account.IsUserRevoked("UNKEY1", 50L).ShouldBeFalse(); } // ── 7 ────────────────────────────────────────────────────────────────────── [Fact] public void UnrevokeUser_NonExistent_ReturnsFalse() { var account = new Account("A"); var removed = account.UnrevokeUser("DOES_NOT_EXIST"); removed.ShouldBeFalse(); account.RevokedUserCount.ShouldBe(0); } // ── 8 ────────────────────────────────────────────────────────────────────── [Fact] public void ClearAllRevocations_EmptiesList() { var account = new Account("A"); account.RevokeUser("UNKEY1", 1L); account.RevokeUser("UNKEY2", 2L); account.RevokeAllUsers(999L); account.RevokedUserCount.ShouldBe(3); account.ClearAllRevocations(); account.RevokedUserCount.ShouldBe(0); account.GetRevokedUsers().ShouldBeEmpty(); account.IsGlobalRevocation().ShouldBeFalse(); } // ── 9 ────────────────────────────────────────────────────────────────────── [Fact] public void RevokeAllUsers_SetsGlobalRevocation() { // Go reference: accounts.go — Revocations[jwt.All] used in isRevoked (~line 2934). // The "*" key causes any user whose issuedAt <= timestamp to be revoked. var account = new Account("A"); account.RevokeAllUsers(500L); account.IsGlobalRevocation().ShouldBeTrue(); // User issued at 500 is revoked (≤ 500). account.IsUserRevoked("ANY_USER", 500L).ShouldBeTrue(); // User issued at 499 is also revoked. account.IsUserRevoked("ANY_USER", 499L).ShouldBeTrue(); // User issued at 501 is NOT revoked (> 500). account.IsUserRevoked("ANY_USER", 501L).ShouldBeFalse(); } // ── 10 ───────────────────────────────────────────────────────────────────── [Fact] public void GetRevocationInfo_ReturnsComplete() { var account = new Account("A"); account.RevokeUser("UNKEY1", 10L); account.RevokeUser("UNKEY2", 20L); account.RevokeAllUsers(999L); var info = account.GetRevocationInfo(); // Two per-user keys + one global "*" key = 3 total. info.RevokedCount.ShouldBe(3); info.HasGlobalRevocation.ShouldBeTrue(); info.RevokedNKeys.Count.ShouldBe(3); info.RevokedNKeys.ShouldContain("UNKEY1"); info.RevokedNKeys.ShouldContain("UNKEY2"); info.RevokedNKeys.ShouldContain("*"); } }