package registry import ( "context" "crypto/ed25519" "crypto/rand" "encoding/base64" "encoding/json" "strings" "sync" "testing" "time" "github.com/klarkxy/nekonest-cloud/relay/internal/authsnapshot" "github.com/klarkxy/nekonest-cloud/relay/internal/controlplane" ) type snapshotSource struct{} func (snapshotSource) AuthorizationSnapshot(context.Context, string, int64) (controlplane.SnapshotResponse, error) { return controlplane.SnapshotResponse{}, context.Canceled } func (snapshotSource) AuthorizationDelta(context.Context, string, int64) (controlplane.DeltaResponse, error) { return controlplane.DeltaResponse{}, context.Canceled } type signer struct { public ed25519.PublicKey private ed25519.PrivateKey keyring authsnapshot.Keyring } func newSigner(t *testing.T, now time.Time) signer { t.Helper() public, private, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatal(err) } return signer{ public: public, private: private, keyring: authsnapshot.Keyring{"key": { PublicKey: public, NotBefore: now.Add(-time.Hour), RetainUntil: now.Add(time.Hour), }}, } } func (signer signer) response(t *testing.T, now time.Time, tenantID, deviceID string) controlplane.SnapshotResponse { t.Helper() key := base64.RawURLEncoding.EncodeToString(make([]byte, 32)) payload := authsnapshot.Payload{ SnapshotVersion: 1, TenantID: tenantID, TenantStatus: "active", HomeRegion: "test", RelayNodeID: "node_test", PlacementGeneration: 1, AuthorizationRevision: 1, Devices: []authsnapshot.Device{{ DeviceID: deviceID, CredentialHash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", IdentityFingerprint: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Ed25519Public: key, X25519Public: key, Name: "Host", OS: "linux", }}, IssuedAt: now.Add(-time.Second).Format(time.RFC3339Nano), ExpiresAt: now.Add(4 * time.Minute).Format(time.RFC3339Nano), } bytes, err := authsnapshot.SigningBytes(payload) if err != nil { t.Fatal(err) } jwk, _ := json.Marshal(map[string]string{ "kty": "OKP", "crv": "Ed25519", "kid": "key", "x": base64.RawURLEncoding.EncodeToString(signer.public), }) return controlplane.SnapshotResponse{ Snapshot: authsnapshot.Signed{ Algorithm: "Ed25519", KID: "key", Payload: payload, Signature: base64.RawURLEncoding.EncodeToString(ed25519.Sign(signer.private, bytes)), }, PublicKeyJWK: jwk, } } func (signer signer) resign(t *testing.T, response controlplane.SnapshotResponse) controlplane.SnapshotResponse { t.Helper() bytes, err := authsnapshot.SigningBytes(response.Snapshot.Payload) if err != nil { t.Fatal(err) } response.Snapshot.Signature = base64.RawURLEncoding.EncodeToString(ed25519.Sign(signer.private, bytes)) return response } func TestDefaultAuthorizationPollingFitsRevocationObjective(t *testing.T) { now := time.Now().UTC() signer := newSigner(t, now) registryValue, err := New(Config{ DataRoot: t.TempDir(), NodeID: "node_test", AllowedOrigins: []string{"https://pwa.example"}, RouteSecret: []byte("0123456789abcdef0123456789abcdef"), Keyring: signer.keyring, ControlPlane: snapshotSource{}, }) if err != nil { t.Fatal(err) } defer registryValue.Close() if registryValue.config.DeltaInterval != 5*time.Second || registryValue.config.RequestTimeout != 3*time.Second { t.Fatalf("delta=%s timeout=%s", registryValue.config.DeltaInterval, registryValue.config.RequestTimeout) } if worstHealthy := registryValue.config.DeltaInterval + 2*registryValue.config.RequestTimeout; worstHealthy >= 15*time.Second { t.Fatalf("healthy revocation path exceeds objective: %s", worstHealthy) } } func TestConcurrentAcceptReturnsSingleTenantEngine(t *testing.T) { now := time.Now().UTC() signer := newSigner(t, now) registryValue, err := New(Config{ DataRoot: t.TempDir(), NodeID: "node_test", AllowedOrigins: []string{"https://pwa.example"}, RouteSecret: []byte("0123456789abcdef0123456789abcdef"), Keyring: signer.keyring, ControlPlane: snapshotSource{}, RefreshInterval: time.Hour, DeltaInterval: time.Hour, }) if err != nil { t.Fatal(err) } defer registryValue.Close() response := signer.response(t, now, "tenant_0123456789abcdef0123456789abcdef", "host_0123456789abcdef0123456789abcdef", ) results := make([]*Tenant, 8) errorsFound := make([]error, 8) var wait sync.WaitGroup for index := range results { wait.Add(1) go func(index int) { defer wait.Done() results[index], errorsFound[index] = registryValue.Accept(context.Background(), response) }(index) } wait.Wait() for index := range results { if errorsFound[index] != nil || results[index] != results[0] { t.Fatalf("result[%d]=%p err=%v; first=%p", index, results[index], errorsFound[index], results[0]) } } } func TestDeviceIdentityCannotBeOwnedByTwoTenants(t *testing.T) { now := time.Now().UTC() signer := newSigner(t, now) registryValue, err := New(Config{ DataRoot: t.TempDir(), NodeID: "node_test", AllowedOrigins: []string{"https://pwa.example"}, RouteSecret: []byte("0123456789abcdef0123456789abcdef"), Keyring: signer.keyring, ControlPlane: snapshotSource{}, RefreshInterval: time.Hour, DeltaInterval: time.Hour, }) if err != nil { t.Fatal(err) } defer registryValue.Close() deviceID := "host_0123456789abcdef0123456789abcdef" first := signer.response(t, now, "tenant_0123456789abcdef0123456789abcdef", deviceID) second := signer.response(t, now, "tenant_fedcba9876543210fedcba9876543210", deviceID) if _, err := registryValue.Accept(context.Background(), first); err != nil { t.Fatal(err) } if _, err := registryValue.Accept(context.Background(), second); err == nil { t.Fatal("cross-tenant device identity was accepted") } } func TestOlderAuthorizationRevisionCannotRestoreRevokedDevice(t *testing.T) { now := time.Now().UTC() signer := newSigner(t, now) registryValue, err := New(Config{ DataRoot: t.TempDir(), NodeID: "node_test", AllowedOrigins: []string{"https://pwa.example"}, RouteSecret: []byte("0123456789abcdef0123456789abcdef"), Keyring: signer.keyring, ControlPlane: snapshotSource{}, RefreshInterval: time.Hour, DeltaInterval: time.Hour, }) if err != nil { t.Fatal(err) } defer registryValue.Close() tenantID := "tenant_0123456789abcdef0123456789abcdef" deviceID := "host_0123456789abcdef0123456789abcdef" revisionOne := signer.response(t, now, tenantID, deviceID) if _, err := registryValue.Accept(context.Background(), revisionOne); err != nil { t.Fatal(err) } revisionTwo := revisionOne revisionTwo.Snapshot.Payload.AuthorizationRevision = 2 revisionTwo.Snapshot.Payload.Devices = nil revisionTwo = signer.resign(t, revisionTwo) if _, err := registryValue.Accept(context.Background(), revisionTwo); err != nil { t.Fatal(err) } if _, ok := registryValue.ByDeviceID(deviceID); ok { t.Fatal("revoked device remained routable after newer revision") } if _, err := registryValue.Accept(context.Background(), revisionOne); err == nil { t.Fatal("older signed authorization revision restored a revoked device") } if _, ok := registryValue.ByDeviceID(deviceID); ok { t.Fatal("stale revision restored revoked device routing") } } func TestExpiredTenantReleasesActiveCapacity(t *testing.T) { now := time.Now().UTC() signer := newSigner(t, now) registryValue, err := New(Config{ DataRoot: t.TempDir(), NodeID: "node_test", AllowedOrigins: []string{"https://pwa.example"}, RouteSecret: []byte("0123456789abcdef0123456789abcdef"), Keyring: signer.keyring, ControlPlane: snapshotSource{}, RefreshInterval: time.Hour, DeltaInterval: time.Hour, MaxTenants: 1, }) if err != nil { t.Fatal(err) } defer registryValue.Close() firstTenantID := "tenant_0123456789abcdef0123456789abcdef" first, err := registryValue.Accept(context.Background(), signer.response( t, now, firstTenantID, "host_0123456789abcdef0123456789abcdef", )) if err != nil { t.Fatal(err) } registryValue.mu.RLock() firstSlot := registryValue.slots[firstTenantID] registryValue.mu.RUnlock() registryValue.expire(firstSlot, first) if _, err := registryValue.Accept(context.Background(), signer.response( t, now, "tenant_fedcba9876543210fedcba9876543210", "host_fedcba9876543210fedcba9876543210", )); err != nil { t.Fatalf("capacity was not released after expiry: %v", err) } } func TestQuiesceFencesExactGeneration(t *testing.T) { now := time.Now().UTC() signer := newSigner(t, now) registryValue, err := New(Config{ DataRoot: t.TempDir(), NodeID: "node_test", AllowedOrigins: []string{"https://pwa.example"}, RouteSecret: []byte("0123456789abcdef0123456789abcdef"), Keyring: signer.keyring, ControlPlane: snapshotSource{}, RefreshInterval: time.Hour, DeltaInterval: time.Hour, }) if err != nil { t.Fatal(err) } defer registryValue.Close() tenantID := "tenant_0123456789abcdef0123456789abcdef" if _, err := registryValue.Accept(context.Background(), signer.response( t, now, tenantID, "host_0123456789abcdef0123456789abcdef", )); err != nil { t.Fatal(err) } if _, err := registryValue.Quiesce(tenantID, 2); err == nil { t.Fatal("wrong generation was quiesced") } paths, err := registryValue.Quiesce(tenantID, 1) if err != nil { t.Fatal(err) } if paths.Database == "" { t.Fatal("quiesce did not return the fenced tenant paths") } if _, ok := registryValue.ByDeviceID("host_0123456789abcdef0123456789abcdef"); ok { t.Fatal("quiesced tenant remained routable") } } func TestRouteHintIsOpaqueAuthenticatedAndCrossNodeDecodable(t *testing.T) { now := time.Now().UTC() signer := newSigner(t, now) secret := []byte("0123456789abcdef0123456789abcdef") registryValue, err := New(Config{ DataRoot: t.TempDir(), NodeID: "node_test", AllowedOrigins: []string{"https://pwa.example"}, RouteSecret: secret, Keyring: signer.keyring, ControlPlane: snapshotSource{}, }) if err != nil { t.Fatal(err) } defer registryValue.Close() tenantID := "tenant_0123456789abcdef0123456789abcdef" hint := registryValue.routeHint(tenantID, 19) if hint == "" || strings.Contains(hint, tenantID) { t.Fatalf("route hint leaked tenant identity: %q", hint) } decodedTenant, generation, err := registryValue.DecodeRouteHint(hint) if err != nil || decodedTenant != tenantID || generation != 19 { t.Fatalf("decoded tenant=%q generation=%d err=%v", decodedTenant, generation, err) } tamperedBytes, err := base64.RawURLEncoding.DecodeString(hint) if err != nil { t.Fatal(err) } tamperedBytes[len(tamperedBytes)/2] ^= 0x01 tampered := base64.RawURLEncoding.EncodeToString(tamperedBytes) if _, _, err := registryValue.DecodeRouteHint(tampered); err == nil { t.Fatal("tampered route hint was accepted") } }