Files
inp2p/internal/server/authz.go
T

130 lines
3.1 KiB
Go

package server
import (
"net/http"
"strconv"
"strings"
)
type AccessContext struct {
Kind string
TenantID int64
UserID int64
Role string
Token string
}
func (s *Server) ResolveAccess(r *http.Request, masterToken uint64) (*AccessContext, bool) {
tok := BearerToken(r)
if tok == "" {
return nil, false
}
if tok == strconv.FormatUint(masterToken, 10) {
return &AccessContext{Kind: "master", Role: "admin", Token: tok}, true
}
return s.ResolveTenantAccessToken(tok)
}
func GetAccessContext(r *http.Request) *AccessContext {
v := r.Context().Value(ServerCtxKeyAccess{})
if v == nil {
return nil
}
if ac, ok := v.(*AccessContext); ok {
return ac
}
return nil
}
func (s *Server) ResolveTenantAccessToken(tok string) (*AccessContext, bool) {
if tok == "" || s.store == nil {
return nil, false
}
if ss, err := s.store.VerifySessionToken(tok); err == nil && ss != nil {
return &AccessContext{
Kind: "session",
TenantID: ss.TenantID,
UserID: ss.UserID,
Role: ss.Role,
Token: tok,
}, true
}
if ten, err := s.store.VerifyAPIKey(tok); err == nil && ten != nil {
return &AccessContext{
Kind: "apikey",
TenantID: ten.ID,
Role: "apikey",
Token: tok,
}, true
}
return nil, false
}
// TenantCan implements action-level RBAC for tenant-scoped APIs.
func TenantCan(ac *AccessContext, action string) bool {
if ac == nil {
return false
}
switch action {
case "view":
return (ac.Kind == "session" && (ac.Role == "admin" || ac.Role == "operator")) || ac.Kind == "apikey"
case "mutate_node_meta", "mutate_sdwan", "mutate_connect", "mutate_node_control":
return ac.Kind == "session" && (ac.Role == "admin" || ac.Role == "operator")
case "enroll_manage":
return ac.Kind == "session" && ac.Role == "admin"
default:
return false
}
}
func settingOn(v string) bool {
v = strings.TrimSpace(strings.ToLower(v))
return v == "1" || v == "true" || v == "on" || v == "yes"
}
// TenantCanWithOverride evaluates baseline RBAC then optional advanced override switches.
// Return values: allowed, usedOverride
func (s *Server) TenantCanWithOverride(ac *AccessContext, action string) (bool, bool) {
if TenantCan(ac, action) {
return true, false
}
if ac == nil || s == nil || s.store == nil || ac.TenantID <= 0 {
return false, false
}
if ac.Kind == "master" {
return false, false
}
// global override gate
if v, _, err := s.store.GetSetting("advanced_override_enabled"); err != nil || !settingOn(v) {
return false, false
}
switch action {
case "mutate_sdwan":
if ac.Kind == "apikey" {
if v, _, err := s.store.GetSetting("allow_apikey_mutate_sdwan"); err == nil && settingOn(v) {
return true, true
}
}
case "mutate_connect":
if ac.Kind == "session" && ac.Role == "operator" {
if v, _, err := s.store.GetSetting("allow_connect_cross_tenant"); err == nil && settingOn(v) {
return true, true
}
}
case "mutate_node_control":
if ac.Kind == "session" && ac.Role == "operator" {
if v, _, err := s.store.GetSetting("allow_operator_kick_cross_node"); err == nil && settingOn(v) {
return true, true
}
}
}
return false, false
}