owpengram-server/cmd/telegramloginkeygen/main_test.go

109 lines
3.3 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
telegramlogin "telesrv/internal/app/telegramlogin"
"telesrv/internal/domain"
)
func TestInitializeAndRotateKeyFiles(t *testing.T) {
dir := t.TempDir()
now := time.Date(2026, 7, 21, 1, 2, 3, 0, time.UTC)
if err := run(options{mode: "init", dir: dir, now: func() time.Time { return now }}); err != nil {
t.Fatal(err)
}
ring, err := telegramlogin.LoadSigningKeyRing(filepath.Join(dir, "signing-keys.json"), func() time.Time { return now })
if err != nil {
t.Fatal(err)
}
if got := ring.SupportedAlgorithms(); len(got) != 3 || got[0] != "RS256" || got[1] != "ES256" || got[2] != "EdDSA" {
t.Fatalf("supported algorithms = %#v", got)
}
if _, err := telegramlogin.LoadCodeSealer(filepath.Join(dir, "code-keys.json")); err != nil {
t.Fatal(err)
}
if _, err := telegramlogin.LoadClientSecretPepper(filepath.Join(dir, "client-secret-pepper")); err != nil {
t.Fatal(err)
}
if err := run(options{mode: "init", dir: dir, now: func() time.Time { return now }}); err == nil {
t.Fatal("second initialization unexpectedly overwrote keys")
}
later := now.Add(time.Minute)
if err := run(options{
mode: "rotate-signing", dir: dir, algorithm: domain.TelegramLoginSigningRS256,
publishFor: 2 * time.Hour, idTokenTTL: time.Hour, now: func() time.Time { return later },
}); err != nil {
t.Fatal(err)
}
var manifest signingManifest
readJSONForTest(t, filepath.Join(dir, "signing-keys.json"), &manifest)
active, retiring := 0, 0
for _, key := range manifest.Keys {
if key.Algorithm != domain.TelegramLoginSigningRS256 {
continue
}
if key.Active {
active++
} else if key.PublishUntil == later.Add(2*time.Hour).Format(time.RFC3339) {
retiring++
}
}
if active != 1 || retiring != 1 {
t.Fatalf("RS256 active=%d retiring=%d manifest=%#v", active, retiring, manifest)
}
ring, err = telegramlogin.LoadSigningKeyRing(filepath.Join(dir, "signing-keys.json"), func() time.Time { return later })
if err != nil {
t.Fatal(err)
}
jwks, _, err := ring.JWKS()
if err != nil {
t.Fatal(err)
}
var set struct {
Keys []json.RawMessage `json:"keys"`
}
if err := json.Unmarshal(jwks, &set); err != nil || len(set.Keys) != 4 {
t.Fatalf("JWKS key count=%d err=%v body=%s", len(set.Keys), err, jwks)
}
var before codeManifest
readJSONForTest(t, filepath.Join(dir, "code-keys.json"), &before)
if err := run(options{mode: "rotate-code", dir: dir, now: func() time.Time { return later }}); err != nil {
t.Fatal(err)
}
var after codeManifest
readJSONForTest(t, filepath.Join(dir, "code-keys.json"), &after)
if after.Active == before.Active || len(after.Keys) != 2 {
t.Fatalf("code ring before=%#v after=%#v", before, after)
}
if _, err := telegramlogin.LoadCodeSealer(filepath.Join(dir, "code-keys.json")); err != nil {
t.Fatal(err)
}
}
func TestRotateSigningRejectsTooShortRetirementWindow(t *testing.T) {
err := run(options{
mode: "rotate-signing", dir: t.TempDir(), algorithm: domain.TelegramLoginSigningRS256,
publishFor: 69 * time.Minute, idTokenTTL: time.Hour, now: time.Now,
})
if err == nil {
t.Fatal("short publish-for unexpectedly accepted")
}
}
func readJSONForTest(t *testing.T, path string, target any) {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if err := json.Unmarshal(data, target); err != nil {
t.Fatal(err)
}
}