diff --git a/pkg/apimachinery/apis/common/v0alpha1/secure_values.go b/pkg/apimachinery/apis/common/v0alpha1/secure_values.go new file mode 100644 index 00000000000..3413753cee4 --- /dev/null +++ b/pkg/apimachinery/apis/common/v0alpha1/secure_values.go @@ -0,0 +1,94 @@ +package v0alpha1 + +import ( + "encoding/json" + "fmt" + "strconv" + + "gopkg.in/yaml.v3" +) + +const redacted = "[REDACTED]" + +// RawSecureValue contains the raw decrypted secure value. +type RawSecureValue string + +var ( + _ fmt.Stringer = (*RawSecureValue)(nil) + _ fmt.Formatter = (*RawSecureValue)(nil) + _ fmt.GoStringer = (*RawSecureValue)(nil) + _ json.Marshaler = (*RawSecureValue)(nil) + _ yaml.Marshaler = (*RawSecureValue)(nil) +) + +// Allow access to a secure value inside +// +k8s:openapi-gen=true +type InlineSecureValue struct { + // Create a secure value -- this is only used for POST/PUT + // +k8s:validation:minLength=1 + // +k8s:validation:maxLength=24576 + Create RawSecureValue `json:"create,omitempty"` + + // Name in the secret service (reference) + Name string `json:"name,omitempty"` + + // Remove this value from the secure value map + // Values owned by this resource will be deleted if necessary + Remove bool `json:"remove,omitempty,omitzero"` +} + +func (v InlineSecureValue) IsZero() bool { + return v.Create.IsZero() && v.Name == "" && !v.Remove +} + +// Collection of secure values +// +k8s:openapi-gen=true +type InlineSecureValues = map[string]InlineSecureValue + +// NewSecretValue creates a new exposed secure value wrapper. +func NewSecretValue(v string) RawSecureValue { + return RawSecureValue(v) +} + +// DangerouslyExposeAndConsumeValue will move the decrypted secure value out of the wrapper and return it. +// Further attempts to call this method will panic. +// The function name is intentionally kept long and weird because this is a dangerous operation and should be used carefully! +func (s *RawSecureValue) DangerouslyExposeAndConsumeValue() string { + if *s == "" { + panic("underlying value is empty or was consumed") + } + + tmp := *s + *s = "" + + return string(tmp) +} + +func (s RawSecureValue) IsZero() bool { + return s == "" // exclude from JSON +} + +// String must not return the exposed secure value. +func (s RawSecureValue) String() string { + return redacted +} + +// Format must not return the exposed secure value. +func (s RawSecureValue) Format(f fmt.State, _verb rune) { + _, _ = fmt.Fprint(f, redacted) +} + +// GoString must not return the exposed secure value. +func (s RawSecureValue) GoString() string { + return redacted +} + +// MarshalJSON must not return the exposed secure value. +func (s RawSecureValue) MarshalJSON() ([]byte, error) { + return []byte(strconv.Quote(redacted)), nil +} + +// MarshalYAML must not return the exposed secure value. +func (s RawSecureValue) MarshalYAML() (any, error) { + return redacted, nil +} diff --git a/pkg/apimachinery/apis/common/v0alpha1/secure_values_test.go b/pkg/apimachinery/apis/common/v0alpha1/secure_values_test.go new file mode 100644 index 00000000000..53f180753a9 --- /dev/null +++ b/pkg/apimachinery/apis/common/v0alpha1/secure_values_test.go @@ -0,0 +1,47 @@ +package v0alpha1 + +import ( + "bytes" + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestSecureValues(t *testing.T) { + expected := "[REDACTED]" + + rawValue := "a-password" + esv := NewSecretValue(rawValue) + + // String must not return the exposed secure value. + require.Equal(t, expected, esv.String()) + + // Format/GoString must not return the exposed secure value. + require.Equal(t, expected, fmt.Sprintf("%+#v", esv)) + require.Equal(t, expected, fmt.Sprintf("%v", esv)) + require.Equal(t, expected, fmt.Sprintf("%s", esv)) + + buf := new(bytes.Buffer) + _, err := fmt.Fprintf(buf, "%#v", esv) + require.NoError(t, err) + require.Equal(t, expected, buf.String()) + + // MarshalJSON must not return the exposed secure value. + bytes, err := json.Marshal(esv) + require.NoError(t, err) + require.Equal(t, `"`+expected+`"`, string(bytes)) + + // MarshalYAML must not return the exposed secure value. + bytes, err = yaml.Marshal(esv) + require.NoError(t, err) + require.Equal(t, "'"+expected+"'\n", string(bytes)) + + // DangerouslyExposeAndConsumeValue returns the raw value. + require.Equal(t, rawValue, esv.DangerouslyExposeAndConsumeValue()) + + // Further calls to DangerouslyExposeAndConsumeValue will panic. + require.Panics(t, func() { esv.DangerouslyExposeAndConsumeValue() }) +} diff --git a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go index 8a3334e3634..d3f8e64dbff 100644 --- a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.deepcopy.go @@ -11,6 +11,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InlineSecureValue) DeepCopyInto(out *InlineSecureValue) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InlineSecureValue. +func (in *InlineSecureValue) DeepCopy() *InlineSecureValue { + if in == nil { + return nil + } + out := new(InlineSecureValue) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ObjectReference) DeepCopyInto(out *ObjectReference) { *out = *in diff --git a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go index f4465722266..2f92840b5e6 100644 --- a/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go +++ b/pkg/apimachinery/apis/common/v0alpha1/zz_generated.openapi.go @@ -11,68 +11,106 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" common "k8s.io/kube-openapi/pkg/common" spec "k8s.io/kube-openapi/pkg/validation/spec" + ptr "k8s.io/utils/ptr" ) func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ObjectReference": schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Scope": schema_apimachinery_apis_common_v0alpha1_Scope(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter": schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec": schema_apimachinery_apis_common_v0alpha1_ScopeSpec(ref), - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": Unstructured{}.OpenAPIDefinition(), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ApplyOptions": schema_pkg_apis_meta_v1_ApplyOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Condition": schema_pkg_apis_meta_v1_Condition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldSelectorRequirement": schema_pkg_apis_meta_v1_FieldSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), - "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), - "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), - "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), - "k8s.io/apimachinery/pkg/version.Info": schema_k8sio_apimachinery_pkg_version_Info(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue": schema_apimachinery_apis_common_v0alpha1_InlineSecureValue(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ObjectReference": schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Scope": schema_apimachinery_apis_common_v0alpha1_Scope(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter": schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec": schema_apimachinery_apis_common_v0alpha1_ScopeSpec(ref), + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": Unstructured{}.OpenAPIDefinition(), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ApplyOptions": schema_pkg_apis_meta_v1_ApplyOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Condition": schema_pkg_apis_meta_v1_Condition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldSelectorRequirement": schema_pkg_apis_meta_v1_FieldSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), + "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), + "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), + "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), + "k8s.io/apimachinery/pkg/version.Info": schema_k8sio_apimachinery_pkg_version_Info(ref), + } +} + +func schema_apimachinery_apis_common_v0alpha1_InlineSecureValue(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Allow access to a secure value inside", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "create": { + SchemaProps: spec.SchemaProps{ + Description: "Create a secure value -- this is only used for POST/PUT", + MinLength: ptr.To[int64](1), + MaxLength: ptr.To[int64](24576), + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name in the secret service (reference)", + Type: []string{"string"}, + Format: "", + }, + }, + "remove": { + SchemaProps: spec.SchemaProps{ + Description: "Remove this value from the secure value map Values owned by this resource will be deleted if necessary", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, } } diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index a69b61d33ff..3d4120daa43 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -7,9 +7,11 @@ require ( github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // @grafana/identity-access-team github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team github.com/stretchr/testify v1.10.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.33.2 k8s.io/apiserver v0.33.2 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 ) require ( @@ -48,9 +50,7 @@ require ( google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect diff --git a/pkg/apimachinery/utils/meta.go b/pkg/apimachinery/utils/meta.go index 0d19536138d..b24b6d58f69 100644 --- a/pkg/apimachinery/utils/meta.go +++ b/pkg/apimachinery/utils/meta.go @@ -12,6 +12,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + + common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) // LabelKeyGetHistory is used to select object history for an given resource @@ -146,6 +148,12 @@ type GrafanaMetaAccessor interface { // SetSourceProperties sets the source properties of the resource. SetSourceProperties(SourceProperties) + + // SetSourceProperties sets the source properties of the resource. + GetSecureValues() (common.InlineSecureValues, error) + + // SetSourceProperties sets the source properties of the resource. + SetSecureValues(common.InlineSecureValues) error } var _ GrafanaMetaAccessor = (*grafanaMetaAccessor)(nil) @@ -821,3 +829,97 @@ func (m *grafanaMetaAccessor) SetSourceProperties(v SourceProperties) { m.obj.SetAnnotations(annot) } + +// GetSecureValues implements GrafanaMetaAccessor. +func (m *grafanaMetaAccessor) GetSecureValues() (vals common.InlineSecureValues, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("error reading spec") + } + }() + + var property any // may be map or struct + + f := m.r.FieldByName("Secure") + if f.IsValid() { + property = f.Interface() + } else { + // Unstructured + u, ok := m.raw.(*unstructured.Unstructured) + if ok { + property = u.Object["secure"] + } + } + + // Not found (and no error) + if property == nil { + return nil, nil + } + + // Try directly casting the property + vals, ok := property.(common.InlineSecureValues) + if ok { + return vals, nil + } + + // Generic map + u, ok := property.(map[string]any) + if ok { + vals = make(common.InlineSecureValues, len(u)) + for k, v := range u { + sv, ok := v.(map[string]any) + if !ok { + return nil, fmt.Errorf("unsupported nested secure value: %t", v) + } + inline := common.InlineSecureValue{} + inline.Name, _, _ = unstructured.NestedString(sv, "name") + inline.Remove, _, _ = unstructured.NestedBool(sv, "remove") + create, _, _ := unstructured.NestedString(sv, "create") + if create != "" { + inline.Create = common.NewSecretValue(create) + } + vals[k] = inline + } + return vals, nil + } + + fmt.Printf("TODO PROPERTY: (%t) %+v\n", property, property) + + return nil, fmt.Errorf("support: %t", property) +} + +// SetSecureValues implements GrafanaMetaAccessor. +func (m *grafanaMetaAccessor) SetSecureValues(vals common.InlineSecureValues) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("error setting spec") + } + }() + + f := m.r.FieldByName("Secure") + if f.IsValid() && f.CanSet() { + f.Set(reflect.ValueOf(vals)) + return + } + + // Unstructured object + u, ok := m.raw.(*unstructured.Unstructured) + if ok { + u.Object["secure"] = vals + return + } + + return fmt.Errorf("unable to set secure values on (%T)", m.raw) +} + +func ToObjectReference(obj GrafanaMetaAccessor) common.ObjectReference { + gvk := obj.GetGroupVersionKind() + return common.ObjectReference{ + APIGroup: gvk.Group, + APIVersion: gvk.Version, + Kind: gvk.Kind, + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + UID: obj.GetUID(), + } +} diff --git a/pkg/apimachinery/utils/meta_mock.go b/pkg/apimachinery/utils/meta_mock.go index d73107b1bd6..4a0815a1b08 100644 --- a/pkg/apimachinery/utils/meta_mock.go +++ b/pkg/apimachinery/utils/meta_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.52.4. DO NOT EDIT. +// Code generated by mockery v2.53.4. DO NOT EDIT. package utils @@ -12,6 +12,8 @@ import ( types "k8s.io/apimachinery/pkg/types" + v0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -1248,6 +1250,63 @@ func (_c *MockGrafanaMetaAccessor_GetRuntimeObject_Call) RunAndReturn(run func() return _c } +// GetSecureValues provides a mock function with no fields +func (_m *MockGrafanaMetaAccessor) GetSecureValues() (map[string]v0alpha1.InlineSecureValue, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetSecureValues") + } + + var r0 map[string]v0alpha1.InlineSecureValue + var r1 error + if rf, ok := ret.Get(0).(func() (map[string]v0alpha1.InlineSecureValue, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() map[string]v0alpha1.InlineSecureValue); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]v0alpha1.InlineSecureValue) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockGrafanaMetaAccessor_GetSecureValues_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSecureValues' +type MockGrafanaMetaAccessor_GetSecureValues_Call struct { + *mock.Call +} + +// GetSecureValues is a helper method to define mock.On call +func (_e *MockGrafanaMetaAccessor_Expecter) GetSecureValues() *MockGrafanaMetaAccessor_GetSecureValues_Call { + return &MockGrafanaMetaAccessor_GetSecureValues_Call{Call: _e.mock.On("GetSecureValues")} +} + +func (_c *MockGrafanaMetaAccessor_GetSecureValues_Call) Run(run func()) *MockGrafanaMetaAccessor_GetSecureValues_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockGrafanaMetaAccessor_GetSecureValues_Call) Return(_a0 map[string]v0alpha1.InlineSecureValue, _a1 error) *MockGrafanaMetaAccessor_GetSecureValues_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockGrafanaMetaAccessor_GetSecureValues_Call) RunAndReturn(run func() (map[string]v0alpha1.InlineSecureValue, error)) *MockGrafanaMetaAccessor_GetSecureValues_Call { + _c.Call.Return(run) + return _c +} + // GetSelfLink provides a mock function with no fields func (_m *MockGrafanaMetaAccessor) GetSelfLink() string { ret := _m.Called() @@ -2369,6 +2428,52 @@ func (_c *MockGrafanaMetaAccessor_SetResourceVersionInt64_Call) RunAndReturn(run return _c } +// SetSecureValues provides a mock function with given fields: _a0 +func (_m *MockGrafanaMetaAccessor) SetSecureValues(_a0 map[string]v0alpha1.InlineSecureValue) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for SetSecureValues") + } + + var r0 error + if rf, ok := ret.Get(0).(func(map[string]v0alpha1.InlineSecureValue) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockGrafanaMetaAccessor_SetSecureValues_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetSecureValues' +type MockGrafanaMetaAccessor_SetSecureValues_Call struct { + *mock.Call +} + +// SetSecureValues is a helper method to define mock.On call +// - _a0 map[string]v0alpha1.InlineSecureValue +func (_e *MockGrafanaMetaAccessor_Expecter) SetSecureValues(_a0 interface{}) *MockGrafanaMetaAccessor_SetSecureValues_Call { + return &MockGrafanaMetaAccessor_SetSecureValues_Call{Call: _e.mock.On("SetSecureValues", _a0)} +} + +func (_c *MockGrafanaMetaAccessor_SetSecureValues_Call) Run(run func(_a0 map[string]v0alpha1.InlineSecureValue)) *MockGrafanaMetaAccessor_SetSecureValues_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(map[string]v0alpha1.InlineSecureValue)) + }) + return _c +} + +func (_c *MockGrafanaMetaAccessor_SetSecureValues_Call) Return(_a0 error) *MockGrafanaMetaAccessor_SetSecureValues_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockGrafanaMetaAccessor_SetSecureValues_Call) RunAndReturn(run func(map[string]v0alpha1.InlineSecureValue) error) *MockGrafanaMetaAccessor_SetSecureValues_Call { + _c.Call.Return(run) + return _c +} + // SetSelfLink provides a mock function with given fields: selfLink func (_m *MockGrafanaMetaAccessor) SetSelfLink(selfLink string) { _m.Called(selfLink) diff --git a/pkg/apimachinery/utils/meta_test.go b/pkg/apimachinery/utils/meta_test.go index 0cc7d423d8f..fa58489b768 100644 --- a/pkg/apimachinery/utils/meta_test.go +++ b/pkg/apimachinery/utils/meta_test.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" ) @@ -24,6 +25,9 @@ type TestResource struct { // Read/write raw status Status Spec `json:"status,omitempty"` + + // Secure values as map + Secure common.InlineSecureValues `json:"secure,omitempty"` } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -84,6 +88,9 @@ type TestResource2 struct { // Exercise read/write pointer status Status *Spec `json:"status,omitempty"` + + // This time defined with a strict struct + SecureValues ExplictSecureValues `json:"secure,omitempty"` } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -112,6 +119,12 @@ func (in *TestResource2) DeepCopyObject() runtime.Object { return nil } +// Spec defines model for Spec. +type ExplictSecureValues struct { + // Sample token value + Prop common.InlineSecureValue `json:"token,omitempty"` +} + // Spec defines model for Spec. type Spec2 struct{} @@ -247,6 +260,15 @@ func TestMetaAccessor(t *testing.T) { status, err = meta.GetStatus() require.NoError(t, err) require.Equal(t, res.Object["status"], status) + + // Check write/read on unstructured object + err = meta.SetSecureValues(common.InlineSecureValues{ + "a": {Name: "bbbb"}, + }) + require.NoError(t, err) + secure, err := meta.GetSecureValues() + require.NoError(t, err) + require.JSONEq(t, `{"a": {"name": "bbbb"}}`, asJSON(secure, true)) }) t.Run("get and set grafana metadata (TestResource)", func(t *testing.T) { @@ -254,6 +276,11 @@ func TestMetaAccessor(t *testing.T) { Spec: Spec{ Title: "test", }, + Secure: common.InlineSecureValues{ + "x": common.InlineSecureValue{ + Create: "hello", + }, + }, // Status is empty, but not nil! } meta, err := utils.MetaAccessor(res) @@ -302,6 +329,19 @@ func TestMetaAccessor(t *testing.T) { require.Equal(t, res.Status, status) require.Equal(t, "111", res.Status.Title) require.Equal(t, `{"title":"111"}`, asJSON(status, false)) + + // Check read/write secure values + secure, err := meta.GetSecureValues() + require.NoError(t, err) + require.JSONEq(t, `{"x": {"create": "[REDACTED]"}}`, asJSON(secure, true)) + + err = meta.SetSecureValues(common.InlineSecureValues{ + "a": {Name: "bbbb"}, + }) + require.NoError(t, err) + secure, err = meta.GetSecureValues() + require.NoError(t, err) + require.JSONEq(t, `{"a": {"name": "bbbb"}}`, asJSON(secure, true)) }) t.Run("get and set grafana metadata (TestResource2)", func(t *testing.T) { diff --git a/pkg/apis/datasource/v0alpha1/config.go b/pkg/apis/datasource/v0alpha1/config.go index f34db889cbe..2e42530df60 100644 --- a/pkg/apis/datasource/v0alpha1/config.go +++ b/pkg/apis/datasource/v0alpha1/config.go @@ -4,7 +4,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" - secret "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" ) // +k8s:deepcopy-gen=true @@ -18,7 +17,7 @@ type DataSource struct { Spec DataSourceSpec `json:"spec"` // Secure values placeholder (true for fields that exist) - Secure secret.InlineSecureValues `json:"secure,omitempty"` + Secure common.InlineSecureValues `json:"secure,omitempty"` } // DsAccess represents how the datasource connects to the remote service diff --git a/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go index 0e4c77428ec..c00da0884c0 100644 --- a/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/datasource/v0alpha1/zz_generated.deepcopy.go @@ -8,7 +8,7 @@ package v0alpha1 import ( - secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + commonv0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -20,7 +20,7 @@ func (in *DataSource) DeepCopyInto(out *DataSource) { in.Spec.DeepCopyInto(&out.Spec) if in.Secure != nil { in, out := &in.Secure, &out.Secure - *out = make(map[string]secretv0alpha1.InlineSecureValue, len(*in)) + *out = make(map[string]commonv0alpha1.InlineSecureValue, len(*in)) for key, val := range *in { (*out)[key] = val } diff --git a/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go b/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go index 1471be36a8a..04195be8e3d 100644 --- a/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/datasource/v0alpha1/zz_generated.openapi.go @@ -63,7 +63,7 @@ func schema_pkg_apis_datasource_v0alpha1_DataSource(ref common.ReferenceCallback Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/secret/v0alpha1.InlineSecureValue"), + Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue"), }, }, }, @@ -74,7 +74,7 @@ func schema_pkg_apis_datasource_v0alpha1_DataSource(ref common.ReferenceCallback }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSourceSpec", "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.InlineSecureValue", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.InlineSecureValue", "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1.DataSourceSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } diff --git a/pkg/apis/secret/v0alpha1/exposed_secure_value.go b/pkg/apis/secret/v0alpha1/exposed_secure_value.go index c695c924f5e..1f77cca2112 100644 --- a/pkg/apis/secret/v0alpha1/exposed_secure_value.go +++ b/pkg/apis/secret/v0alpha1/exposed_secure_value.go @@ -40,10 +40,6 @@ func (s *ExposedSecureValue) DangerouslyExposeAndConsumeValue() string { return string(tmp) } -func (s ExposedSecureValue) IsZero() bool { - return s == "" // exclude from JSON -} - // String must not return the exposed secure value. func (s ExposedSecureValue) String() string { return redacted diff --git a/pkg/apis/secret/v0alpha1/exposed_secure_value_test.go b/pkg/apis/secret/v0alpha1/exposed_secure_value_test.go index 003d9b9edf7..1d21e0505bc 100644 --- a/pkg/apis/secret/v0alpha1/exposed_secure_value_test.go +++ b/pkg/apis/secret/v0alpha1/exposed_secure_value_test.go @@ -6,10 +6,9 @@ import ( "fmt" "testing" + "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" - - "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" ) func TestExposedSecureValue(t *testing.T) { diff --git a/pkg/apis/secret/v0alpha1/inline.go b/pkg/apis/secret/v0alpha1/inline.go deleted file mode 100644 index c9557503500..00000000000 --- a/pkg/apis/secret/v0alpha1/inline.go +++ /dev/null @@ -1,23 +0,0 @@ -package v0alpha1 - -// Access secure values inside any resource -// +k8s:openapi-gen=true -type InlineSecureValue struct { - // Create a secure value -- this is only used for POST/PUT - // +k8s:validation:minLength=1 - // +k8s:validation:maxLength=24576 - Create ExposedSecureValue `json:"create,omitempty"` - - // Name in the secret service (reference) - Name string `json:"name,omitempty"` - - // The secret is shared (enterprise only) - Shared bool `json:"shared,omitempty"` - - // Remove this value -- cascading delete to the secret service if necessary - Remove bool `json:"remove,omitempty,omitzero"` -} - -// Collection of secure values -// +k8s:openapi-gen=true -type InlineSecureValues = map[string]InlineSecureValue diff --git a/pkg/apis/secret/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/secret/v0alpha1/zz_generated.deepcopy.go index 5dd1844463a..727f1e43034 100644 --- a/pkg/apis/secret/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/secret/v0alpha1/zz_generated.deepcopy.go @@ -163,22 +163,6 @@ func (in *HashiCorpKeeperConfig) DeepCopy() *HashiCorpKeeperConfig { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InlineSecureValue) DeepCopyInto(out *InlineSecureValue) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InlineSecureValue. -func (in *InlineSecureValue) DeepCopy() *InlineSecureValue { - if in == nil { - return nil - } - out := new(InlineSecureValue) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Keeper) DeepCopyInto(out *Keeper) { *out = *in diff --git a/pkg/apis/secret/v0alpha1/zz_generated.openapi.go b/pkg/apis/secret/v0alpha1/zz_generated.openapi.go index 2857e5f4996..836d33f83fa 100644 --- a/pkg/apis/secret/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/secret/v0alpha1/zz_generated.openapi.go @@ -24,7 +24,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.GCPKeeperConfig": schema_pkg_apis_secret_v0alpha1_GCPKeeperConfig(ref), "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.HashiCorpCredentials": schema_pkg_apis_secret_v0alpha1_HashiCorpCredentials(ref), "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.HashiCorpKeeperConfig": schema_pkg_apis_secret_v0alpha1_HashiCorpKeeperConfig(ref), - "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.InlineSecureValue": schema_pkg_apis_secret_v0alpha1_InlineSecureValue(ref), "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.Keeper": schema_pkg_apis_secret_v0alpha1_Keeper(ref), "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.KeeperList": schema_pkg_apis_secret_v0alpha1_KeeperList(ref), "github.com/grafana/grafana/pkg/apis/secret/v0alpha1.KeeperSpec": schema_pkg_apis_secret_v0alpha1_KeeperSpec(ref), @@ -345,49 +344,6 @@ func schema_pkg_apis_secret_v0alpha1_HashiCorpKeeperConfig(ref common.ReferenceC } } -func schema_pkg_apis_secret_v0alpha1_InlineSecureValue(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Access secure values inside any resource", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "create": { - SchemaProps: spec.SchemaProps{ - Description: "Create a secure value -- this is only used for POST/PUT", - MinLength: ptr.To[int64](1), - MaxLength: ptr.To[int64](24576), - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name in the secret service (reference)", - Type: []string{"string"}, - Format: "", - }, - }, - "shared": { - SchemaProps: spec.SchemaProps{ - Description: "The secret is shared (enterprise only)", - Type: []string{"boolean"}, - Format: "", - }, - }, - "remove": { - SchemaProps: spec.SchemaProps{ - Description: "Remove this value -- cascading delete to the secret service if necessary", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - } -} - func schema_pkg_apis_secret_v0alpha1_Keeper(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/registry/apis/datasource/converter.go b/pkg/registry/apis/datasource/converter.go index 53f832489af..c77f8e75687 100644 --- a/pkg/registry/apis/datasource/converter.go +++ b/pkg/registry/apis/datasource/converter.go @@ -10,9 +10,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/grafana/authlib/types" + common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" datasourceV0 "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1" - secretV0 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils" @@ -64,7 +64,7 @@ func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.Data } if ds.SecureJsonData != nil { - cfg.Secure = make(secretV0.InlineSecureValues) + cfg.Secure = make(common.InlineSecureValues) for k := range ds.SecureJsonData { h := sha256.New() h.Write([]byte(ds.Type)) // group+resource @@ -73,7 +73,7 @@ func (r *converter) asDataSource(ds *datasources.DataSource) (*datasourceV0.Data h.Write([]byte("|")) h.Write([]byte(k)) // property n := hex.EncodeToString(h.Sum(nil)) - cfg.Secure[k] = secretV0.InlineSecureValue{ + cfg.Secure[k] = common.InlineSecureValue{ Name: "@" + n[0:10], // ?????? } } @@ -159,9 +159,6 @@ func toSecureJsonData(ds *datasourceV0.DataSource) (map[string]string, error) { secure := map[string]string{} for k, v := range ds.Secure { - if v.Shared { - return nil, fmt.Errorf("shared secrets not yet supported (%s)", k) - } if v.Create != "" { secure[k] = v.Create.DangerouslyExposeAndConsumeValue() } diff --git a/pkg/registry/apis/datasource/hardcoded/testdata.go b/pkg/registry/apis/datasource/hardcoded/testdata.go index 9d74dbb3e08..66f7cc73ca2 100644 --- a/pkg/registry/apis/datasource/hardcoded/testdata.go +++ b/pkg/registry/apis/datasource/hardcoded/testdata.go @@ -232,5 +232,4 @@ func TestdataOpenAPIExtension() (*datasourceV0.DataSourceOpenAPIExtension, error oas.Routes["/test/json"] = &testcopy return oas, nil - } diff --git a/pkg/registry/apis/datasource/openapi.go b/pkg/registry/apis/datasource/openapi.go index 9414a79d870..9c508d8d455 100644 --- a/pkg/registry/apis/datasource/openapi.go +++ b/pkg/registry/apis/datasource/openapi.go @@ -8,7 +8,7 @@ import ( "k8s.io/kube-openapi/pkg/spec3" "k8s.io/kube-openapi/pkg/validation/spec" - secretsV0 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1" + common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/query/queryschema" "github.com/grafana/grafana/pkg/services/apiserver/builder" ) @@ -66,7 +66,7 @@ func (b *DataSourceAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.Op } if custom.SecureValues != nil { - example := secretsV0.InlineSecureValues{} + example := common.InlineSecureValues{} ref := spec.MustCreateRef("#/components/schemas/com.github.grafana.grafana.pkg.apis.secret.v0alpha1.InlineSecureValue") secure := &spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -84,7 +84,7 @@ func (b *DataSourceAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.Op } if v.Required { secure.Required = append(secure.Required, v.Key) - example[v.Key] = secretsV0.InlineSecureValue{Create: "***"} + example[v.Key] = common.InlineSecureValue{Create: "***"} } }