104 lines
3.6 KiB
Go
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)
|
|
}
|
|
}
|