// Package middleware provides privacy enforcement for experiment access control. package middleware import ( "context" "fmt" "github.com/jfraeys/fetch_ml/internal/auth" ) // PrivacyLevel defines experiment visibility levels. type PrivacyLevel string const ( // PrivacyPrivate restricts access to owner only. PrivacyPrivate PrivacyLevel = "private" // PrivacyTeam allows team members to view. PrivacyTeam PrivacyLevel = "team" // PrivacyPublic allows all authenticated users. PrivacyPublic PrivacyLevel = "public" // PrivacyAnonymized allows access with PII stripped. PrivacyAnonymized PrivacyLevel = "anonymized" ) // PrivacyEnforcer handles privacy access control. type PrivacyEnforcer struct { enforceTeams bool auditAccess bool } // NewPrivacyEnforcer creates a privacy enforcer. func NewPrivacyEnforcer(enforceTeams, auditAccess bool) *PrivacyEnforcer { return &PrivacyEnforcer{ enforceTeams: enforceTeams, auditAccess: auditAccess, } } // CanAccess checks if a user can access an experiment. func (pe *PrivacyEnforcer) CanAccess( ctx context.Context, user *auth.User, experimentOwner string, level string, team string, ) (bool, error) { privacyLevel := GetPrivacyLevelFromString(level) switch privacyLevel { case PrivacyPublic: return true, nil case PrivacyPrivate: return user.Name == experimentOwner || user.Admin, nil case PrivacyTeam: if user.Name == experimentOwner || user.Admin { return true, nil } if !pe.enforceTeams { return true, nil // Teams not enforced, allow access } // Check if user is in same team return pe.isUserInTeam(ctx, user, team) case PrivacyAnonymized: // Anonymized data is accessible but with PII stripped return true, nil default: return false, fmt.Errorf("unknown privacy level: %s", privacyLevel) } } func (pe *PrivacyEnforcer) isUserInTeam(ctx context.Context, user *auth.User, team string) (bool, error) { // Note: Team membership check not yet implemented. // Future: query teams database or use JWT claims for verification. // Currently denies access when team enforcement is enabled. _ = ctx _ = user _ = team return false, nil } // GetPrivacyLevelFromString converts string to PrivacyLevel. func GetPrivacyLevelFromString(level string) PrivacyLevel { switch level { case "private": return PrivacyPrivate case "team": return PrivacyTeam case "public": return PrivacyPublic case "anonymized": return PrivacyAnonymized default: return PrivacyPrivate // Default to private for safety } }