package platform import ( "context" "strings" "time" ) type VehicleGrant struct { VIN string `json:"-"` ValidFrom time.Time `json:"-"` ValidTo *time.Time `json:"-"` } type Principal struct { VehicleScopeSource string `json:"vehicleScopeSource,omitempty"` ReadOnly bool `json:"readOnly,omitempty"` DataSource string `json:"dataSource,omitempty"` AuthorizationStatus string `json:"authorizationStatus,omitempty"` PermissionKeys []string `json:"-"` SubjectID string `json:"subjectId,omitempty"` SessionID string `json:"-"` Name string `json:"name"` Username string `json:"username,omitempty"` Role string `json:"role"` UserType string `json:"userType"` CustomerRef string `json:"customerRef,omitempty"` TenantRef string `json:"tenantRef,omitempty"` AuthProvider string `json:"authProvider"` MenuKeys []string `json:"menuKeys"` VehicleVINs []string `json:"-"` VehicleGrants []VehicleGrant `json:"-"` VehicleCount int `json:"vehicleCount"` BusinessScopeLevel string `json:"businessScopeLevel,omitempty"` DepartmentIDs []string `json:"departmentIds,omitempty"` ResponsibleUserID string `json:"responsibleUserId,omitempty"` } func (p Principal) Clone() Principal { p.PermissionKeys = append([]string(nil), p.PermissionKeys...) p.MenuKeys = append([]string(nil), p.MenuKeys...) p.VehicleVINs = append([]string(nil), p.VehicleVINs...) p.VehicleGrants = append([]VehicleGrant(nil), p.VehicleGrants...) p.DepartmentIDs = append([]string(nil), p.DepartmentIDs...) p.VehicleCount = len(p.VehicleVINs) return p } func (p Principal) CanMenu(key string) bool { if p.AuthorizationStatus == "loading" { return false } for _, value := range p.MenuKeys { if value == key { return true } } // Legacy admin principals did not carry an explicit menu list. Keep those // principals unrestricted while allowing persisted admin menus to remove a // sensitive surface such as account management. if len(p.MenuKeys) == 0 && (p.UserType == "admin" || p.Role == "admin") { return true } return false } func (p Principal) HasVehicleScope() bool { return p.UserType == "customer" || p.VehicleScopeSource == "oneos-vehicle-query" } // Vehicle-query authorization grants history for currently visible VINs. It // carries no assignment dates; existing local/contract time grants stay intact. func (p Principal) HasHistoryTimeScope() bool { return p.HasVehicleScope() && p.VehicleScopeSource != "oneos-vehicle-query" } func (p Principal) CanVIN(vin string) bool { if p.AuthorizationStatus == "loading" { return false } if !p.HasVehicleScope() { return true } for _, allowed := range p.VehicleVINs { if allowed == vin { if p.AuthProvider == "oneos" { if grant, ok := p.VehicleGrant(vin); ok && !grant.ValidFrom.IsZero() { now := time.Now() return !grant.ValidFrom.After(now) && (grant.ValidTo == nil || grant.ValidTo.After(now)) } } return true } } return false } func (p Principal) VehicleGrant(vin string) (VehicleGrant, bool) { vin = strings.ToUpper(strings.TrimSpace(vin)) for _, grant := range p.VehicleGrants { if strings.ToUpper(strings.TrimSpace(grant.VIN)) == vin { return grant, true } } return VehicleGrant{}, false } type principalContextKey struct{} func WithPrincipal(ctx context.Context, principal Principal) context.Context { return context.WithValue(ctx, principalContextKey{}, principal) } func PrincipalFromContext(ctx context.Context) (Principal, bool) { principal, ok := ctx.Value(principalContextKey{}).(Principal) return principal, ok } func ActorFromContext(ctx context.Context) string { if principal, ok := PrincipalFromContext(ctx); ok && principal.Name != "" { return principal.Name } return "system" }