package middleware import ( "encoding/json" "net/http" "github.com/casbin/casbin/v2" pkgcasbin "muyu-apiserver/pkg/casbin" "muyu-apiserver/pkg/ctxdata" ) type AuthorityMiddleware struct { enforcer *casbin.Enforcer } func NewAuthorityMiddleware(enforcer *casbin.Enforcer) *AuthorityMiddleware { return &AuthorityMiddleware{enforcer: enforcer} } func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // JWT claims are injected by go-zero middleware into context. // Keep a hard guard here to prevent tenantless requests from passing through. userId := ctxdata.GetUserId(r.Context()) roleKey := ctxdata.GetRoleKey(r.Context()) tenantId := ctxdata.GetTenantId(r.Context()) if userId == "" || roleKey == "" || tenantId == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) _ = json.NewEncoder(w).Encode(map[string]interface{}{ "code": 1004, "msg": "invalid auth claims: tenant scope required", }) return } // Tenant scope cannot be overridden by caller. if hdrTenant := r.Header.Get("X-Tenant-Id"); hdrTenant != "" && hdrTenant != tenantId { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusForbidden) _ = json.NewEncoder(w).Encode(map[string]interface{}{ "code": 1006, "msg": "tenant scope mismatch", }) return } r.Header.Set("X-Tenant-Id", tenantId) // Keep permissions closed by default once enforcer is enabled. if m.enforcer != nil && !pkgcasbin.CheckPermission(m.enforcer, roleKey, r.URL.Path, r.Method) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusForbidden) _ = json.NewEncoder(w).Encode(map[string]interface{}{ "code": 1006, "msg": "access denied", }) return } next(w, r) } }