package server import ( "context" "encoding/json" "strings" "sync" "testing" "vdi-cloud/platform/internal/store" ) const testAccountPassword = "Account-test-2026" func createTestAccount(t *testing.T, f *fixture, token, username, role string) string { t.Helper() status, out := f.request("POST", "/administrators", token, map[string]any{"username": username, "displayName": "Account test", "role": role, "password": testAccountPassword}, "") expect(t, 201, status, out) for _, field := range []string{"password", "passwordHash", "password_hash"} { if _, ok := out[field]; ok { t.Fatal("credential leaked", field) } } return out["id"].(string) } func accountLogin(t *testing.T, f *fixture, username, password string) string { t.Helper() status, out := f.request("POST", "/auth/login", "", map[string]any{"username": username, "password": password}, "") expect(t, 200, status, out) return out["accessToken"].(string) } func TestAdministratorLifecycleAndSessionRevocation(t *testing.T) { f := newFixture(t) admin := f.login("admin") id := createTestAccount(t, f, admin, "maintainer", "operator") token := accountLogin(t, f, "maintainer", testAccountPassword) status, out := f.request("GET", "/administrators", token, nil, "") expect(t, 403, status, out) status, out = f.request("PUT", "/administrators/"+id, admin, map[string]any{"displayName": "Read-only", "role": "viewer", "enabled": true}, "") expect(t, 200, status, out) status, out = f.request("GET", "/auth/me", token, nil, "") expect(t, 401, status, out) token = accountLogin(t, f, "maintainer", testAccountPassword) status, out = f.request("POST", "/desktops/desk_demo_kylin/actions", token, map[string]any{"action": "start"}, "viewer-no-power") expect(t, 403, status, out) status, out = f.request("POST", "/administrators/"+id+"/password", admin, map[string]any{"password": "Reset-test-2026"}, "") expect(t, 204, status, out) status, out = f.request("GET", "/auth/me", token, nil, "") expect(t, 401, status, out) status, out = f.request("POST", "/auth/login", "", map[string]any{"username": "maintainer", "password": testAccountPassword}, "") expect(t, 401, status, out) token = accountLogin(t, f, "maintainer", "Reset-test-2026") status, out = f.request("PUT", "/administrators/"+id, admin, map[string]any{"displayName": "Disabled", "role": "viewer", "enabled": false}, "") expect(t, 200, status, out) status, out = f.request("GET", "/auth/me", token, nil, "") expect(t, 401, status, out) status, out = f.request("POST", "/auth/login", "", map[string]any{"username": "maintainer", "password": "Reset-test-2026"}, "") expect(t, 401, status, out) if err := store.Bootstrap(context.Background(), f.db, "demo", "", ""); err != nil { t.Fatal(err) } var enabled bool if err := f.db.Get(&enabled, "SELECT enabled FROM administrators WHERE id=$1", id); err != nil { t.Fatal(err) } if enabled { t.Fatal("restart re-enabled disabled account") } status, out = f.request("PUT", "/administrators/"+id, admin, map[string]any{"displayName": "Restored", "role": "operator", "enabled": true}, "") expect(t, 200, status, out) _ = accountLogin(t, f, "maintainer", "Reset-test-2026") var audit []string if err := f.db.Select(&audit, "SELECT action||':'||target FROM audit_logs WHERE target='maintainer' AND action LIKE '%administrator%'"); err != nil { t.Fatal(err) } if len(audit) != 5 { t.Fatal("account changes not audited", audit) } status, out = f.request("GET", "/administrators", admin, nil, "") expect(t, 200, status, out) encoded, _ := json.Marshal(out) if strings.Contains(string(encoded), "$2") || strings.Contains(string(encoded), testAccountPassword) { t.Fatal("credential material in account listing") } } func TestOwnPasswordRequiresCurrentPasswordAndInvalidatesAllSessions(t *testing.T) { f := newFixture(t) token := f.login("viewer") other := f.login("viewer") status, out := f.request("POST", "/auth/password", token, map[string]any{"currentPassword": "wrong-password", "newPassword": "Updated-test-2026"}, "") expect(t, 400, status, out) status, out = f.request("GET", "/auth/me", token, nil, "") expect(t, 200, status, out) status, out = f.request("POST", "/auth/password", token, map[string]any{"currentPassword": "Demo@2026", "newPassword": "Updated-test-2026"}, "") expect(t, 204, status, out) for _, old := range []string{token, other} { status, out = f.request("GET", "/auth/me", old, nil, "") expect(t, 401, status, out) } status, out = f.request("POST", "/auth/login", "", map[string]any{"username": "viewer", "password": "Demo@2026"}, "") expect(t, 401, status, out) _ = accountLogin(t, f, "viewer", "Updated-test-2026") var audit string if err := f.db.Get(&audit, "SELECT action FROM audit_logs WHERE action='change_own_password'"); err != nil { t.Fatal(err) } } func TestAdministratorValidationRBACAndLastOwner(t *testing.T) { f := newFixture(t) admin := f.login("admin") var adminID string if err := f.db.Get(&adminID, "SELECT id FROM administrators WHERE username='admin'"); err != nil { t.Fatal(err) } for _, role := range []string{"operator", "viewer"} { token := f.login(role) for _, r := range []struct{ method, path string }{{"GET", "/administrators"}, {"POST", "/administrators"}, {"PUT", "/administrators/" + adminID}, {"POST", "/administrators/" + adminID + "/password"}} { status, out := f.request(r.method, r.path, token, map[string]any{}, "") expect(t, 403, status, out) } } for _, password := range []string{"short", strings.Repeat("a", 73), strings.Repeat("密", 25)} { status, out := f.request("POST", "/administrators", admin, map[string]any{"username": "invalid-pass", "displayName": "Invalid", "role": "viewer", "password": password}, "") expect(t, 400, status, out) } body := map[string]any{"username": "test-user", "displayName": "Valid", "role": "super_admin", "password": testAccountPassword} status, out := f.request("POST", "/administrators", admin, body, "") expect(t, 201, status, out) status, out = f.request("POST", "/administrators", admin, body, "") expect(t, 409, status, out) status, out = f.request("PUT", "/administrators/"+adminID, admin, map[string]any{"displayName": "Self", "role": "viewer", "enabled": true}, "") expect(t, 409, status, out) status, out = f.request("POST", "/administrators/"+adminID+"/password", admin, map[string]any{"password": testAccountPassword}, "") expect(t, 409, status, out) status, out = f.request("PUT", "/administrators/"+adminID, admin, map[string]any{"displayName": "Self name", "role": "super_admin", "enabled": true}, "") expect(t, 200, status, out) status, out = f.request("GET", "/auth/me", admin, nil, "") expect(t, 200, status, out) if out["displayName"] != "Self name" { t.Fatal(out) } // The final owner's role and enabled state remain protected independently of UI restrictions. if _, err := f.db.Exec("UPDATE administrators SET enabled=false WHERE username='test-user'"); err != nil { t.Fatal(err) } status, out = f.request("PUT", "/administrators/"+adminID, admin, map[string]any{"displayName": "Self", "role": "super_admin", "enabled": false}, "") expect(t, 409, status, out) if out["code"] != "last_super_admin" { t.Fatal(out) } } func TestConcurrentAdministratorDisableKeepsOneOwner(t *testing.T) { f := newFixture(t) admin := f.login("admin") otherID := createTestAccount(t, f, admin, "second-owner", "super_admin") other := accountLogin(t, f, "second-owner", testAccountPassword) var adminID string if err := f.db.Get(&adminID, "SELECT id FROM administrators WHERE username='admin'"); err != nil { t.Fatal(err) } start := make(chan struct{}) results := make(chan int, 2) var group sync.WaitGroup for _, r := range []struct{ token, target string }{{admin, otherID}, {other, adminID}} { group.Add(1) go func(token, target string) { defer group.Done() <-start status, _ := f.request("PUT", "/administrators/"+target, token, map[string]any{"displayName": "Concurrent", "role": "super_admin", "enabled": false}, "") results <- status }(r.token, r.target) } close(start) group.Wait() close(results) success := 0 for status := range results { if status == 200 { success++ } else if status != 401 && status != 403 { t.Fatalf("unexpected losing request status %d", status) } } var count int if err := f.db.Get(&count, "SELECT count(*) FROM administrators WHERE enabled AND role='super_admin'"); err != nil { t.Fatal(err) } if success != 1 || count != 1 { t.Fatal("concurrent writes lost owner", success, count) } }