- 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.
111 lines
2.5 KiB
Go
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(),
|
|
})
|
|
}
|