Files
lijiaoqiao/supply-api/internal/iam/middleware/scope_auth.go

339 lines
8.6 KiB
Go
Raw Normal View History

package middleware
import (
"context"
"encoding/json"
"net/http"
"lijiaoqiao/supply-api/internal/middleware"
)
// IAM token claims context key
type iamContextKey string
const (
// IAMTokenClaimsKey 用于在context中存储token claims
IAMTokenClaimsKey iamContextKey = "iam_token_claims"
)
// IAMTokenClaims IAM扩展Token Claims
type IAMTokenClaims struct {
SubjectID string `json:"subject_id"`
Role string `json:"role"`
Scope []string `json:"scope"`
TenantID int64 `json:"tenant_id"`
UserType string `json:"user_type"` // 用户类型: platform/supply/consumer
Permissions []string `json:"permissions"` // 细粒度权限列表
}
// 角色层级定义
var roleHierarchyLevels = map[string]int{
"super_admin": 100,
"org_admin": 50,
"supply_admin": 40,
"consumer_admin": 40,
"operator": 30,
"developer": 20,
"finops": 20,
"supply_operator": 30,
"supply_finops": 20,
"supply_viewer": 10,
"consumer_operator": 30,
"consumer_viewer": 10,
"viewer": 10,
}
// ScopeAuthMiddleware Scope权限验证中间件
type ScopeAuthMiddleware struct {
// 路由-Scope映射
routeScopePolicies map[string][]string
// 角色层级已废弃使用包级变量roleHierarchyLevels
roleHierarchy map[string]int
}
// NewScopeAuthMiddleware 创建Scope权限验证中间件
func NewScopeAuthMiddleware() *ScopeAuthMiddleware {
return &ScopeAuthMiddleware{
routeScopePolicies: make(map[string][]string),
roleHierarchy: roleHierarchyLevels,
}
}
// SetRouteScopePolicy 设置路由的Scope要求
func (m *ScopeAuthMiddleware) SetRouteScopePolicy(route string, scopes []string) {
m.routeScopePolicies[route] = scopes
}
// CheckScope 检查是否拥有指定Scope
func CheckScope(ctx context.Context, requiredScope string) bool {
claims := getIAMTokenClaims(ctx)
if claims == nil {
return false
}
// 空scope应该拒绝访问
if requiredScope == "" {
return false
}
return hasScope(claims.Scope, requiredScope)
}
// CheckAllScopes 检查是否拥有所有指定Scope
func CheckAllScopes(ctx context.Context, requiredScopes []string) bool {
claims := getIAMTokenClaims(ctx)
if claims == nil {
return false
}
// 空列表直接通过
if len(requiredScopes) == 0 {
return true
}
for _, scope := range requiredScopes {
if !hasScope(claims.Scope, scope) {
return false
}
}
return true
}
// CheckAnyScope 检查是否拥有任一指定Scope
func CheckAnyScope(ctx context.Context, requiredScopes []string) bool {
claims := getIAMTokenClaims(ctx)
if claims == nil {
return false
}
// 空列表直接通过
if len(requiredScopes) == 0 {
return true
}
for _, scope := range requiredScopes {
if hasScope(claims.Scope, scope) {
return true
}
}
return false
}
// HasRole 检查是否拥有指定角色
func HasRole(ctx context.Context, requiredRole string) bool {
claims := getIAMTokenClaims(ctx)
if claims == nil {
return false
}
return claims.Role == requiredRole
}
// HasRoleLevel 检查角色层级是否满足要求
func HasRoleLevel(ctx context.Context, minLevel int) bool {
claims := getIAMTokenClaims(ctx)
if claims == nil {
return false
}
level := GetRoleLevel(claims.Role)
return level >= minLevel
}
// GetRoleLevel 获取角色层级数值
func GetRoleLevel(role string) int {
if level, ok := roleHierarchyLevels[role]; ok {
return level
}
return 0
}
// GetIAMTokenClaims 获取IAM Token Claims
func GetIAMTokenClaims(ctx context.Context) *IAMTokenClaims {
if claims, ok := ctx.Value(IAMTokenClaimsKey).(*IAMTokenClaims); ok {
return claims
}
return nil
}
// getIAMTokenClaims 内部获取IAM Token Claims
func getIAMTokenClaims(ctx context.Context) *IAMTokenClaims {
if claims, ok := ctx.Value(IAMTokenClaimsKey).(*IAMTokenClaims); ok {
return claims
}
return nil
}
// hasScope 检查scope列表是否包含目标scope
func hasScope(scopes []string, target string) bool {
for _, scope := range scopes {
if scope == target || scope == "*" {
return true
}
}
return false
}
// RequireScope 返回一个要求特定Scope的中间件
func (m *ScopeAuthMiddleware) RequireScope(requiredScope string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := getIAMTokenClaims(r.Context())
if claims == nil {
writeAuthError(w, http.StatusUnauthorized, "AUTH_CONTEXT_MISSING",
"authentication context is missing")
return
}
// 检查scope
if requiredScope != "" && !hasScope(claims.Scope, requiredScope) {
writeAuthError(w, http.StatusForbidden, "AUTH_SCOPE_DENIED",
"required scope is not granted")
return
}
next.ServeHTTP(w, r)
})
}
}
// RequireAllScopes 返回一个要求所有指定Scope的中间件
func (m *ScopeAuthMiddleware) RequireAllScopes(requiredScopes []string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := getIAMTokenClaims(r.Context())
if claims == nil {
writeAuthError(w, http.StatusUnauthorized, "AUTH_CONTEXT_MISSING",
"authentication context is missing")
return
}
for _, scope := range requiredScopes {
if !hasScope(claims.Scope, scope) {
writeAuthError(w, http.StatusForbidden, "AUTH_SCOPE_DENIED",
"required scope is not granted")
return
}
}
next.ServeHTTP(w, r)
})
}
}
// RequireAnyScope 返回一个要求任一指定Scope的中间件
func (m *ScopeAuthMiddleware) RequireAnyScope(requiredScopes []string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := getIAMTokenClaims(r.Context())
if claims == nil {
writeAuthError(w, http.StatusUnauthorized, "AUTH_CONTEXT_MISSING",
"authentication context is missing")
return
}
// 空列表应该拒绝访问
if len(requiredScopes) == 0 || !hasAnyScope(claims.Scope, requiredScopes) {
writeAuthError(w, http.StatusForbidden, "AUTH_SCOPE_DENIED",
"none of the required scopes are granted")
return
}
next.ServeHTTP(w, r)
})
}
}
// RequireRole 返回一个要求特定角色的中间件
func (m *ScopeAuthMiddleware) RequireRole(requiredRole string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := getIAMTokenClaims(r.Context())
if claims == nil {
writeAuthError(w, http.StatusUnauthorized, "AUTH_CONTEXT_MISSING",
"authentication context is missing")
return
}
if claims.Role != requiredRole {
writeAuthError(w, http.StatusForbidden, "AUTH_ROLE_DENIED",
"required role is not granted")
return
}
next.ServeHTTP(w, r)
})
}
}
// RequireMinLevel 返回一个要求最小角色层级的中间件
func (m *ScopeAuthMiddleware) RequireMinLevel(minLevel int) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := getIAMTokenClaims(r.Context())
if claims == nil {
writeAuthError(w, http.StatusUnauthorized, "AUTH_CONTEXT_MISSING",
"authentication context is missing")
return
}
level := GetRoleLevel(claims.Role)
if level < minLevel {
writeAuthError(w, http.StatusForbidden, "AUTH_ROLE_LEVEL_DENIED",
"insufficient role level")
return
}
next.ServeHTTP(w, r)
})
}
}
// hasAnyScope 检查scope列表是否包含任一目标scope
func hasAnyScope(scopes, targets []string) bool {
for _, scope := range scopes {
for _, target := range targets {
if scope == target || scope == "*" {
return true
}
}
}
return false
}
// writeAuthError 写入鉴权错误
func writeAuthError(w http.ResponseWriter, status int, code, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
resp := map[string]interface{}{
"error": map[string]string{
"code": code,
"message": message,
},
}
json.NewEncoder(w).Encode(resp)
}
// WithIAMClaims 设置IAM Claims到Context
func WithIAMClaims(ctx context.Context, claims *IAMTokenClaims) context.Context {
return context.WithValue(ctx, IAMTokenClaimsKey, claims)
}
// GetClaimsFromLegacy 从原有middleware.TokenClaims转换为IAMTokenClaims
func GetClaimsFromLegacy(legacy *middleware.TokenClaims) *IAMTokenClaims {
if legacy == nil {
return nil
}
return &IAMTokenClaims{
SubjectID: legacy.SubjectID,
Role: legacy.Role,
Scope: legacy.Scope,
TenantID: legacy.TenantID,
}
}