// Package clientpolicy defines fixed credential-level limits that can be // enforced before an extraction reaches the activity-pool store. package clientpolicy import ( "errors" "strings" ) var ErrInvalidPolicy = errors.New("invalid client policy") // Policy limits an authenticated client's permitted Distribution extraction or // Gateway routing. Zero values preserve the existing unrestricted behavior. type Policy struct { MaxExtractCount int `yaml:"maxExtractCount"` RequestsPerMinute int `yaml:"requestsPerMinute"` MaxConcurrentConnections int `yaml:"maxConcurrentConnections"` AllowedUpstreams []string `yaml:"allowedUpstreams"` AllowedRegions []string `yaml:"allowedRegions"` AllowedRoutings []string `yaml:"allowedRoutings"` } func (policy Policy) IsZero() bool { return policy.MaxExtractCount == 0 && policy.RequestsPerMinute == 0 && policy.MaxConcurrentConnections == 0 && len(policy.AllowedUpstreams) == 0 && len(policy.AllowedRegions) == 0 && len(policy.AllowedRoutings) == 0 } func (policy Policy) Validate() error { if policy.MaxExtractCount < 0 || policy.RequestsPerMinute < 0 || policy.MaxConcurrentConnections < 0 || !validUniqueValues(policy.AllowedUpstreams) || !validUniqueValues(policy.AllowedRegions) || !validUniqueValues(policy.AllowedRoutings) { return ErrInvalidPolicy } return nil } func (policy Policy) Clone() Policy { policy.AllowedUpstreams = append([]string(nil), policy.AllowedUpstreams...) policy.AllowedRegions = append([]string(nil), policy.AllowedRegions...) policy.AllowedRoutings = append([]string(nil), policy.AllowedRoutings...) return policy } func (policy Policy) AllowsExtractCount(count int) bool { return policy.MaxExtractCount == 0 || count <= policy.MaxExtractCount } func (policy Policy) RestrictUpstreams(requested []string) ([]string, bool) { return restrict(requested, policy.AllowedUpstreams) } func (policy Policy) RestrictRegions(requested []string) ([]string, bool) { return restrict(requested, policy.AllowedRegions) } func (policy Policy) AllowsRouting(routing string) bool { if len(policy.AllowedRoutings) == 0 { return true } for _, allowed := range policy.AllowedRoutings { if routing == allowed { return true } } return false } func restrict(requested, allowed []string) ([]string, bool) { if len(allowed) == 0 { return append([]string(nil), requested...), true } if len(requested) == 0 { return append([]string(nil), allowed...), true } allowedSet := make(map[string]struct{}, len(allowed)) for _, value := range allowed { allowedSet[value] = struct{}{} } for _, value := range requested { if _, ok := allowedSet[value]; !ok { return nil, false } } return append([]string(nil), requested...), true } func validUniqueValues(values []string) bool { seen := make(map[string]struct{}, len(values)) for _, value := range values { if value == "" || strings.TrimSpace(value) != value { return false } if _, exists := seen[value]; exists { return false } seen[value] = struct{}{} } return true }