ilk
This commit is contained in:
601
svc/permissions/repository.go
Normal file
601
svc/permissions/repository.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user