Files
grafana/pkg/storage/unified/apistore/secure.go
T

150 lines
4.4 KiB
Go

package apistore
import (
"context"
"fmt"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secret "github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
)
// prepareSecureValues will create any new secure values and register changes inside the provided objectForStorage
// any call to this function MUST be followed by a call to info.finish(ctx, nil, store) to ensure that the secure values are cleaned up
func prepareSecureValues(ctx context.Context, store secret.InlineSecureValueSupport, obj utils.GrafanaMetaAccessor, previousObject utils.GrafanaMetaAccessor, v *objectForStorage) (err error) {
secure, err := obj.GetSecureValues()
if err != nil {
return err
}
// Owner reference for inline values
v.ref = utils.ToObjectReference(obj)
var previous common.InlineSecureValues
if previousObject == nil {
if len(secure) == 0 {
return nil // nothing needs to change
}
if store == nil {
return fmt.Errorf("secure value support is not configured (create)")
}
previous = make(common.InlineSecureValues, 0)
} else {
// Merge in any values from the previous object and handle remove
previous, err = previousObject.GetSecureValues()
if err != nil {
return err
}
for _, p := range previous {
if p.Name == "" || p.Remove || !p.Create.IsZero() {
return fmt.Errorf("invalid state, saved values must only have a name")
}
}
// Keep exactly what we had before
if len(secure) == 0 {
if len(previous) > 0 {
return obj.SetSecureValues(previous)
}
return nil
}
if store == nil {
return fmt.Errorf("secure value support is not configured (update)")
}
}
for k, val := range secure {
before := previous[k]
if val.Name == "" {
if before.Name != "" { // implicitly delete previous secure value if the same field no longer references it
v.deleteSecureValues = append(v.deleteSecureValues, before.Name)
delete(previous, k)
}
if val.Remove {
if before.Name == "" {
return fmt.Errorf("cannot remove secure value '%s', it did not exist in the previous value", k)
}
delete(secure, k)
v.hasChanged = true
continue
}
if !val.Create.IsZero() {
n, err := store.CreateInline(ctx, v.ref, val.Create)
if err != nil {
return err
}
v.createdSecureValues = append(v.createdSecureValues, n)
v.hasChanged = true
secure[k] = common.InlineSecureValue{Name: n}
continue
}
return fmt.Errorf("invalid secure value state: %s", k)
}
// The name changed from the previously stored value
if before.Name != "" && before.Name != val.Name {
// This can happen when explicitly shifting from an inline value to a shared secret
v.deleteSecureValues = append(v.deleteSecureValues, before.Name)
v.hasChanged = true
}
delete(previous, k)
}
// Keep all previous values that were not referenced in the update
for k, v := range previous {
_, found := secure[k]
if !found {
secure[k] = v // the previous value
}
}
return cleanupSecureValues(v, obj, secure)
}
// make sure the registered changes are unique and valid
func cleanupSecureValues(v *objectForStorage, obj utils.GrafanaMetaAccessor, secure common.InlineSecureValues) error {
// Make sure the deleted list is unique and does not contain any referenced values
if len(v.deleteSecureValues) > 0 && len(secure) > 0 {
confirm := v.deleteSecureValues
v.deleteSecureValues = make([]string, 0, len(v.deleteSecureValues))
used := make(map[string]bool, len(secure))
for _, v := range secure {
used[v.Name] = true
}
for _, name := range confirm {
if _, ok := used[name]; ok {
continue
}
used[name] = true
v.deleteSecureValues = append(v.deleteSecureValues, name)
}
}
if len(v.deleteSecureValues) > 0 || len(v.createdSecureValues) > 0 {
v.hasChanged = true
}
return obj.SetSecureValues(secure)
}
// Mutation hook that will delete secure values
func handleSecureValuesDelete(ctx context.Context, store secret.InlineSecureValueSupport, obj utils.GrafanaMetaAccessor) error {
secure, err := obj.GetSecureValues()
if err != nil || len(secure) == 0 {
return err
}
if store == nil {
return fmt.Errorf("secure value support is not configured (delete)")
}
owner := utils.ToObjectReference(obj)
for _, v := range secure {
if err = store.DeleteWhenOwnedByResource(ctx, owner, v.Name); err != nil {
return err
}
}
return obj.SetSecureValues(nil) // remove them from the object
}