package store import ( "bytes" "context" "path/filepath" "strings" "testing" ) func testKey(b byte) []byte { return bytes.Repeat([]byte{b}, 32) } func TestSecretsEncryptedAndKeyChecked(t *testing.T) { ctx := context.Background() path := filepath.Join(t.TempDir(), "t.db") st, err := Open(path, testKey(1)) if err != nil { t.Fatal(err) } if err := st.SaveCluster(ctx, "", Cluster{Name: "ref", Reference: true, URL: "https://ref:8006", TokenID: "a@pve!ro", Secret: "s3cr3t-uuid"}); err != nil { t.Fatal(err) } var blob []byte if err := st.db.QueryRowContext(ctx, `SELECT secret FROM clusters WHERE name = 'ref'`).Scan(&blob); err != nil { t.Fatal(err) } if bytes.Contains(blob, []byte("s3cr3t")) { t.Fatal("secret stored in clear") } cls, _ := st.Clusters(ctx) if len(cls) != 1 || cls[0].Secret != "" || !cls[0].HasSecret { t.Fatalf("listing must hide the secret: %+v", cls) } // an empty secret on update keeps the stored one c := cls[0] c.URL = "https://ref2:8006" if err := st.SaveCluster(ctx, "ref", c); err != nil { t.Fatal(err) } cfg, err := st.Config(ctx) if err != nil { t.Fatal(err) } if s, _ := cfg.Reference.ResolveSecret(); s != "s3cr3t-uuid" { t.Fatalf("secret after update: %q", s) } exp, _ := st.ExportConfig(ctx) if exp.Reference.Secret != "" || exp.Reference.TokenSecretEnv != "PVE_REF_SECRET" { t.Fatalf("export: %+v", exp.Reference) } st.Close() if _, err := Open(path, testKey(2)); err == nil { t.Fatal("a wrong key must be refused") } } func TestUsers(t *testing.T) { ctx := context.Background() st, err := Open(filepath.Join(t.TempDir(), "t.db"), testKey(1)) if err != nil { t.Fatal(err) } defer st.Close() if err := st.SaveUser(ctx, "admin", RoleAdmin, "short"); err == nil { t.Fatal("short password accepted") } if err := st.SaveUser(ctx, "admin", RoleAdmin, "long-enough"); err != nil { t.Fatal(err) } if _, ok := st.Authenticate(ctx, "admin", "wrong-pass"); ok { t.Fatal("wrong password accepted") } u, ok := st.Authenticate(ctx, "admin", "long-enough") if !ok || u.Role != RoleAdmin { t.Fatalf("login: %+v %v", u, ok) } if err := st.SaveUser(ctx, "admin", RoleViewer, ""); err == nil { t.Fatal("last admin demoted") } if err := st.DeleteUser(ctx, "admin"); err == nil { t.Fatal("last admin deleted") } // a new password changes the epoch (open sessions are invalidated) if err := st.SaveUser(ctx, "admin", RoleAdmin, "another-one"); err != nil { t.Fatal(err) } u2, _ := st.User(ctx, "admin") if u2.Epoch == u.Epoch { t.Fatal("epoch unchanged after a password change") } } func TestPasswordHash(t *testing.T) { h, err := HashPassword("p@ssw0rd!") if err != nil { t.Fatal(err) } if strings.Contains(h, "p@ssw0rd") || !CheckPassword(h, "p@ssw0rd!") || CheckPassword(h, "p@ssw0rd") { t.Fatal("hash check") } }