mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-06 14:26:22 +00:00
Optional Authentication
This commit is contained in:
parent
78029fb34f
commit
08050c960d
@ -23,6 +23,15 @@ func Init(dev bool) {
|
||||
|
||||
}
|
||||
|
||||
func UpdateLoggerPath(path string, dev bool) {
|
||||
defaultConfig := zap.NewProductionConfig()
|
||||
if dev {
|
||||
defaultConfig = zap.NewDevelopmentConfig()
|
||||
}
|
||||
defaultConfig.OutputPaths = []string{path}
|
||||
UpdateLogger(&defaultConfig)
|
||||
}
|
||||
|
||||
func UpdateLogger(config *zap.Config) {
|
||||
defaultConfig := zap.NewProductionConfig()
|
||||
defaultConfig.OutputPaths = []string{"resolvespec.log"}
|
||||
|
||||
@ -91,7 +91,8 @@ security.UserContext{
|
||||
RemoteID: "remote_xyz", // Remote system ID
|
||||
Roles: []string{"admin"}, // User roles
|
||||
Email: "john@example.com", // User email
|
||||
Claims: map[string]any{}, // Additional metadata
|
||||
Claims: map[string]any{}, // Additional authentication claims
|
||||
Meta: map[string]any{}, // Additional metadata (JSON-serializable)
|
||||
}
|
||||
```
|
||||
|
||||
@ -621,6 +622,67 @@ func main() {
|
||||
|
||||
---
|
||||
|
||||
## Authentication Modes
|
||||
|
||||
```go
|
||||
// Required authentication (default)
|
||||
// Authentication must succeed or returns 401
|
||||
router.Use(security.NewAuthMiddleware(securityList))
|
||||
|
||||
// Skip authentication for specific routes
|
||||
// Always sets guest user context
|
||||
func PublicRoute(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := security.SkipAuth(r.Context())
|
||||
r = r.WithContext(ctx)
|
||||
// Guest context will be set
|
||||
}
|
||||
|
||||
// Optional authentication for specific routes
|
||||
// Tries to authenticate, falls back to guest if it fails
|
||||
func HomeRoute(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := security.OptionalAuth(r.Context())
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
userCtx, _ := security.GetUserContext(r.Context())
|
||||
if userCtx.UserID == 0 {
|
||||
// Guest user
|
||||
} else {
|
||||
// Authenticated user
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Comparison:**
|
||||
- **Required**: Auth must succeed or return 401 (default)
|
||||
- **SkipAuth**: Never tries to authenticate, always guest
|
||||
- **OptionalAuth**: Tries to authenticate, guest on failure
|
||||
|
||||
---
|
||||
|
||||
## Standalone Handlers
|
||||
|
||||
```go
|
||||
// NewAuthHandler - Required authentication (returns 401 on failure)
|
||||
authHandler := security.NewAuthHandler(securityList, myHandler)
|
||||
http.Handle("/api/protected", authHandler)
|
||||
|
||||
// NewOptionalAuthHandler - Optional authentication (guest on failure)
|
||||
optionalHandler := security.NewOptionalAuthHandler(securityList, myHandler)
|
||||
http.Handle("/home", optionalHandler)
|
||||
|
||||
// Example handler
|
||||
func myHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userCtx, _ := security.GetUserContext(r.Context())
|
||||
if userCtx.UserID == 0 {
|
||||
// Guest user
|
||||
} else {
|
||||
// Authenticated user
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Context Helpers
|
||||
|
||||
```go
|
||||
@ -635,6 +697,7 @@ sessionID, ok := security.GetSessionID(ctx)
|
||||
remoteID, ok := security.GetRemoteID(ctx)
|
||||
roles, ok := security.GetUserRoles(ctx)
|
||||
email, ok := security.GetUserEmail(ctx)
|
||||
meta, ok := security.GetUserMeta(ctx)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@ -118,14 +118,15 @@ Enhanced user context with complete user information:
|
||||
|
||||
```go
|
||||
type UserContext struct {
|
||||
UserID int // User's unique ID
|
||||
UserName string // Username
|
||||
UserLevel int // User privilege level
|
||||
SessionID string // Current session ID
|
||||
RemoteID string // Remote system ID
|
||||
Roles []string // User roles
|
||||
Email string // User email
|
||||
Claims map[string]any // Additional metadata
|
||||
UserID int // User's unique ID
|
||||
UserName string // Username
|
||||
UserLevel int // User privilege level
|
||||
SessionID string // Current session ID
|
||||
RemoteID string // Remote system ID
|
||||
Roles []string // User roles
|
||||
Email string // User email
|
||||
Claims map[string]any // Additional authentication claims
|
||||
Meta map[string]any // Additional metadata (can hold any JSON-serializable values)
|
||||
}
|
||||
```
|
||||
|
||||
@ -629,6 +630,142 @@ func (p *MyProvider) GetRowSecurity(ctx context.Context, userID int, schema, tab
|
||||
}
|
||||
```
|
||||
|
||||
## Middleware and Handler API
|
||||
|
||||
### NewAuthMiddleware
|
||||
Standard middleware that authenticates all requests:
|
||||
|
||||
```go
|
||||
router.Use(security.NewAuthMiddleware(securityList))
|
||||
```
|
||||
|
||||
Routes can skip authentication using the `SkipAuth` helper:
|
||||
|
||||
```go
|
||||
func PublicHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := security.SkipAuth(r.Context())
|
||||
// This route will bypass authentication
|
||||
// A guest user context will be set instead
|
||||
}
|
||||
|
||||
router.Handle("/public", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := security.SkipAuth(r.Context())
|
||||
PublicHandler(w, r.WithContext(ctx))
|
||||
}))
|
||||
```
|
||||
|
||||
When authentication is skipped, a guest user context is automatically set:
|
||||
- UserID: 0
|
||||
- UserName: "guest"
|
||||
- Roles: ["guest"]
|
||||
- RemoteID: Request's remote address
|
||||
|
||||
Routes can use optional authentication with the `OptionalAuth` helper:
|
||||
|
||||
```go
|
||||
func OptionalAuthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := security.OptionalAuth(r.Context())
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
// This route will try to authenticate
|
||||
// If authentication succeeds, authenticated user context is set
|
||||
// If authentication fails, guest user context is set instead
|
||||
|
||||
userCtx, _ := security.GetUserContext(r.Context())
|
||||
if userCtx.UserID == 0 {
|
||||
// Guest user
|
||||
fmt.Fprintf(w, "Welcome, guest!")
|
||||
} else {
|
||||
// Authenticated user
|
||||
fmt.Fprintf(w, "Welcome back, %s!", userCtx.UserName)
|
||||
}
|
||||
}
|
||||
|
||||
router.Handle("/home", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := security.OptionalAuth(r.Context())
|
||||
OptionalAuthHandler(w, r.WithContext(ctx))
|
||||
}))
|
||||
```
|
||||
|
||||
**Authentication Modes Summary:**
|
||||
- **Required (default)**: Authentication must succeed or returns 401
|
||||
- **SkipAuth**: Bypasses authentication entirely, always sets guest context
|
||||
- **OptionalAuth**: Tries authentication, falls back to guest context if it fails
|
||||
|
||||
### NewAuthHandler
|
||||
|
||||
Standalone authentication handler (without middleware wrapping):
|
||||
|
||||
```go
|
||||
// Use when you need authentication logic without middleware
|
||||
authHandler := security.NewAuthHandler(securityList, myHandler)
|
||||
http.Handle("/api/protected", authHandler)
|
||||
```
|
||||
|
||||
### NewOptionalAuthHandler
|
||||
|
||||
Standalone optional authentication handler that tries to authenticate but falls back to guest:
|
||||
|
||||
```go
|
||||
// Use for routes that should work for both authenticated and guest users
|
||||
optionalHandler := security.NewOptionalAuthHandler(securityList, myHandler)
|
||||
http.Handle("/home", optionalHandler)
|
||||
|
||||
// Example handler that checks user context
|
||||
func myHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userCtx, _ := security.GetUserContext(r.Context())
|
||||
if userCtx.UserID == 0 {
|
||||
fmt.Fprintf(w, "Welcome, guest!")
|
||||
} else {
|
||||
fmt.Fprintf(w, "Welcome back, %s!", userCtx.UserName)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Helper Functions
|
||||
|
||||
Extract user information from context:
|
||||
|
||||
```go
|
||||
// Get full user context
|
||||
userCtx, ok := security.GetUserContext(ctx)
|
||||
|
||||
// Get specific fields
|
||||
userID, ok := security.GetUserID(ctx)
|
||||
userName, ok := security.GetUserName(ctx)
|
||||
userLevel, ok := security.GetUserLevel(ctx)
|
||||
sessionID, ok := security.GetSessionID(ctx)
|
||||
remoteID, ok := security.GetRemoteID(ctx)
|
||||
roles, ok := security.GetUserRoles(ctx)
|
||||
email, ok := security.GetUserEmail(ctx)
|
||||
meta, ok := security.GetUserMeta(ctx)
|
||||
```
|
||||
|
||||
### Metadata Support
|
||||
|
||||
The `Meta` field in `UserContext` can hold any JSON-serializable values:
|
||||
|
||||
```go
|
||||
// Set metadata during login
|
||||
loginReq := security.LoginRequest{
|
||||
Username: "user@example.com",
|
||||
Password: "password",
|
||||
Meta: map[string]any{
|
||||
"department": "engineering",
|
||||
"location": "US",
|
||||
"preferences": map[string]any{
|
||||
"theme": "dark",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Access metadata in handlers
|
||||
meta, ok := security.GetUserMeta(ctx)
|
||||
if ok {
|
||||
department := meta["department"].(string)
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Part of the ResolveSpec project.
|
||||
|
||||
@ -54,6 +54,8 @@ func (a *HeaderAuthenticatorExample) Authenticate(r *http.Request) (*UserContext
|
||||
RemoteID: r.Header.Get("X-Remote-ID"),
|
||||
Email: r.Header.Get("X-User-Email"),
|
||||
Roles: parseRoles(r.Header.Get("X-User-Roles")),
|
||||
Claims: make(map[string]any),
|
||||
Meta: make(map[string]any),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -125,6 +127,8 @@ func (a *JWTAuthenticatorExample) Login(ctx context.Context, req LoginRequest) (
|
||||
Email: user.Email,
|
||||
UserLevel: user.UserLevel,
|
||||
Roles: parseRoles(user.Roles),
|
||||
Claims: req.Claims,
|
||||
Meta: req.Meta,
|
||||
},
|
||||
ExpiresIn: int64(24 * time.Hour.Seconds()),
|
||||
}, nil
|
||||
@ -242,6 +246,9 @@ func (a *DatabaseAuthenticatorExample) Login(ctx context.Context, req LoginReque
|
||||
Email: user.Email,
|
||||
UserLevel: user.UserLevel,
|
||||
Roles: parseRoles(user.Roles),
|
||||
SessionID: sessionToken,
|
||||
Claims: req.Claims,
|
||||
Meta: req.Meta,
|
||||
},
|
||||
ExpiresIn: int64(24 * time.Hour.Seconds()),
|
||||
}, nil
|
||||
@ -320,6 +327,8 @@ func (a *DatabaseAuthenticatorExample) Authenticate(r *http.Request) (*UserConte
|
||||
UserLevel: session.UserLevel,
|
||||
SessionID: sessionToken,
|
||||
Roles: parseRoles(session.Roles),
|
||||
Claims: make(map[string]any),
|
||||
Meta: make(map[string]any),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -370,9 +379,12 @@ func (a *DatabaseAuthenticatorExample) RefreshToken(ctx context.Context, refresh
|
||||
return &LoginResponse{
|
||||
Token: newSessionToken,
|
||||
User: &UserContext{
|
||||
UserID: session.UserID,
|
||||
UserName: session.Username,
|
||||
Email: session.Email,
|
||||
UserID: session.UserID,
|
||||
UserName: session.Username,
|
||||
Email: session.Email,
|
||||
SessionID: newSessionToken,
|
||||
Claims: make(map[string]any),
|
||||
Meta: make(map[string]any),
|
||||
},
|
||||
ExpiresIn: int64(24 * time.Hour.Seconds()),
|
||||
}, nil
|
||||
|
||||
@ -7,35 +7,37 @@ import (
|
||||
|
||||
// UserContext holds authenticated user information
|
||||
type UserContext struct {
|
||||
UserID int
|
||||
UserName string
|
||||
UserLevel int
|
||||
SessionID string
|
||||
RemoteID string
|
||||
Roles []string
|
||||
Email string
|
||||
Claims map[string]any
|
||||
UserID int `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
UserLevel int `json:"user_level"`
|
||||
SessionID string `json:"session_id"`
|
||||
RemoteID string `json:"remote_id"`
|
||||
Roles []string `json:"roles"`
|
||||
Email string `json:"email"`
|
||||
Claims map[string]any `json:"claims"`
|
||||
Meta map[string]any `json:"meta"` // Additional metadata that can hold any JSON-serializable values
|
||||
}
|
||||
|
||||
// LoginRequest contains credentials for login
|
||||
type LoginRequest struct {
|
||||
Username string
|
||||
Password string
|
||||
Claims map[string]any // Additional login data
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Claims map[string]any `json:"claims"` // Additional login data
|
||||
Meta map[string]any `json:"meta"` // Additional metadata to be set on user context
|
||||
}
|
||||
|
||||
// LoginResponse contains the result of a login attempt
|
||||
type LoginResponse struct {
|
||||
Token string
|
||||
RefreshToken string
|
||||
User *UserContext
|
||||
ExpiresIn int64 // Token expiration in seconds
|
||||
Token string `json:"token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
User *UserContext `json:"user"`
|
||||
ExpiresIn int64 `json:"expires_in"` // Token expiration in seconds
|
||||
}
|
||||
|
||||
// LogoutRequest contains information for logout
|
||||
type LogoutRequest struct {
|
||||
Token string
|
||||
UserID int
|
||||
Token string `json:"token"`
|
||||
UserID int `json:"user_id"`
|
||||
}
|
||||
|
||||
// Authenticator handles user authentication operations
|
||||
|
||||
@ -10,21 +10,145 @@ type contextKey string
|
||||
|
||||
const (
|
||||
// Context keys for user information
|
||||
UserIDKey contextKey = "user_id"
|
||||
UserNameKey contextKey = "user_name"
|
||||
UserLevelKey contextKey = "user_level"
|
||||
SessionIDKey contextKey = "session_id"
|
||||
RemoteIDKey contextKey = "remote_id"
|
||||
UserRolesKey contextKey = "user_roles"
|
||||
UserEmailKey contextKey = "user_email"
|
||||
UserContextKey contextKey = "user_context"
|
||||
UserIDKey contextKey = "user_id"
|
||||
UserNameKey contextKey = "user_name"
|
||||
UserLevelKey contextKey = "user_level"
|
||||
SessionIDKey contextKey = "session_id"
|
||||
RemoteIDKey contextKey = "remote_id"
|
||||
UserRolesKey contextKey = "user_roles"
|
||||
UserEmailKey contextKey = "user_email"
|
||||
UserContextKey contextKey = "user_context"
|
||||
UserMetaKey contextKey = "user_meta"
|
||||
SkipAuthKey contextKey = "skip_auth"
|
||||
OptionalAuthKey contextKey = "optional_auth"
|
||||
)
|
||||
|
||||
// SkipAuth returns a context with skip auth flag set to true
|
||||
// Use this to mark routes that should bypass authentication middleware
|
||||
func SkipAuth(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, SkipAuthKey, true)
|
||||
}
|
||||
|
||||
// OptionalAuth returns a context with optional auth flag set to true
|
||||
// Use this to mark routes that should try to authenticate, but fall back to guest if authentication fails
|
||||
func OptionalAuth(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, OptionalAuthKey, true)
|
||||
}
|
||||
|
||||
// createGuestContext creates a guest user context for unauthenticated requests
|
||||
func createGuestContext(r *http.Request) *UserContext {
|
||||
return &UserContext{
|
||||
UserID: 0,
|
||||
UserName: "guest",
|
||||
UserLevel: 0,
|
||||
SessionID: "",
|
||||
RemoteID: r.RemoteAddr,
|
||||
Roles: []string{"guest"},
|
||||
Email: "",
|
||||
Claims: map[string]any{},
|
||||
Meta: map[string]any{},
|
||||
}
|
||||
}
|
||||
|
||||
// setUserContext adds a user context to the request context
|
||||
func setUserContext(r *http.Request, userCtx *UserContext) *http.Request {
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, UserContextKey, userCtx)
|
||||
ctx = context.WithValue(ctx, UserIDKey, userCtx.UserID)
|
||||
ctx = context.WithValue(ctx, UserNameKey, userCtx.UserName)
|
||||
ctx = context.WithValue(ctx, UserLevelKey, userCtx.UserLevel)
|
||||
ctx = context.WithValue(ctx, SessionIDKey, userCtx.SessionID)
|
||||
ctx = context.WithValue(ctx, RemoteIDKey, userCtx.RemoteID)
|
||||
ctx = context.WithValue(ctx, UserRolesKey, userCtx.Roles)
|
||||
|
||||
if userCtx.Email != "" {
|
||||
ctx = context.WithValue(ctx, UserEmailKey, userCtx.Email)
|
||||
}
|
||||
if len(userCtx.Meta) > 0 {
|
||||
ctx = context.WithValue(ctx, UserMetaKey, userCtx.Meta)
|
||||
}
|
||||
|
||||
return r.WithContext(ctx)
|
||||
}
|
||||
|
||||
// authenticateRequest performs authentication and adds user context to the request
|
||||
// This is the shared authentication logic used by both handler and middleware
|
||||
func authenticateRequest(w http.ResponseWriter, r *http.Request, provider SecurityProvider) (*http.Request, bool) {
|
||||
// Call the provider's Authenticate method
|
||||
userCtx, err := provider.Authenticate(r)
|
||||
if err != nil {
|
||||
http.Error(w, "Authentication failed: "+err.Error(), http.StatusUnauthorized)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return setUserContext(r, userCtx), true
|
||||
}
|
||||
|
||||
// NewAuthHandler creates an authentication handler that can be used standalone
|
||||
// This handler performs authentication and returns 401 if authentication fails
|
||||
// Use this when you need authentication logic without middleware wrapping
|
||||
func NewAuthHandler(securityList *SecurityList, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Get the security provider
|
||||
provider := securityList.Provider()
|
||||
if provider == nil {
|
||||
http.Error(w, "Security provider not configured", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Authenticate the request
|
||||
authenticatedReq, ok := authenticateRequest(w, r, provider)
|
||||
if !ok {
|
||||
return // authenticateRequest already wrote the error response
|
||||
}
|
||||
|
||||
// Continue with authenticated context
|
||||
next.ServeHTTP(w, authenticatedReq)
|
||||
})
|
||||
}
|
||||
|
||||
// NewOptionalAuthHandler creates an optional authentication handler that can be used standalone
|
||||
// This handler tries to authenticate but falls back to guest context if authentication fails
|
||||
// Use this for routes that should show personalized content for authenticated users but still work for guests
|
||||
func NewOptionalAuthHandler(securityList *SecurityList, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Get the security provider
|
||||
provider := securityList.Provider()
|
||||
if provider == nil {
|
||||
http.Error(w, "Security provider not configured", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Try to authenticate
|
||||
userCtx, err := provider.Authenticate(r)
|
||||
if err != nil {
|
||||
// Authentication failed - set guest context and continue
|
||||
guestCtx := createGuestContext(r)
|
||||
next.ServeHTTP(w, setUserContext(r, guestCtx))
|
||||
return
|
||||
}
|
||||
|
||||
// Authentication succeeded - set user context
|
||||
next.ServeHTTP(w, setUserContext(r, userCtx))
|
||||
})
|
||||
}
|
||||
|
||||
// NewAuthMiddleware creates an authentication middleware with the given security list
|
||||
// This middleware extracts user authentication from the request and adds it to context
|
||||
// Routes can skip authentication by setting SkipAuthKey context value (use SkipAuth helper)
|
||||
// Routes can use optional authentication by setting OptionalAuthKey context value (use OptionalAuth helper)
|
||||
// When authentication is skipped or fails with optional auth, a guest user context is set instead
|
||||
func NewAuthMiddleware(securityList *SecurityList) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Check if this route should skip authentication
|
||||
if skip, ok := r.Context().Value(SkipAuthKey).(bool); ok && skip {
|
||||
// Set guest user context for skipped routes
|
||||
guestCtx := createGuestContext(r)
|
||||
next.ServeHTTP(w, setUserContext(r, guestCtx))
|
||||
return
|
||||
}
|
||||
|
||||
// Get the security provider
|
||||
provider := securityList.Provider()
|
||||
if provider == nil {
|
||||
@ -32,31 +156,25 @@ func NewAuthMiddleware(securityList *SecurityList) func(http.Handler) http.Handl
|
||||
return
|
||||
}
|
||||
|
||||
// Call the provider's Authenticate method
|
||||
// Check if this route has optional authentication
|
||||
optional, _ := r.Context().Value(OptionalAuthKey).(bool)
|
||||
|
||||
// Try to authenticate
|
||||
userCtx, err := provider.Authenticate(r)
|
||||
if err != nil {
|
||||
if optional {
|
||||
// Optional auth failed - set guest context and continue
|
||||
guestCtx := createGuestContext(r)
|
||||
next.ServeHTTP(w, setUserContext(r, guestCtx))
|
||||
return
|
||||
}
|
||||
// Required auth failed - return error
|
||||
http.Error(w, "Authentication failed: "+err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Add user information to context
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, UserContextKey, userCtx)
|
||||
ctx = context.WithValue(ctx, UserIDKey, userCtx.UserID)
|
||||
ctx = context.WithValue(ctx, UserNameKey, userCtx.UserName)
|
||||
ctx = context.WithValue(ctx, UserLevelKey, userCtx.UserLevel)
|
||||
ctx = context.WithValue(ctx, SessionIDKey, userCtx.SessionID)
|
||||
ctx = context.WithValue(ctx, RemoteIDKey, userCtx.RemoteID)
|
||||
|
||||
if len(userCtx.Roles) > 0 {
|
||||
ctx = context.WithValue(ctx, UserRolesKey, userCtx.Roles)
|
||||
}
|
||||
if userCtx.Email != "" {
|
||||
ctx = context.WithValue(ctx, UserEmailKey, userCtx.Email)
|
||||
}
|
||||
|
||||
// Continue with authenticated context
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
// Authentication succeeded - set user context
|
||||
next.ServeHTTP(w, setUserContext(r, userCtx))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -119,3 +237,9 @@ func GetUserEmail(ctx context.Context) (string, bool) {
|
||||
email, ok := ctx.Value(UserEmailKey).(string)
|
||||
return email, ok
|
||||
}
|
||||
|
||||
// GetUserMeta extracts user metadata from context
|
||||
func GetUserMeta(ctx context.Context) (map[string]any, bool) {
|
||||
meta, ok := ctx.Value(UserMetaKey).(map[string]any)
|
||||
return meta, ok
|
||||
}
|
||||
|
||||
@ -15,26 +15,26 @@ import (
|
||||
)
|
||||
|
||||
type ColumnSecurity struct {
|
||||
Schema string
|
||||
Tablename string
|
||||
Path []string
|
||||
ExtraFilters map[string]string
|
||||
UserID int
|
||||
Accesstype string `json:"accesstype"`
|
||||
MaskStart int
|
||||
MaskEnd int
|
||||
MaskInvert bool
|
||||
MaskChar string
|
||||
Control string `json:"control"`
|
||||
ID int `json:"id"`
|
||||
Schema string `json:"schema"`
|
||||
Tablename string `json:"tablename"`
|
||||
Path []string `json:"path"`
|
||||
ExtraFilters map[string]string `json:"extra_filters"`
|
||||
UserID int `json:"user_id"`
|
||||
Accesstype string `json:"accesstype"`
|
||||
MaskStart int `json:"mask_start"`
|
||||
MaskEnd int `json:"mask_end"`
|
||||
MaskInvert bool `json:"mask_invert"`
|
||||
MaskChar string `json:"mask_char"`
|
||||
Control string `json:"control"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type RowSecurity struct {
|
||||
Schema string
|
||||
Tablename string
|
||||
Template string
|
||||
HasBlock bool
|
||||
UserID int
|
||||
Schema string `json:"schema"`
|
||||
Tablename string `json:"tablename"`
|
||||
Template string `json:"template"`
|
||||
HasBlock bool `json:"has_block"`
|
||||
UserID int `json:"user_id"`
|
||||
}
|
||||
|
||||
func (m *RowSecurity) GetTemplate(pPrimaryKeyName string, pModelType reflect.Type) string {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user