yao/openapi/oauth/authorized/utils.go
Max e96896eb40 Implement detailed ACL enforcement logic and data access constraints
- Refactored the ACL enforcement process to include a comprehensive chain of permission checks for clients, users, teams, and members.
- Introduced data access constraints (OwnerOnly, TeamOnly) to enhance access control based on endpoint requirements.
- Updated the Error struct to include the stage of permission checks where failures occur, improving error reporting.
- Enhanced scope management with wildcard matching capabilities and added checks for restricted scopes.
- Implemented tests to validate the enforcement logic and ensure correct handling of data access constraints.
2025-10-21 17:04:20 +08:00

126 lines
3.4 KiB
Go

package authorized
import (
"github.com/gin-gonic/gin"
"github.com/yaoapp/yao/openapi/oauth/types"
)
// GetInfo extracts authorized information from the gin context
// This function reads authorization data that was set by the OAuth guard middleware
func GetInfo(c *gin.Context) *types.AuthorizedInfo {
info := &types.AuthorizedInfo{}
if subject, ok := c.Get("__subject"); ok {
info.Subject = subject.(string)
}
if clientID, ok := c.Get("__client_id"); ok {
info.ClientID = clientID.(string)
}
if userID, ok := c.Get("__user_id"); ok {
info.UserID = userID.(string)
}
if scope, ok := c.Get("__scope"); ok {
info.Scope = scope.(string)
}
if teamID, ok := c.Get("__team_id"); ok {
info.TeamID = teamID.(string)
}
if tenantID, ok := c.Get("__tenant_id"); ok {
info.TenantID = tenantID.(string)
}
if sessionID, ok := c.Get("__sid"); ok {
info.SessionID = sessionID.(string)
}
if rememberMe, ok := c.Get("__remember_me"); ok {
if rmBool, ok := rememberMe.(bool); ok {
info.RememberMe = rmBool
}
}
// Get data access constraints (set by ACL enforcement)
info.Constraints = GetConstraints(c)
return info
}
// GetConstraints extracts data access constraints from the gin context
// Returns a DataConstraints struct with all constraint flags
func GetConstraints(c *gin.Context) types.DataConstraints {
constraints := types.DataConstraints{}
if ownerOnly, ok := c.Get("__owner_only"); ok {
if ownerOnlyBool, ok := ownerOnly.(bool); ok {
constraints.OwnerOnly = ownerOnlyBool
}
}
if teamOnly, ok := c.Get("__team_only"); ok {
if teamOnlyBool, ok := teamOnly.(bool); ok {
constraints.TeamOnly = teamOnlyBool
}
}
// Future constraints can be read here:
// if departmentOnly, ok := c.Get("__department_only"); ok {
// if deptBool, ok := departmentOnly.(bool); ok {
// constraints.DepartmentOnly = deptBool
// }
// }
return constraints
}
// UpdateConstraints updates data access constraints in the gin context
// This should be called by ACL enforcement after successful permission check
// Accepts a map of constraints for flexible extension
func UpdateConstraints(c *gin.Context, constraints map[string]interface{}) {
// Set each constraint in the context
for key, value := range constraints {
c.Set("__"+key, value)
}
}
// SetInfo sets authorized information in the gin context
// This function should be called by the OAuth guard middleware after token validation
// userIDGetter is a function that resolves the user_id from clientID and subject
func SetInfo(c *gin.Context, claims *types.TokenClaims, sessionID string, userIDGetter func(clientID, subject string) (string, error)) {
// Set session ID in context
if sessionID != "" {
c.Set("__sid", sessionID)
}
// Set user_id in context (resolve from claims)
if userIDGetter != nil {
userID, err := userIDGetter(claims.ClientID, claims.Subject)
if err == nil && userID != "" {
c.Set("__user_id", userID)
}
}
// Set subject, scope, client_id in context
c.Set("__subject", claims.Subject)
c.Set("__scope", claims.Scope)
c.Set("__client_id", claims.ClientID)
// Set team_id and tenant_id in context if available
if claims.TeamID != "" {
c.Set("__team_id", claims.TeamID)
}
if claims.TenantID != "" {
c.Set("__tenant_id", claims.TenantID)
}
// Set custom claims from Extra field into context
if claims.Extra != nil {
for key, value := range claims.Extra {
c.Set("__"+key, value)
}
}
}