This commit is contained in:
2026-02-11 17:46:22 +03:00
commit eacfacb13b
266 changed files with 51337 additions and 0 deletions

View File

@@ -0,0 +1,601 @@
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
}