Files
Charon/backend/internal/api/handlers/permission_helpers.go
GitHub Actions 9ef8a1ce21 fix: add system permissions handler for diagnostics and repair
- Implemented SystemPermissionsHandler to check and repair file permissions.
- Added endpoints for retrieving and repairing permissions.
- Introduced utility functions for permission checks and error mapping.
- Created tests for the new handler and utility functions.
- Updated routes to include the new permissions endpoints.
- Enhanced configuration to support new logging and plugin directories.
2026-02-11 05:33:19 +00:00

111 lines
2.5 KiB
Go

package handlers
import (
"encoding/json"
"fmt"
"net/http"
"os"
"github.com/gin-gonic/gin"
"github.com/Wikid82/charon/backend/internal/models"
"github.com/Wikid82/charon/backend/internal/services"
"github.com/Wikid82/charon/backend/internal/util"
)
func requireAdmin(c *gin.Context) bool {
if isAdmin(c) {
return true
}
c.JSON(http.StatusForbidden, gin.H{
"error": "admin privileges required",
"error_code": "permissions_admin_only",
})
return false
}
func isAdmin(c *gin.Context) bool {
role, _ := c.Get("role")
roleStr, _ := role.(string)
return roleStr == "admin"
}
func respondPermissionError(c *gin.Context, securityService *services.SecurityService, action string, err error, path string) bool {
code, ok := util.MapSaveErrorCode(err)
if !ok {
return false
}
admin := isAdmin(c)
response := gin.H{
"error": permissionErrorMessage(code),
"error_code": code,
}
if admin {
if path != "" {
response["path"] = path
}
response["help"] = buildPermissionHelp(path)
} else {
response["help"] = "Check volume permissions or contact an administrator."
}
logPermissionAudit(securityService, c, action, code, path, admin)
c.JSON(http.StatusInternalServerError, response)
return true
}
func permissionErrorMessage(code string) string {
switch code {
case "permissions_db_readonly":
return "database is read-only"
case "permissions_db_locked":
return "database is locked"
case "permissions_readonly":
return "filesystem is read-only"
case "permissions_write_denied":
return "permission denied"
default:
return "permission error"
}
}
func buildPermissionHelp(path string) string {
uid := os.Geteuid()
gid := os.Getegid()
if path == "" {
return fmt.Sprintf("chown -R %d:%d <path-to-volume>", uid, gid)
}
return fmt.Sprintf("chown -R %d:%d %s", uid, gid, path)
}
func logPermissionAudit(securityService *services.SecurityService, c *gin.Context, action, code, path string, admin bool) {
if securityService == nil {
return
}
details := map[string]any{
"error_code": code,
"admin": admin,
}
if admin && path != "" {
details["path"] = path
}
detailsJSON, _ := json.Marshal(details)
actor := "unknown"
if userID, ok := c.Get("userID"); ok {
actor = fmt.Sprintf("%v", userID)
}
_ = securityService.LogAudit(&models.SecurityAudit{
Actor: actor,
Action: action,
EventCategory: "permissions",
Details: string(detailsJSON),
IPAddress: c.ClientIP(),
UserAgent: c.Request.UserAgent(),
})
}