proxy-pool/internal/domain/clientpolicy/policy.go

83 lines
2.3 KiB
Go

// 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 extraction request. Zero values
// preserve the existing unrestricted listener behavior.
type Policy struct {
MaxExtractCount int `yaml:"maxExtractCount"`
AllowedUpstreams []string `yaml:"allowedUpstreams"`
AllowedRegions []string `yaml:"allowedRegions"`
}
func (policy Policy) IsZero() bool {
return policy.MaxExtractCount == 0 && len(policy.AllowedUpstreams) == 0 && len(policy.AllowedRegions) == 0
}
func (policy Policy) Validate() error {
if policy.MaxExtractCount < 0 ||
!validUniqueValues(policy.AllowedUpstreams) ||
!validUniqueValues(policy.AllowedRegions) {
return ErrInvalidPolicy
}
return nil
}
func (policy Policy) Clone() Policy {
policy.AllowedUpstreams = append([]string(nil), policy.AllowedUpstreams...)
policy.AllowedRegions = append([]string(nil), policy.AllowedRegions...)
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 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
}