* Secrets manager: create secure value using the active keeper * SecureValueService.Update: fetch secure value from db to get the keeper * fix keeper_store_test.go * SecureValueService: fix bug in update where the current version keeper wasn't being passed to the createNewVersion method * make gofmt * remove outdated test * update TestModel * undo enterprise_imports changes * use xkube.Namespace * migrator: set secret_secure_value.keeper to 'system' when the column is null * indent cue * fix tests * fix enterprise imports * properly fix enterprise imports * make update-workspace * go mod tidy --------- Co-authored-by: Matheus Macabu <macabu.matheus@gmail.com>
170 lines
5.0 KiB
Go
170 lines
5.0 KiB
Go
package validator
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation"
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
"k8s.io/apiserver/pkg/admission"
|
|
|
|
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
|
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
|
)
|
|
|
|
type secureValueValidator struct{}
|
|
|
|
var _ contracts.SecureValueValidator = &secureValueValidator{}
|
|
|
|
func ProvideSecureValueValidator() contracts.SecureValueValidator {
|
|
return &secureValueValidator{}
|
|
}
|
|
|
|
func (v *secureValueValidator) Validate(sv, oldSv *secretv1beta1.SecureValue, operation admission.Operation) field.ErrorList {
|
|
errs := make(field.ErrorList, 0)
|
|
|
|
// Operation-specific field validation.
|
|
switch operation {
|
|
case admission.Create:
|
|
errs = validateSecureValueCreate(sv)
|
|
|
|
// If we plan to support PATCH-style updates, we shouldn't be requiring fields to be set.
|
|
case admission.Update:
|
|
errs = validateSecureValueUpdate(sv, oldSv)
|
|
|
|
case admission.Delete:
|
|
case admission.Connect:
|
|
}
|
|
|
|
// General validations.
|
|
if err := validation.IsDNS1123Subdomain(sv.Name); len(err) > 0 {
|
|
errs = append(
|
|
errs,
|
|
field.Invalid(field.NewPath("metadata", "name"), sv.Name, strings.Join(err, ",")),
|
|
)
|
|
|
|
return errs
|
|
}
|
|
if err := validation.IsDNS1123Subdomain(sv.Namespace); len(err) > 0 {
|
|
errs = append(
|
|
errs,
|
|
field.Invalid(field.NewPath("metadata", "namespace"), sv.Name, strings.Join(err, ",")),
|
|
)
|
|
|
|
return errs
|
|
}
|
|
|
|
if sv.Spec.Value != nil && len(*sv.Spec.Value) > contracts.SecureValueRawInputMaxSizeBytes {
|
|
errs = append(
|
|
errs,
|
|
field.TooLong(field.NewPath("spec", "value"), len(*sv.Spec.Value), contracts.SecureValueRawInputMaxSizeBytes),
|
|
)
|
|
}
|
|
|
|
if errs := validateDecrypters(sv.Spec.Decrypters); len(errs) > 0 {
|
|
return errs
|
|
}
|
|
|
|
return errs
|
|
}
|
|
|
|
// validateSecureValueCreate does basic spec validation of a securevalue for the Create operation.
|
|
func validateSecureValueCreate(sv *secretv1beta1.SecureValue) field.ErrorList {
|
|
errs := make(field.ErrorList, 0)
|
|
|
|
if sv.Spec.Description == "" {
|
|
errs = append(errs, field.Required(field.NewPath("spec", "description"), "a `description` is required"))
|
|
}
|
|
|
|
if (sv.Spec.Value == nil || (sv.Spec.Value != nil && *sv.Spec.Value == "")) && (sv.Spec.Ref == nil || (sv.Spec.Ref != nil && *sv.Spec.Ref == "")) {
|
|
errs = append(errs, field.Required(field.NewPath("spec"), "either a `value` or `ref` is required"))
|
|
}
|
|
|
|
if (sv.Spec.Value != nil && *sv.Spec.Value != "") && (sv.Spec.Ref != nil && *sv.Spec.Ref != "") {
|
|
errs = append(errs, field.Forbidden(field.NewPath("spec"), "only one of `value` or `ref` can be set"))
|
|
}
|
|
|
|
return errs
|
|
}
|
|
|
|
// validateSecureValueUpdate does basic spec validation of a securevalue for the Update operation.
|
|
func validateSecureValueUpdate(sv, oldSv *secretv1beta1.SecureValue) field.ErrorList {
|
|
errs := make(field.ErrorList, 0)
|
|
|
|
// For updates, an `old` object is required.
|
|
if oldSv == nil {
|
|
errs = append(errs, field.InternalError(field.NewPath("spec"), errors.New("old object is nil")))
|
|
|
|
return errs
|
|
}
|
|
|
|
// Only validate if one of the fields is being changed/set.
|
|
if (sv.Spec.Value != nil && *sv.Spec.Value != "") || (sv.Spec.Ref != nil && *sv.Spec.Ref != "") {
|
|
if (oldSv.Spec.Ref != nil && *oldSv.Spec.Ref != "") && (sv.Spec.Value != nil && *sv.Spec.Value != "") {
|
|
errs = append(errs, field.Forbidden(field.NewPath("spec"), "cannot set `value` when `ref` was already previously set"))
|
|
}
|
|
|
|
if (oldSv.Spec.Ref == nil || (oldSv.Spec.Ref != nil && *oldSv.Spec.Ref == "")) && (sv.Spec.Ref != nil && *sv.Spec.Ref != "") {
|
|
errs = append(errs, field.Forbidden(field.NewPath("spec"), "cannot set `ref` when `value` was already previously set"))
|
|
}
|
|
}
|
|
|
|
return errs
|
|
}
|
|
|
|
// validateDecrypters validates that (if populated) the `decrypters` must be unique.
|
|
func validateDecrypters(decrypters []string) field.ErrorList {
|
|
errs := make(field.ErrorList, 0)
|
|
|
|
// Limit the number of decrypters to 64 to not have it unbounded.
|
|
// The number was chosen arbitrarily and should be enough.
|
|
if len(decrypters) > 64 {
|
|
errs = append(
|
|
errs,
|
|
field.TooMany(field.NewPath("spec", "decrypters"), len(decrypters), 64),
|
|
)
|
|
|
|
return errs
|
|
}
|
|
|
|
decrypterNames := make(map[string]struct{}, 0)
|
|
|
|
for i, decrypter := range decrypters {
|
|
decrypter = strings.TrimSpace(decrypter)
|
|
if decrypter == "" {
|
|
errs = append(
|
|
errs,
|
|
field.Invalid(field.NewPath("spec", "decrypters", "["+strconv.Itoa(i)+"]"), decrypter, "decrypters cannot be empty if specified"),
|
|
)
|
|
|
|
continue
|
|
}
|
|
|
|
// Use the same validation as labels for the decrypters.
|
|
if verrs := validation.IsValidLabelValue(decrypter); len(verrs) > 0 {
|
|
for _, verr := range verrs {
|
|
errs = append(
|
|
errs,
|
|
field.Invalid(field.NewPath("spec", "decrypters", "["+strconv.Itoa(i)+"]"), decrypter, verr),
|
|
)
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
if _, exists := decrypterNames[decrypter]; exists {
|
|
errs = append(
|
|
errs,
|
|
field.Invalid(field.NewPath("spec", "decrypters", "["+strconv.Itoa(i)+"]"), decrypter, "decrypters must be unique"),
|
|
)
|
|
|
|
continue
|
|
}
|
|
|
|
decrypterNames[decrypter] = struct{}{}
|
|
}
|
|
|
|
return errs
|
|
}
|