Files
zeekay 4d2f28a2f7 security: harden compliance router — full RBAC, library delegation, audit logging
- Add RBAC guards to ALL read endpoints (pipelines, sessions, funds,
  esign, templates, dashboard, users, transactions, reports, settings,
  credentials, billing, envelope views) — previously 20+ GET routes
  had no permission checks
- Wire Jube client into amlHandler via RouterOption pattern
- Replace NewRouter variadic authStore param with proper RouterOption API
- Delegate seed roles to rbac.DefaultRoles() — fixes drift where seed
  was missing aml and applications modules
- Delegate modules list to rbac.ComplianceModules() — single source of truth
- Delegate onboarding helpers (newApplicationSteps, isTerminalStatus,
  markStepCompleted, markStepFailed) to compliance library
- SSN HMAC key: hard-fail in production if SSN_HMAC_KEY not set
- Add audit logging when super_admin RBAC bypass is used
- Credential management elevated to roles:admin (was unguarded)
2025-12-24 17:43:04 -07:00

93 lines
2.6 KiB
Go

package compliance
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/luxfi/compliance/pkg/rbac"
)
// rolesHandler holds RBAC HTTP handler state.
type rolesHandler struct {
store ComplianceStore
}
func (h *rolesHandler) handleListRoles(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, h.store.ListRoles())
}
func (h *rolesHandler) handleCreateRole(w http.ResponseWriter, r *http.Request) {
var role Role
if err := json.NewDecoder(r.Body).Decode(&role); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if role.Name == "" {
writeError(w, http.StatusBadRequest, "name is required")
return
}
if err := h.store.SaveRole(&role); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusCreated, &role)
}
func (h *rolesHandler) handleGetRole(w http.ResponseWriter, r *http.Request) {
role, err := h.store.GetRole(chi.URLParam(r, "id"))
if err != nil {
writeError(w, http.StatusNotFound, err.Error())
return
}
writeJSON(w, http.StatusOK, role)
}
func (h *rolesHandler) handleUpdateRole(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
existing, err := h.store.GetRole(id)
if err != nil {
writeError(w, http.StatusNotFound, err.Error())
return
}
var patch struct {
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
Permissions []Permission `json:"permissions,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&patch); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if patch.Name != nil {
existing.Name = *patch.Name
}
if patch.Description != nil {
existing.Description = *patch.Description
}
if patch.Permissions != nil {
existing.Permissions = patch.Permissions
}
if err := h.store.SaveRole(existing); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, existing)
}
func (h *rolesHandler) handleDeleteRole(w http.ResponseWriter, r *http.Request) {
if err := h.store.DeleteRole(chi.URLParam(r, "id")); err != nil {
writeError(w, http.StatusNotFound, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
// handleListModules returns the static list of compliance modules and their
// available actions, used to build a permission matrix UI.
// Uses the canonical definition from the compliance library to prevent drift.
func (h *rolesHandler) handleListModules(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, rbac.ComplianceModules())
}