Files
bssapp/svc/routes/permission_matrix_v2.go
2026-02-11 17:46:22 +03:00

204 lines
3.7 KiB
Go

package routes
import (
"bssapp-backend/auth"
"bssapp-backend/internal/auditlog"
"bssapp-backend/permissions"
"database/sql"
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
func GetRolePermissionMatrix(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", 401)
return
}
roleID, _ := strconv.Atoi(mux.Vars(r)["id"])
rows, err := db.Query(`
SELECT
module_code,
action,
allowed
FROM mk_sys_role_permissions
WHERE role_id=$1
`, roleID)
if err != nil {
http.Error(w, "db error", 500)
return
}
defer rows.Close()
var list []permissions.PermissionMatrixRow
for rows.Next() {
var row permissions.PermissionMatrixRow
if err := rows.Scan(
&row.Module,
&row.Action,
&row.Allowed,
); err != nil {
http.Error(w, "scan error", 500)
return
}
row.Source = "role"
list = append(list, row)
}
json.NewEncoder(w).Encode(list)
}
}
func SaveRolePermissionMatrix(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", 401)
return
}
roleID, _ := strconv.Atoi(mux.Vars(r)["id"])
var list []permissions.PermissionMatrixRow
if err := json.NewDecoder(r.Body).Decode(&list); err != nil {
http.Error(w, "bad payload", 400)
return
}
repo := permissions.NewPermissionRepository(db)
// ================= OLD =================
oldRows, _ := repo.GetPermissionMatrixForRoles([]int{roleID})
oldMap := map[string]bool{}
for _, p := range oldRows {
key := p.Module + ":" + p.Action
oldMap[key] = p.Allowed
}
// ================= DIFF =================
var changes []map[string]any
for _, p := range list {
key := p.Module + ":" + p.Action
oldVal, ok := oldMap[key]
if !ok {
oldVal = false
}
if oldVal != p.Allowed {
changes = append(changes, map[string]any{
"module": p.Module,
"action": p.Action,
"before": oldVal,
"after": p.Allowed,
})
}
}
// ================= SAVE =================
tx, err := db.Begin()
if err != nil {
http.Error(w, "tx error", 500)
return
}
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 {
http.Error(w, "prepare error", 500)
return
}
defer stmt.Close()
for _, p := range list {
if _, err := stmt.Exec(
roleID,
p.Module,
p.Action,
p.Allowed,
); err != nil {
http.Error(w, "exec error", 500)
return
}
}
if err := tx.Commit(); err != nil {
http.Error(w, "commit error", 500)
return
}
// ================= AUDIT =================
if len(changes) > 0 {
var roleCode string
_ = db.QueryRow(`
SELECT code FROM dfrole WHERE id=$1
`, roleID).Scan(&roleCode)
auditlog.Enqueue(r.Context(), auditlog.ActivityLog{
ActionType: "role_permission_change",
ActionCategory: "role_permission",
ActionTarget: fmt.Sprintf("/api/roles/%d/permissions", roleID),
Description: "role permission matrix updated",
Username: claims.Username,
RoleCode: claims.RoleCode,
DfUsrID: int64(claims.ID),
ChangeBefore: map[string]any{
"permissions": oldRows,
},
ChangeAfter: map[string]any{
"changes": changes,
},
IsSuccess: true,
})
}
json.NewEncoder(w).Encode(map[string]bool{
"success": true,
})
}
}