// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package api import ( "fmt" "reflect" "regexp" "sort" "strings" ) // +kubebuilder:object:generate=true type ForbiddenListSpec struct { Exact []string `json:"denied,omitempty"` Regex string `json:"deniedRegex,omitempty"` } func (in ForbiddenListSpec) ExactMatch(value string) (ok bool) { if len(in.Exact) > 0 { sort.SliceStable(in.Exact, func(i, j int) bool { return strings.ToLower(in.Exact[i]) < strings.ToLower(in.Exact[j]) }) i := sort.SearchStrings(in.Exact, value) ok = i < len(in.Exact) && in.Exact[i] == value } return ok } func (in ForbiddenListSpec) RegexMatch(value string) (ok bool) { if len(in.Regex) > 0 { ok = regexp.MustCompile(in.Regex).MatchString(value) } return ok } type ForbiddenError struct { key string spec ForbiddenListSpec } func NewForbiddenError(key string, forbiddenSpec ForbiddenListSpec) error { return &ForbiddenError{ key: key, spec: forbiddenSpec, } } func (f ForbiddenError) Error() string { return fmt.Sprintf("%s is forbidden for the current Tenant. %s", f.key, f.appendForbiddenError()) } //nolint:predeclared,revive func (f *ForbiddenError) appendForbiddenError() (append string) { append += "Forbidden are " if len(f.spec.Exact) > 0 { append += fmt.Sprintf("one of the following (%s)", strings.Join(f.spec.Exact, ", ")) if len(f.spec.Regex) > 0 { append += " or " } } if len(f.spec.Regex) > 0 { append += fmt.Sprintf("matching the regex %s", f.spec.Regex) } return append } func ValidateForbidden(metadata map[string]string, forbiddenList ForbiddenListSpec) error { if reflect.DeepEqual(ForbiddenListSpec{}, forbiddenList) { return nil } for key := range metadata { var forbidden, matched bool forbidden = forbiddenList.ExactMatch(key) matched = forbiddenList.RegexMatch(key) if forbidden || matched { return NewForbiddenError( key, forbiddenList, ) } } return nil }