package httpapi import ( "net/http" "strings" ) // Portal permission strings. Format: :
: (bgp:modules:read). // Superset order: admin ⊃ write ⊃ read for the same :
. const ( permLevelRead = "read" permLevelWrite = "write" permLevelAdmin = "admin" ) // permLevelRank returns 0 for unknown, 1 for read, 2 for write, 3 for admin. func permLevelRank(level string) int { switch strings.ToLower(strings.TrimSpace(level)) { case permLevelRead: return 1 case permLevelWrite: return 2 case permLevelAdmin: return 3 default: return 0 } } // splitPerm splits a permission string into (app, section, level). func splitPerm(perm string) (app, section, level string, ok bool) { parts := strings.Split(strings.TrimSpace(perm), ":") if len(parts) != 3 { return "", "", "", false } return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), strings.TrimSpace(parts[2]), true } // HasPermission reports whether the granted list satisfies required, applying the // admin ⊃ write ⊃ read superset within the same app+section. func HasPermission(granted []string, required string) bool { rApp, rSection, rLevel, ok := splitPerm(required) if !ok { return false } needRank := permLevelRank(rLevel) if needRank == 0 { return false } for _, g := range granted { gApp, gSection, gLevel, ok := splitPerm(g) if !ok { continue } if !strings.EqualFold(gApp, rApp) || !strings.EqualFold(gSection, rSection) { continue } if permLevelRank(gLevel) >= needRank { return true } } return false } // permAPIKeyRoleFor maps a permission level to the API-key role required. func permAPIKeyRoleFor(perm string) string { _, _, level, ok := splitPerm(perm) if !ok { return "operator" } switch strings.ToLower(level) { case permLevelRead: return "viewer" case permLevelWrite: return "editor" case permLevelAdmin: return "operator" default: return "operator" } } // requirePerm enforces a permission for a portal JWT or falls back to the API-key role ladder. // node/firewall roles are always rejected (they use requireNode / requireFirewall). func (s *Server) requirePerm(w http.ResponseWriter, a Auth, perm string) bool { switch strings.ToLower(a.Role) { case "node": writeProblem(w, http.StatusForbidden, "Forbidden", "node role cannot access this resource") return false case "firewall": writeProblem(w, http.StatusForbidden, "Forbidden", "firewall role cannot access this resource") return false } if a.Kind == AuthKindJWT || len(a.Permissions) > 0 { if a.IsAdmin || HasPermission(a.Permissions, perm) { return true } writeProblem(w, http.StatusForbidden, "Forbidden", "missing permission: "+perm) return false } need := permAPIKeyRoleFor(perm) if roleLevel(a.Role) < roleLevel(need) { writeProblem(w, http.StatusForbidden, "Forbidden", "insufficient role") return false } return true }