Files
Gardomatic/internal/storage/postgres/roles_integration_test.go
T
kleiax 904d14b64c
CI / test (push) Canceled after 0s
Initial commit
2026-09-12 22:22:17 +02:00

104 lines
3.6 KiB
Go

package postgres
import (
"database/sql"
"os"
"strconv"
"testing"
"time"
"gardomatic.kleiax.de/internal/auth"
"gardomatic.kleiax.de/internal/storage"
_ "github.com/lib/pq"
)
func TestScopedRolesAndPersistedPermissions(t *testing.T) {
dsn := os.Getenv("GARDOMATIC_TEST_DB_DSN")
if dsn == "" {
t.Skip("set GARDOMATIC_TEST_DB_DSN to run PostgreSQL integration tests")
}
db, err := sql.Open("postgres", dsn)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = db.Close() })
if err = db.Ping(); err != nil {
t.Fatal(err)
}
stamp := time.Now().Format("150405.000000000")
password := auth.NewPassword([]byte("integration-test-hash"))
users := UserModel{DB: db}
user, err := users.Insert(storage.User{Name: "Role integration", Email: "roles-" + stamp + "@example.com", Password: *password, Activated: true})
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _, _ = db.Exec(`DELETE FROM users WHERE id=$1`, user.ID) })
if user.Role != storage.ApplicationRoleUser || !user.Can(storage.ApplicationPermissionGardensCreate) {
t.Fatalf("new user role=%q permissions=%v", user.Role, user.Permissions)
}
user, err = users.UpdateRole(user.ID, storage.ApplicationRoleAdmin)
if err != nil {
t.Fatal(err)
}
if !user.Can(storage.ApplicationPermissionUsersManage) || !user.Can(storage.ApplicationPermissionRolesManage) {
t.Fatalf("admin permissions were not loaded: %v", user.Permissions)
}
var gardenID int
if err = db.QueryRow(`INSERT INTO gardens (name) VALUES ($1) RETURNING id`, "Role integration "+stamp).Scan(&gardenID); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _, _ = db.Exec(`DELETE FROM gardens WHERE id=$1`, gardenID) })
members := GardenMemberModel{DB: db}
member, err := members.Insert(storage.GardenMember{GardenID: gardenID, UserID: user.ID, Role: storage.GardenRoleOwner})
if err != nil {
t.Fatal(err)
}
if !member.Can(storage.GardenPermissionGardenDelete) {
t.Fatal("owner wildcard permission was not expanded")
}
if err = (RoleModel{DB: db}).ReplaceGardenOverrides(gardenID, string(storage.GardenRoleOwner), []storage.GardenRolePermissionOverride{{GardenID: gardenID, RoleName: string(storage.GardenRoleOwner), Permission: string(storage.GardenPermissionGardenDelete), Granted: false}}); err != nil {
t.Fatal(err)
}
member, err = members.Get(gardenID, user.ID)
if err != nil {
t.Fatal(err)
}
if member.Can(storage.GardenPermissionGardenDelete) || !member.Can(storage.GardenPermissionGardenUpdate) {
t.Fatalf("garden override was not applied: %v", member.Permissions)
}
var otherGardenID int
if err = db.QueryRow(`INSERT INTO gardens (name) VALUES ($1) RETURNING id`, "Other role integration "+stamp).Scan(&otherGardenID); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _, _ = db.Exec(`DELETE FROM gardens WHERE id=$1`, otherGardenID) })
roleName := "garden:" + strconv.Itoa(gardenID) + ":integration"
role, err := (RoleModel{DB: db}).Create(storage.Role{
Name: roleName, Scope: storage.RoleScopeGarden, GardenID: &gardenID,
Label: "Integration", Permissions: []string{string(storage.GardenPermissionGardenRead)},
})
if err != nil {
t.Fatal(err)
}
if role.GardenID == nil || *role.GardenID != gardenID {
t.Fatalf("garden-specific role has garden_id=%v", role.GardenID)
}
roles, err := (RoleModel{DB: db}).ListForGarden(gardenID)
if err != nil {
t.Fatal(err)
}
found := false
for _, candidate := range roles {
found = found || candidate.Name == roleName
}
if !found {
t.Fatalf("garden-specific role %q is missing", roleName)
}
if _, err = (RoleModel{DB: db}).GetForGarden(otherGardenID, roleName); err != storage.ErrRecordNotFound {
t.Fatalf("role leaked into another garden: %v", err)
}
}