package permissions import ( "database/sql" "log" "strings" "github.com/lib/pq" ) type PermissionRepository struct { DB *sql.DB } func NewPermissionRepository(db *sql.DB) *PermissionRepository { return &PermissionRepository{DB: db} } /* ===================================================== MATRIX READ (V2) - ROLE BASED ===================================================== */ func (r *PermissionRepository) GetPermissionMatrixForRoles( roleIDs []int, ) ([]PermissionMatrixRow, error) { if len(roleIDs) == 0 { return []PermissionMatrixRow{}, nil } query := ` SELECT rp.role_id, rol.code, rp.module_code, rp.action, rp.allowed FROM mk_sys_role_permissions rp JOIN dfrole rol ON rol.id = rp.role_id WHERE rp.role_id = ANY($1) ORDER BY rol.id, rp.module_code, rp.action ` rows, err := r.DB.Query(query, pq.Array(roleIDs)) if err != nil { return nil, err } defer rows.Close() list := make([]PermissionMatrixRow, 0) for rows.Next() { var row PermissionMatrixRow if err := rows.Scan( &row.RoleID, &row.RoleCode, &row.Module, &row.Action, &row.Allowed, ); err != nil { return nil, err } row.Source = "role" list = append(list, row) } return list, nil } /* ===================================================== MATRIX UPDATE (V2) - ROLE BASED ===================================================== */ func (r *PermissionRepository) UpdatePermissions( list []PermissionUpdateRequest, ) error { tx, err := r.DB.Begin() if err != nil { return err } defer tx.Rollback() stmt, err := tx.Prepare(` INSERT INTO mk_sys_role_permissions (role_id, module_code, action, allowed) VALUES ($1,$2,$3,$4) ON CONFLICT (role_id, module_code, action) DO UPDATE SET allowed = EXCLUDED.allowed `) if err != nil { return err } defer stmt.Close() for _, p := range list { if _, err := stmt.Exec( p.RoleID, p.Module, p.Action, p.Allowed, ); err != nil { return err } } return tx.Commit() } /* ===================================================== USER OVERRIDES - READ GET /api/users/{id}/permissions ===================================================== */ // Tek tip: PermissionMatrixRow döndürüyoruz (source=user) func (r *PermissionRepository) GetUserOverridesByUserID( userID int64, ) ([]PermissionMatrixRow, error) { rows, err := r.DB.Query(` SELECT user_id, module_code, action, allowed FROM mk_sys_user_permissions WHERE user_id = $1 ORDER BY module_code, action `, userID) if err != nil { return nil, err } defer rows.Close() list := make([]PermissionMatrixRow, 0) for rows.Next() { var row PermissionMatrixRow if err := rows.Scan( &row.UserID, &row.Module, &row.Action, &row.Allowed, ); err != nil { return nil, err } row.Source = "user" list = append(list, row) } return list, nil } /* ===================================================== USER OVERRIDES - UPSERT SAVE (typed) POST /api/users/{id}/permissions ===================================================== */ func (r *PermissionRepository) SaveUserOverrides( userID int64, list []UserPermissionRequest, ) error { log.Println("➡️ REPO SaveUserOverrides START") log.Println("USER:", userID) log.Println("ROWS:", len(list)) tx, err := r.DB.Begin() if err != nil { log.Println("❌ TX BEGIN ERROR:", err) return err } defer tx.Rollback() // önce sil _, err = tx.Exec(` DELETE FROM mk_sys_user_permissions WHERE user_id = $1 `, userID) if err != nil { log.Println("❌ DELETE ERROR:", err) return err } stmt, err := tx.Prepare(` INSERT INTO mk_sys_user_permissions (user_id, module_code, action, allowed) VALUES ($1,$2,$3,$4) `) if err != nil { log.Println("❌ PREPARE ERROR:", err) return err } defer stmt.Close() for _, p := range list { if strings.TrimSpace(p.Module) == "" { log.Printf("⚠️ SKIP EMPTY MODULE user=%d action=%s", userID, p.Action, ) continue } _, err := stmt.Exec( userID, p.Module, p.Action, p.Allowed, ) if err != nil { log.Println("❌ INSERT ERROR:", err) return err } } if err := tx.Commit(); err != nil { log.Println("❌ COMMIT ERROR:", err) return err } log.Println("✅ REPO SaveUserOverrides DONE") return nil } /* ===================================================== RESOLUTION HELPERS (middleware için) ===================================================== */ // user override var mı? varsa *bool döner, yoksa nil func (r *PermissionRepository) HasUserOverride( userID int64, module string, action string, ) (*bool, error) { var allowed bool err := r.DB.QueryRow(` SELECT allowed FROM mk_sys_user_permissions WHERE user_id=$1 AND module_code=$2 AND action=$3 `, userID, module, action, ).Scan(&allowed) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } return &allowed, nil } // roleIDs içinden OR logic: herhangi biri allowed=true ise true func (r *PermissionRepository) HasRoleAccess( roleIDs []int, module string, action string, ) (bool, error) { if len(roleIDs) == 0 { return false, nil } var count int err := r.DB.QueryRow(` SELECT COUNT(*) FROM mk_sys_role_permissions WHERE role_id = ANY($1) AND module_code=$2 AND action=$3 AND allowed=true `, pq.Array(roleIDs), module, action, ).Scan(&count) if err != nil { return false, err } return count > 0, nil } // Final decision: user override varsa onu uygula, yoksa role bazlı func (r *PermissionRepository) ResolvePermission( userID int64, roleIDs []int, module string, action string, ) (bool, error) { override, err := r.HasUserOverride(userID, module, action) if err != nil { return false, err } if override != nil { return *override, nil } return r.HasRoleAccess(roleIDs, module, action) } func (r *PermissionRepository) GetUserOverrides( userID int64, ) ([]UserPermissionOverride, error) { rows, err := r.DB.Query(` SELECT module_code, action, allowed FROM mk_sys_user_permissions WHERE user_id = $1 `, userID) if err != nil { return nil, err } defer rows.Close() var list []UserPermissionOverride for rows.Next() { var o UserPermissionOverride if err := rows.Scan( &o.Module, &o.Action, &o.Allowed, ); err != nil { return nil, err } list = append(list, o) } return list, nil } func (r *PermissionRepository) UpdateUserOverrides( list []UserPermission, ) error { tx, err := r.DB.Begin() if err != nil { return err } defer tx.Rollback() stmt, err := tx.Prepare(` INSERT INTO mk_sys_user_permissions (user_id, module_code, action, allowed) VALUES ($1,$2,$3,$4) ON CONFLICT (user_id, module_code, action) DO UPDATE SET allowed = EXCLUDED.allowed `) if err != nil { return err } defer stmt.Close() for _, p := range list { _, err := stmt.Exec( p.UserID, p.Module, p.Action, p.Allowed, ) if err != nil { return err } } return tx.Commit() } func (r *PermissionRepository) ResolveEffectivePermission( userID int64, roleID int64, deptCode string, module string, action string, ) (bool, error) { // 1️⃣ USER OVERRIDE var allowed bool err := r.DB.QueryRow(` SELECT allowed FROM mk_sys_user_permissions WHERE user_id = $1 AND module_code = $2 AND action = $3 `, userID, module, action, ).Scan(&allowed) if err == nil { return allowed, nil } if err != sql.ErrNoRows { return false, err } // ================================================== // 2️⃣ ROLE + DEPARTMENT // ================================================== if len(deptCode) > 0 { var allowed bool err = r.DB.QueryRow(` -- 🔥 := DEĞİL = SELECT allowed FROM vw_role_dept_permissions WHERE role_id = $1 AND department_code = ANY($2) AND module_code = $3 AND action = $4 ORDER BY allowed DESC LIMIT 1 `, roleID, pq.Array([]string{deptCode}), module, action, ).Scan(&allowed) if err == nil { log.Printf( " ↳ ROLE+DEPT OVERRIDE = %v", allowed, ) return allowed, nil } if err != sql.ErrNoRows { log.Println("❌ ROLE+DEPT ERR:", err) return false, err } } // 3️⃣ ROLE DEFAULT err = r.DB.QueryRow(` SELECT allowed FROM mk_sys_role_permissions WHERE role_id = $1 AND module_code = $2 AND action = $3 `, roleID, module, action, ).Scan(&allowed) if err == nil { return allowed, nil } if err != sql.ErrNoRows { return false, err } // 4️⃣ DENY return false, nil } func (r *PermissionRepository) ResolvePermissionChain( userID int64, roleID int64, deptCodes []string, module string, action string, ) (bool, error) { log.Printf( "🔐 PERM CHECK user=%d role=%d dept=%v %s:%s", userID, roleID, deptCodes, module, action, ) // ================================================== // 1️⃣ USER OVERRIDE // ================================================== override, err := r.HasUserOverride(userID, module, action) if err != nil { log.Println("❌ USER OVERRIDE ERR:", err) return false, err } if override != nil { log.Printf( " ↳ USER OVERRIDE = %v", *override, ) return *override, nil } // ================================================== // 2️⃣ ROLE + DEPARTMENT // ================================================== if len(deptCodes) > 0 { var allowed bool err := r.DB.QueryRow(` SELECT allowed FROM vw_role_dept_permissions WHERE role_id = $1 AND department_code IN ( SELECT UNNEST($2::text[]) ) AND module_code = $3 AND action = $4 ORDER BY allowed DESC LIMIT 1 `, roleID, pq.Array(deptCodes), module, action, ).Scan(&allowed) if err == nil { log.Printf( " ↳ ROLE+DEPT OVERRIDE = %v", allowed, ) return allowed, nil } if err != sql.ErrNoRows { log.Println("❌ ROLE+DEPT ERR:", err) return false, err } } // ================================================== // 3️⃣ ROLE DEFAULT // ================================================== var roleAllowed bool err = r.DB.QueryRow(` SELECT allowed FROM mk_sys_role_permissions WHERE role_id = $1 AND module_code = $2 AND action = $3 `, roleID, module, action, ).Scan(&roleAllowed) if err == nil { log.Printf( " ↳ ROLE DEFAULT = %v", roleAllowed, ) return roleAllowed, nil } if err != sql.ErrNoRows { log.Println("❌ ROLE DEFAULT ERR:", err) return false, err } // ================================================== // 4️⃣ DENY // ================================================== log.Println(" ↳ NO RULE → DENY") return false, nil }