mirror of https://github.com/grafana/grafana.git
SecretsManager: add data key store (#107396)
* SecretsManager: Add data key store Co-authored-by: Michael Mandrus <michael.mandrus@grafana.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> Co-authored-by: Dana Axinte <53751979+dana-axinte@users.noreply.github.com> * SecretsManager: Add wiring of data key store Co-authored-by: Michael Mandrus <michael.mandrus@grafana.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> Co-authored-by: Dana Axinte <53751979+dana-axinte@users.noreply.github.com> --------- Co-authored-by: Michael Mandrus <michael.mandrus@grafana.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com>
This commit is contained in:
parent
2d634639a2
commit
0fccc01ebe
|
|
@ -0,0 +1,35 @@
|
|||
package contracts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDataKeyNotFound = errors.New("data key not found")
|
||||
)
|
||||
|
||||
// SecretDataKey does not have a mirrored K8s resource
|
||||
type SecretDataKey struct {
|
||||
UID string
|
||||
Active bool
|
||||
Namespace string
|
||||
Label string
|
||||
Provider encryption.ProviderID
|
||||
EncryptedData []byte
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
// DataKeyStorage is the interface for wiring and dependency injection.
|
||||
type DataKeyStorage interface {
|
||||
CreateDataKey(ctx context.Context, dataKey *SecretDataKey) error
|
||||
GetDataKey(ctx context.Context, namespace, uid string) (*SecretDataKey, error)
|
||||
GetCurrentDataKey(ctx context.Context, namespace, label string) (*SecretDataKey, error)
|
||||
GetAllDataKeys(ctx context.Context, namespace string) ([]*SecretDataKey, error)
|
||||
DisableDataKeys(ctx context.Context, namespace string) error
|
||||
DeleteDataKey(ctx context.Context, namespace, uid string) error
|
||||
}
|
||||
|
|
@ -1,3 +1,26 @@
|
|||
package encryption
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const UsageInsightsPrefix = "secrets_manager"
|
||||
|
||||
type ProviderID string
|
||||
|
||||
func (id ProviderID) Kind() (string, error) {
|
||||
idStr := string(id)
|
||||
|
||||
parts := strings.SplitN(idStr, ".", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("malformatted provider identifier %s: expected format <provider>.<keyName>", idStr)
|
||||
}
|
||||
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
func KeyLabel(providerID ProviderID) string {
|
||||
return fmt.Sprintf("%s@%s", time.Now().Format("2006-01-02"), providerID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -423,6 +423,7 @@ var wireBasicSet = wire.NewSet(
|
|||
secretmetadata.ProvideSecureValueMetadataStorage,
|
||||
secretmetadata.ProvideKeeperMetadataStorage,
|
||||
secretmetadata.ProvideOutboxQueue,
|
||||
secretencryption.ProvideDataKeyStorage,
|
||||
secretencryption.ProvideEncryptedValueStorage,
|
||||
secretmigrator.NewWithEngine,
|
||||
secretdatabase.ProvideDatabase,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
INSERT INTO {{ .Ident "secret_data_key" }} (
|
||||
{{ .Ident "uid" }},
|
||||
{{ .Ident "namespace" }},
|
||||
{{ .Ident "label" }},
|
||||
{{ .Ident "provider" }},
|
||||
{{ .Ident "encrypted_data" }},
|
||||
{{ .Ident "active" }},
|
||||
{{ .Ident "created" }},
|
||||
{{ .Ident "updated" }}
|
||||
) VALUES (
|
||||
{{ .Arg .Row.UID }},
|
||||
{{ .Arg .Row.Namespace }},
|
||||
{{ .Arg .Row.Label }},
|
||||
{{ .Arg .Row.Provider }},
|
||||
{{ .Arg .Row.EncryptedData }},
|
||||
{{ .Arg .Row.Active }},
|
||||
{{ .Arg .Row.Created }},
|
||||
{{ .Arg .Row.Updated }}
|
||||
);
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
DELETE FROM {{ .Ident "secret_data_key" }}
|
||||
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
|
||||
{{ .Ident "uid" }} = {{ .Arg .UID }}
|
||||
;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
UPDATE
|
||||
{{ .Ident "secret_data_key" }}
|
||||
SET
|
||||
{{ .Ident "active" }} = false,
|
||||
{{ .Ident "updated" }} = {{ .Arg .Updated }}
|
||||
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
|
||||
{{ .Ident "active" }} = true
|
||||
;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
SELECT
|
||||
{{ .Ident "uid" }},
|
||||
{{ .Ident "namespace" }},
|
||||
{{ .Ident "label" }},
|
||||
{{ .Ident "provider" }},
|
||||
{{ .Ident "encrypted_data" }},
|
||||
{{ .Ident "active" }},
|
||||
{{ .Ident "created" }},
|
||||
{{ .Ident "updated" }}
|
||||
FROM
|
||||
{{ .Ident "secret_data_key" }}
|
||||
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }}
|
||||
;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
SELECT
|
||||
{{ .Ident "uid" }},
|
||||
{{ .Ident "namespace" }},
|
||||
{{ .Ident "label" }},
|
||||
{{ .Ident "provider" }},
|
||||
{{ .Ident "encrypted_data" }},
|
||||
{{ .Ident "active" }},
|
||||
{{ .Ident "created" }},
|
||||
{{ .Ident "updated" }}
|
||||
FROM
|
||||
{{ .Ident "secret_data_key" }}
|
||||
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
|
||||
{{ .Ident "uid" }} = {{ .Arg .UID }}
|
||||
;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
SELECT
|
||||
{{ .Ident "uid" }},
|
||||
{{ .Ident "namespace" }},
|
||||
{{ .Ident "label" }},
|
||||
{{ .Ident "provider" }},
|
||||
{{ .Ident "encrypted_data" }},
|
||||
{{ .Ident "active" }},
|
||||
{{ .Ident "created" }},
|
||||
{{ .Ident "updated" }}
|
||||
FROM
|
||||
{{ .Ident "secret_data_key" }}
|
||||
WHERE {{ .Ident "namespace" }} = {{ .Arg .Namespace }} AND
|
||||
{{ .Ident "label" }} = {{ .Arg .Label }} AND
|
||||
{{ .Ident "active" }} = true
|
||||
;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package encryption
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption"
|
||||
)
|
||||
|
||||
// SecretDataKey does not have a mirrored K8s resource
|
||||
type SecretDataKey struct {
|
||||
UID string
|
||||
Active bool
|
||||
Namespace string
|
||||
Label string
|
||||
Provider encryption.ProviderID
|
||||
EncryptedData []byte
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
package encryption
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// encryptionStoreImpl is the actual implementation of the data key storage.
|
||||
type encryptionStoreImpl struct {
|
||||
db contracts.Database
|
||||
dialect sqltemplate.Dialect
|
||||
tracer trace.Tracer
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func ProvideDataKeyStorage(
|
||||
db contracts.Database,
|
||||
tracer trace.Tracer,
|
||||
features featuremgmt.FeatureToggles,
|
||||
registerer prometheus.Registerer,
|
||||
) (contracts.DataKeyStorage, error) {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) ||
|
||||
!features.IsEnabledGlobally(featuremgmt.FlagSecretsManagementAppPlatform) {
|
||||
return &encryptionStoreImpl{}, nil
|
||||
}
|
||||
|
||||
store := &encryptionStoreImpl{
|
||||
db: db,
|
||||
dialect: sqltemplate.DialectForDriver(db.DriverName()),
|
||||
tracer: tracer,
|
||||
log: log.New("encryption.store"),
|
||||
}
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
func (ss *encryptionStoreImpl) GetDataKey(ctx context.Context, namespace, uid string) (*contracts.SecretDataKey, error) {
|
||||
ctx, span := ss.tracer.Start(ctx, "DataKeyStorage.GetDataKey", trace.WithAttributes(
|
||||
attribute.String("namespace", namespace),
|
||||
attribute.String("uid", uid),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
req := readDataKey{
|
||||
SQLTemplate: sqltemplate.New(ss.dialect),
|
||||
Namespace: namespace,
|
||||
UID: uid,
|
||||
}
|
||||
|
||||
query, err := sqltemplate.Execute(sqlDataKeyRead, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlDataKeyRead.Name(), err)
|
||||
}
|
||||
|
||||
res, err := ss.db.QueryContext(ctx, query, req.GetArgs()...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting data key row: %w", err)
|
||||
}
|
||||
defer func() { _ = res.Close() }()
|
||||
|
||||
if !res.Next() {
|
||||
return nil, contracts.ErrDataKeyNotFound
|
||||
}
|
||||
|
||||
var dataKey SecretDataKey
|
||||
err = res.Scan(
|
||||
&dataKey.UID,
|
||||
&dataKey.Namespace,
|
||||
&dataKey.Label,
|
||||
&dataKey.Provider,
|
||||
&dataKey.EncryptedData,
|
||||
&dataKey.Active,
|
||||
&dataKey.Created,
|
||||
&dataKey.Updated,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to scan data key row: %w", err)
|
||||
}
|
||||
if err := res.Err(); err != nil {
|
||||
return nil, fmt.Errorf("read rows error: %w", err)
|
||||
}
|
||||
|
||||
return &contracts.SecretDataKey{
|
||||
UID: dataKey.UID,
|
||||
Namespace: dataKey.Namespace,
|
||||
Label: dataKey.Label,
|
||||
Provider: dataKey.Provider,
|
||||
EncryptedData: dataKey.EncryptedData,
|
||||
Active: dataKey.Active,
|
||||
Created: dataKey.Created,
|
||||
Updated: dataKey.Updated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ss *encryptionStoreImpl) GetCurrentDataKey(ctx context.Context, namespace, label string) (*contracts.SecretDataKey, error) {
|
||||
ctx, span := ss.tracer.Start(ctx, "DataKeyStorage.GetCurrentDataKey", trace.WithAttributes(
|
||||
attribute.String("namespace", namespace),
|
||||
attribute.String("label", label),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
req := readCurrentDataKey{
|
||||
SQLTemplate: sqltemplate.New(ss.dialect),
|
||||
Namespace: namespace,
|
||||
Label: label,
|
||||
}
|
||||
|
||||
query, err := sqltemplate.Execute(sqlDataKeyReadCurrent, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlDataKeyReadCurrent.Name(), err)
|
||||
}
|
||||
|
||||
res, err := ss.db.QueryContext(ctx, query, req.GetArgs()...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting current data key row: %w", err)
|
||||
}
|
||||
defer func() { _ = res.Close() }()
|
||||
|
||||
if !res.Next() {
|
||||
return nil, contracts.ErrDataKeyNotFound
|
||||
}
|
||||
|
||||
var dataKey SecretDataKey
|
||||
err = res.Scan(
|
||||
&dataKey.UID,
|
||||
&dataKey.Namespace,
|
||||
&dataKey.Label,
|
||||
&dataKey.Provider,
|
||||
&dataKey.EncryptedData,
|
||||
&dataKey.Active,
|
||||
&dataKey.Created,
|
||||
&dataKey.Updated,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to scan data key row: %w", err)
|
||||
}
|
||||
if err := res.Err(); err != nil {
|
||||
return nil, fmt.Errorf("read rows error: %w", err)
|
||||
}
|
||||
|
||||
return &contracts.SecretDataKey{
|
||||
UID: dataKey.UID,
|
||||
Namespace: dataKey.Namespace,
|
||||
Label: dataKey.Label,
|
||||
Provider: dataKey.Provider,
|
||||
EncryptedData: dataKey.EncryptedData,
|
||||
Active: dataKey.Active,
|
||||
Created: dataKey.Created,
|
||||
Updated: dataKey.Updated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ss *encryptionStoreImpl) GetAllDataKeys(ctx context.Context, namespace string) ([]*contracts.SecretDataKey, error) {
|
||||
ctx, span := ss.tracer.Start(ctx, "DataKeyStorage.GetAllDataKeys", trace.WithAttributes(
|
||||
attribute.String("namespace", namespace),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
req := listDataKeys{
|
||||
SQLTemplate: sqltemplate.New(ss.dialect),
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
query, err := sqltemplate.Execute(sqlDataKeyList, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlDataKeyList.Name(), err)
|
||||
}
|
||||
|
||||
rows, err := ss.db.QueryContext(ctx, query, req.GetArgs()...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing data keys %q: %w", sqlDataKeyList.Name(), err)
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
dataKeys := make([]*contracts.SecretDataKey, 0)
|
||||
for rows.Next() {
|
||||
var row SecretDataKey
|
||||
err = rows.Scan(
|
||||
&row.UID,
|
||||
&row.Namespace,
|
||||
&row.Label,
|
||||
&row.Provider,
|
||||
&row.EncryptedData,
|
||||
&row.Active,
|
||||
&row.Created,
|
||||
&row.Updated,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading data key row: %w", err)
|
||||
}
|
||||
|
||||
dataKeys = append(dataKeys, &contracts.SecretDataKey{
|
||||
UID: row.UID,
|
||||
Namespace: row.Namespace,
|
||||
Label: row.Label,
|
||||
Provider: row.Provider,
|
||||
EncryptedData: row.EncryptedData,
|
||||
Active: row.Active,
|
||||
Created: row.Created,
|
||||
Updated: row.Updated,
|
||||
})
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("read rows error: %w", err)
|
||||
}
|
||||
|
||||
return dataKeys, nil
|
||||
}
|
||||
|
||||
func (ss *encryptionStoreImpl) CreateDataKey(ctx context.Context, dataKey *contracts.SecretDataKey) error {
|
||||
ctx, span := ss.tracer.Start(ctx, "DataKeyStorage.CreateDataKey", trace.WithAttributes(
|
||||
attribute.String("uid", dataKey.UID),
|
||||
attribute.String("namespace", dataKey.Namespace),
|
||||
attribute.Bool("active", dataKey.Active),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
if !dataKey.Active {
|
||||
return fmt.Errorf("cannot insert deactivated data keys")
|
||||
}
|
||||
|
||||
dataKey.Created = time.Now()
|
||||
dataKey.Updated = dataKey.Created
|
||||
|
||||
req := createDataKey{
|
||||
SQLTemplate: sqltemplate.New(ss.dialect),
|
||||
Row: dataKey,
|
||||
}
|
||||
|
||||
query, err := sqltemplate.Execute(sqlDataKeyCreate, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("execute template %q: %w", sqlDataKeyCreate.Name(), err)
|
||||
}
|
||||
|
||||
result, err := ss.db.ExecContext(ctx, query, req.GetArgs()...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting data key row: %w", err)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting rows affected: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected != 1 {
|
||||
return fmt.Errorf("expected 1 row affected, but affected %d", rowsAffected)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *encryptionStoreImpl) DisableDataKeys(ctx context.Context, namespace string) error {
|
||||
ctx, span := ss.tracer.Start(ctx, "DataKeyStorage.DisableDataKeys", trace.WithAttributes(
|
||||
attribute.String("namespace", namespace),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
req := disableDataKeys{
|
||||
SQLTemplate: sqltemplate.New(ss.dialect),
|
||||
Namespace: namespace,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
query, err := sqltemplate.Execute(sqlDataKeyDisable, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("execute template %q: %w", sqlDataKeyDisable.Name(), err)
|
||||
}
|
||||
|
||||
result, err := ss.db.ExecContext(ctx, query, req.GetArgs()...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("updating data key row: %w", err)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting rows affected: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected != 1 {
|
||||
return fmt.Errorf("expected 1 row affected, but affected %d", rowsAffected)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *encryptionStoreImpl) DeleteDataKey(ctx context.Context, namespace, uid string) error {
|
||||
ctx, span := ss.tracer.Start(ctx, "DataKeyStorage.DeleteDataKey", trace.WithAttributes(
|
||||
attribute.String("uid", uid),
|
||||
attribute.String("namespace", namespace),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
if len(uid) == 0 {
|
||||
return fmt.Errorf("data key id is missing")
|
||||
}
|
||||
|
||||
req := deleteDataKey{
|
||||
SQLTemplate: sqltemplate.New(ss.dialect),
|
||||
Namespace: namespace,
|
||||
UID: uid,
|
||||
}
|
||||
|
||||
query, err := sqltemplate.Execute(sqlDataKeyDelete, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("execute template %q: %w", sqlDataKeyDelete.Name(), err)
|
||||
}
|
||||
|
||||
result, err := ss.db.ExecContext(ctx, query, req.GetArgs()...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting data key is %s in namespace %s: %w", uid, namespace, err)
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting rows affected: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected != 1 {
|
||||
return fmt.Errorf("bug: deleted more than one row from the data key table, should delete only one at a time: deleted=%v", rowsAffected)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package encryption
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/storage/secret/database"
|
||||
"github.com/grafana/grafana/pkg/storage/secret/migrator"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testsuite.Run(m)
|
||||
}
|
||||
|
||||
const (
|
||||
passThroughProvider encryption.ProviderID = "PASS_THROUGH_PROVIDER"
|
||||
base64Provider encryption.ProviderID = "BASE64_PROVIDER"
|
||||
)
|
||||
|
||||
func TestEncryptionStoreImpl_DataKeyLifecycle(t *testing.T) {
|
||||
// Initialize data key storage with a fake db
|
||||
testDB := sqlstore.NewTestStore(t, sqlstore.WithMigrator(migrator.New()))
|
||||
tracer := noop.NewTracerProvider().Tracer("test")
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, featuremgmt.FlagSecretsManagementAppPlatform)
|
||||
store, err := ProvideDataKeyStorage(database.ProvideDatabase(testDB), tracer, features, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Define a data key whose lifecycle we will test
|
||||
dataKey := &contracts.SecretDataKey{
|
||||
UID: "test-uid",
|
||||
Namespace: "test-namespace",
|
||||
Label: "test-label",
|
||||
Active: true,
|
||||
EncryptedData: []byte("test-data"),
|
||||
Provider: passThroughProvider, // so that the Decrypt() method just gets us the input test data
|
||||
}
|
||||
|
||||
// Define a second data key in a different namespace that should remain undisturbed
|
||||
unchangingDataKey := &contracts.SecretDataKey{
|
||||
UID: "static-uid",
|
||||
Namespace: "static-namespace",
|
||||
Label: "static-label",
|
||||
Active: true,
|
||||
EncryptedData: []byte("static-data"),
|
||||
}
|
||||
|
||||
// Test CreateDataKey
|
||||
err = store.CreateDataKey(ctx, dataKey)
|
||||
require.NoError(t, err)
|
||||
err = store.CreateDataKey(ctx, unchangingDataKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test GetDataKey
|
||||
retrievedKey, err := store.GetDataKey(ctx, "test-namespace", "test-uid")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dataKey.UID, retrievedKey.UID)
|
||||
require.Equal(t, dataKey.Namespace, retrievedKey.Namespace)
|
||||
|
||||
// Test GetCurrentDataKey
|
||||
currentKey, err := store.GetCurrentDataKey(ctx, "test-namespace", "test-label")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dataKey.UID, currentKey.UID)
|
||||
require.Equal(t, dataKey.Namespace, currentKey.Namespace)
|
||||
|
||||
// Test GetAllDataKeys
|
||||
allKeys, err := store.GetAllDataKeys(ctx, "test-namespace")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, allKeys, 1)
|
||||
require.Equal(t, dataKey.UID, allKeys[0].UID)
|
||||
|
||||
// Test DisableDataKeys
|
||||
err = store.DisableDataKeys(ctx, "test-namespace")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify that the data key is disabled
|
||||
disabledKey, err := store.GetDataKey(ctx, "test-namespace", "test-uid")
|
||||
require.NoError(t, err)
|
||||
require.False(t, disabledKey.Active)
|
||||
|
||||
// Test DeleteDataKey
|
||||
err = store.DeleteDataKey(ctx, "test-namespace", "test-uid")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify that the data key is deleted
|
||||
_, err = store.GetDataKey(ctx, "test-namespace", "test-uid")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, contracts.ErrDataKeyNotFound, err)
|
||||
|
||||
// Verify that the unchanging data key still exists and is active
|
||||
staticKey, err := store.GetDataKey(ctx, "static-namespace", "static-uid")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, unchangingDataKey.UID, staticKey.UID)
|
||||
require.Equal(t, unchangingDataKey.Namespace, staticKey.Namespace)
|
||||
require.True(t, staticKey.Active)
|
||||
}
|
||||
|
||||
type PassThroughEncryptionProvider struct{}
|
||||
|
||||
func (d *PassThroughEncryptionProvider) Encrypt(ctx context.Context, blob []byte) ([]byte, error) {
|
||||
return blob, nil
|
||||
}
|
||||
|
||||
func (d *PassThroughEncryptionProvider) Decrypt(ctx context.Context, blob []byte) ([]byte, error) {
|
||||
return blob, nil
|
||||
}
|
||||
|
||||
type Base64EncryptionProvider struct{}
|
||||
|
||||
func (d *Base64EncryptionProvider) Encrypt(ctx context.Context, blob []byte) ([]byte, error) {
|
||||
r := base64.RawStdEncoding.EncodeToString(blob)
|
||||
return []byte(r), nil
|
||||
}
|
||||
|
||||
func (d *Base64EncryptionProvider) Decrypt(ctx context.Context, blob []byte) ([]byte, error) {
|
||||
r, err := base64.RawStdEncoding.DecodeString(string(blob))
|
||||
return r, err
|
||||
}
|
||||
|
|
@ -4,7 +4,9 @@ import (
|
|||
"embed"
|
||||
"fmt"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
|
|
@ -19,6 +21,13 @@ var (
|
|||
sqlEncryptedValueRead = mustTemplate("encrypted_value_read.sql")
|
||||
sqlEncryptedValueUpdate = mustTemplate("encrypted_value_update.sql")
|
||||
sqlEncryptedValueDelete = mustTemplate("encrypted_value_delete.sql")
|
||||
|
||||
sqlDataKeyCreate = mustTemplate("data_key_create.sql")
|
||||
sqlDataKeyRead = mustTemplate("data_key_read.sql")
|
||||
sqlDataKeyReadCurrent = mustTemplate("data_key_read_current.sql")
|
||||
sqlDataKeyList = mustTemplate("data_key_list.sql")
|
||||
sqlDataKeyDisable = mustTemplate("data_key_disable.sql")
|
||||
sqlDataKeyDelete = mustTemplate("data_key_delete.sql")
|
||||
)
|
||||
|
||||
// TODO: Move this to a common place so that all stores can use
|
||||
|
|
@ -79,3 +88,52 @@ type deleteEncryptedValue struct {
|
|||
func (r deleteEncryptedValue) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/**-- Data Key Queries --**/
|
||||
/*************************************/
|
||||
type createDataKey struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Row *contracts.SecretDataKey
|
||||
}
|
||||
|
||||
func (r createDataKey) Validate() error { return nil }
|
||||
|
||||
type readDataKey struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Namespace string
|
||||
UID string
|
||||
}
|
||||
|
||||
func (r readDataKey) Validate() error { return nil }
|
||||
|
||||
type readCurrentDataKey struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Namespace string
|
||||
Label string
|
||||
}
|
||||
|
||||
func (r readCurrentDataKey) Validate() error { return nil }
|
||||
|
||||
type listDataKeys struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (r listDataKeys) Validate() error { return nil }
|
||||
|
||||
type disableDataKeys struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Namespace string
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
func (r disableDataKeys) Validate() error { return nil }
|
||||
|
||||
type deleteDataKey struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Namespace string
|
||||
UID string
|
||||
}
|
||||
|
||||
func (r deleteDataKey) Validate() error { return nil }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package encryption
|
|||
import (
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate/mocks"
|
||||
)
|
||||
|
||||
|
|
@ -61,3 +63,98 @@ func TestEncryptedValueQueries(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestDataKeyQueries(t *testing.T) {
|
||||
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
|
||||
RootDir: "testdata",
|
||||
Templates: map[*template.Template][]mocks.TemplateTestCase{
|
||||
sqlDataKeyCreate: {
|
||||
{
|
||||
Name: "create",
|
||||
Data: &createDataKey{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Row: &contracts.SecretDataKey{
|
||||
UID: "abc123",
|
||||
Active: true,
|
||||
Namespace: "ns",
|
||||
Label: "label",
|
||||
Provider: "provider",
|
||||
EncryptedData: []byte("secret"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "create-not-active",
|
||||
Data: &createDataKey{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Row: &contracts.SecretDataKey{
|
||||
UID: "abc123",
|
||||
Active: false,
|
||||
Namespace: "ns",
|
||||
Label: "label",
|
||||
Provider: "provider",
|
||||
EncryptedData: []byte("secret"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sqlDataKeyRead: {
|
||||
{
|
||||
Name: "read",
|
||||
Data: &readDataKey{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Namespace: "ns",
|
||||
UID: "abc123",
|
||||
},
|
||||
},
|
||||
},
|
||||
sqlDataKeyReadCurrent: {
|
||||
{
|
||||
Name: "read_current",
|
||||
Data: &readCurrentDataKey{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Namespace: "ns",
|
||||
Label: "label",
|
||||
},
|
||||
},
|
||||
},
|
||||
sqlDataKeyList: {
|
||||
{
|
||||
Name: "list",
|
||||
Data: &listDataKeys{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
},
|
||||
sqlDataKeyDisable: {
|
||||
{
|
||||
Name: "disable",
|
||||
Data: &disableDataKeys{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Namespace: "ns",
|
||||
Updated: time.Unix(1735689600, 0).UTC(),
|
||||
},
|
||||
},
|
||||
},
|
||||
sqlDataKeyDelete: {
|
||||
{
|
||||
Name: "delete",
|
||||
Data: &deleteDataKey{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Namespace: "ns",
|
||||
UID: "abc123",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "delete-no-uid",
|
||||
Data: &deleteDataKey{
|
||||
SQLTemplate: mocks.NewTestingSQLTemplate(),
|
||||
Namespace: "ns",
|
||||
UID: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
19
pkg/storage/secret/encryption/testdata/mysql--data_key_create-create-not-active.sql
vendored
Executable file
19
pkg/storage/secret/encryption/testdata/mysql--data_key_create-create-not-active.sql
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
INSERT INTO `secret_data_key` (
|
||||
`uid`,
|
||||
`namespace`,
|
||||
`label`,
|
||||
`provider`,
|
||||
`encrypted_data`,
|
||||
`active`,
|
||||
`created`,
|
||||
`updated`
|
||||
) VALUES (
|
||||
'abc123',
|
||||
'ns',
|
||||
'label',
|
||||
'provider',
|
||||
'[115 101 99 114 101 116]',
|
||||
FALSE,
|
||||
'0001-01-01 00:00:00 +0000 UTC',
|
||||
'0001-01-01 00:00:00 +0000 UTC'
|
||||
);
|
||||
19
pkg/storage/secret/encryption/testdata/mysql--data_key_create-create.sql
vendored
Executable file
19
pkg/storage/secret/encryption/testdata/mysql--data_key_create-create.sql
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
INSERT INTO `secret_data_key` (
|
||||
`uid`,
|
||||
`namespace`,
|
||||
`label`,
|
||||
`provider`,
|
||||
`encrypted_data`,
|
||||
`active`,
|
||||
`created`,
|
||||
`updated`
|
||||
) VALUES (
|
||||
'abc123',
|
||||
'ns',
|
||||
'label',
|
||||
'provider',
|
||||
'[115 101 99 114 101 116]',
|
||||
TRUE,
|
||||
'0001-01-01 00:00:00 +0000 UTC',
|
||||
'0001-01-01 00:00:00 +0000 UTC'
|
||||
);
|
||||
4
pkg/storage/secret/encryption/testdata/mysql--data_key_delete-delete-no-uid.sql
vendored
Executable file
4
pkg/storage/secret/encryption/testdata/mysql--data_key_delete-delete-no-uid.sql
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
DELETE FROM `secret_data_key`
|
||||
WHERE `namespace` = 'ns' AND
|
||||
`uid` = ''
|
||||
;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
DELETE FROM `secret_data_key`
|
||||
WHERE `namespace` = 'ns' AND
|
||||
`uid` = 'abc123'
|
||||
;
|
||||
8
pkg/storage/secret/encryption/testdata/mysql--data_key_disable-disable.sql
vendored
Executable file
8
pkg/storage/secret/encryption/testdata/mysql--data_key_disable-disable.sql
vendored
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
UPDATE
|
||||
`secret_data_key`
|
||||
SET
|
||||
`active` = false,
|
||||
`updated` = '2025-01-01 00:00:00 +0000 UTC'
|
||||
WHERE `namespace` = 'ns' AND
|
||||
`active` = true
|
||||
;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
SELECT
|
||||
`uid`,
|
||||
`namespace`,
|
||||
`label`,
|
||||
`provider`,
|
||||
`encrypted_data`,
|
||||
`active`,
|
||||
`created`,
|
||||
`updated`
|
||||
FROM
|
||||
`secret_data_key`
|
||||
WHERE `namespace` = 'ns'
|
||||
;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
SELECT
|
||||
`uid`,
|
||||
`namespace`,
|
||||
`label`,
|
||||
`provider`,
|
||||
`encrypted_data`,
|
||||
`active`,
|
||||
`created`,
|
||||
`updated`
|
||||
FROM
|
||||
`secret_data_key`
|
||||
WHERE `namespace` = 'ns' AND
|
||||
`uid` = 'abc123'
|
||||
;
|
||||
15
pkg/storage/secret/encryption/testdata/mysql--data_key_read_current-read_current.sql
vendored
Executable file
15
pkg/storage/secret/encryption/testdata/mysql--data_key_read_current-read_current.sql
vendored
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
SELECT
|
||||
`uid`,
|
||||
`namespace`,
|
||||
`label`,
|
||||
`provider`,
|
||||
`encrypted_data`,
|
||||
`active`,
|
||||
`created`,
|
||||
`updated`
|
||||
FROM
|
||||
`secret_data_key`
|
||||
WHERE `namespace` = 'ns' AND
|
||||
`label` = 'label' AND
|
||||
`active` = true
|
||||
;
|
||||
19
pkg/storage/secret/encryption/testdata/postgres--data_key_create-create-not-active.sql
vendored
Executable file
19
pkg/storage/secret/encryption/testdata/postgres--data_key_create-create-not-active.sql
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
INSERT INTO "secret_data_key" (
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
) VALUES (
|
||||
'abc123',
|
||||
'ns',
|
||||
'label',
|
||||
'provider',
|
||||
'[115 101 99 114 101 116]',
|
||||
FALSE,
|
||||
'0001-01-01 00:00:00 +0000 UTC',
|
||||
'0001-01-01 00:00:00 +0000 UTC'
|
||||
);
|
||||
19
pkg/storage/secret/encryption/testdata/postgres--data_key_create-create.sql
vendored
Executable file
19
pkg/storage/secret/encryption/testdata/postgres--data_key_create-create.sql
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
INSERT INTO "secret_data_key" (
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
) VALUES (
|
||||
'abc123',
|
||||
'ns',
|
||||
'label',
|
||||
'provider',
|
||||
'[115 101 99 114 101 116]',
|
||||
TRUE,
|
||||
'0001-01-01 00:00:00 +0000 UTC',
|
||||
'0001-01-01 00:00:00 +0000 UTC'
|
||||
);
|
||||
4
pkg/storage/secret/encryption/testdata/postgres--data_key_delete-delete-no-uid.sql
vendored
Executable file
4
pkg/storage/secret/encryption/testdata/postgres--data_key_delete-delete-no-uid.sql
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
DELETE FROM "secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"uid" = ''
|
||||
;
|
||||
4
pkg/storage/secret/encryption/testdata/postgres--data_key_delete-delete.sql
vendored
Executable file
4
pkg/storage/secret/encryption/testdata/postgres--data_key_delete-delete.sql
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
DELETE FROM "secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"uid" = 'abc123'
|
||||
;
|
||||
8
pkg/storage/secret/encryption/testdata/postgres--data_key_disable-disable.sql
vendored
Executable file
8
pkg/storage/secret/encryption/testdata/postgres--data_key_disable-disable.sql
vendored
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
UPDATE
|
||||
"secret_data_key"
|
||||
SET
|
||||
"active" = false,
|
||||
"updated" = '2025-01-01 00:00:00 +0000 UTC'
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"active" = true
|
||||
;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
SELECT
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
FROM
|
||||
"secret_data_key"
|
||||
WHERE "namespace" = 'ns'
|
||||
;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
SELECT
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
FROM
|
||||
"secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"uid" = 'abc123'
|
||||
;
|
||||
15
pkg/storage/secret/encryption/testdata/postgres--data_key_read_current-read_current.sql
vendored
Executable file
15
pkg/storage/secret/encryption/testdata/postgres--data_key_read_current-read_current.sql
vendored
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
SELECT
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
FROM
|
||||
"secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"label" = 'label' AND
|
||||
"active" = true
|
||||
;
|
||||
19
pkg/storage/secret/encryption/testdata/sqlite--data_key_create-create-not-active.sql
vendored
Executable file
19
pkg/storage/secret/encryption/testdata/sqlite--data_key_create-create-not-active.sql
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
INSERT INTO "secret_data_key" (
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
) VALUES (
|
||||
'abc123',
|
||||
'ns',
|
||||
'label',
|
||||
'provider',
|
||||
'[115 101 99 114 101 116]',
|
||||
FALSE,
|
||||
'0001-01-01 00:00:00 +0000 UTC',
|
||||
'0001-01-01 00:00:00 +0000 UTC'
|
||||
);
|
||||
19
pkg/storage/secret/encryption/testdata/sqlite--data_key_create-create.sql
vendored
Executable file
19
pkg/storage/secret/encryption/testdata/sqlite--data_key_create-create.sql
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
INSERT INTO "secret_data_key" (
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
) VALUES (
|
||||
'abc123',
|
||||
'ns',
|
||||
'label',
|
||||
'provider',
|
||||
'[115 101 99 114 101 116]',
|
||||
TRUE,
|
||||
'0001-01-01 00:00:00 +0000 UTC',
|
||||
'0001-01-01 00:00:00 +0000 UTC'
|
||||
);
|
||||
4
pkg/storage/secret/encryption/testdata/sqlite--data_key_delete-delete-no-uid.sql
vendored
Executable file
4
pkg/storage/secret/encryption/testdata/sqlite--data_key_delete-delete-no-uid.sql
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
DELETE FROM "secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"uid" = ''
|
||||
;
|
||||
4
pkg/storage/secret/encryption/testdata/sqlite--data_key_delete-delete.sql
vendored
Executable file
4
pkg/storage/secret/encryption/testdata/sqlite--data_key_delete-delete.sql
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
DELETE FROM "secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"uid" = 'abc123'
|
||||
;
|
||||
8
pkg/storage/secret/encryption/testdata/sqlite--data_key_disable-disable.sql
vendored
Executable file
8
pkg/storage/secret/encryption/testdata/sqlite--data_key_disable-disable.sql
vendored
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
UPDATE
|
||||
"secret_data_key"
|
||||
SET
|
||||
"active" = false,
|
||||
"updated" = '2025-01-01 00:00:00 +0000 UTC'
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"active" = true
|
||||
;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
SELECT
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
FROM
|
||||
"secret_data_key"
|
||||
WHERE "namespace" = 'ns'
|
||||
;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
SELECT
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
FROM
|
||||
"secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"uid" = 'abc123'
|
||||
;
|
||||
15
pkg/storage/secret/encryption/testdata/sqlite--data_key_read_current-read_current.sql
vendored
Executable file
15
pkg/storage/secret/encryption/testdata/sqlite--data_key_read_current-read_current.sql
vendored
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
SELECT
|
||||
"uid",
|
||||
"namespace",
|
||||
"label",
|
||||
"provider",
|
||||
"encrypted_data",
|
||||
"active",
|
||||
"created",
|
||||
"updated"
|
||||
FROM
|
||||
"secret_data_key"
|
||||
WHERE "namespace" = 'ns' AND
|
||||
"label" = 'label' AND
|
||||
"active" = true
|
||||
;
|
||||
|
|
@ -15,6 +15,7 @@ const (
|
|||
TableNameKeeper = "secret_keeper"
|
||||
TableNameSecureValue = "secret_secure_value"
|
||||
TableNameSecureValueOutbox = "secret_secure_value_outbox"
|
||||
TableNameDataKey = "secret_data_key"
|
||||
TableNameEncryptedValue = "secret_encrypted_value"
|
||||
)
|
||||
|
||||
|
|
@ -45,31 +46,6 @@ func (*SecretDB) AddMigration(mg *migrator.Migrator) {
|
|||
|
||||
tables := []migrator.Table{}
|
||||
|
||||
tables = append(tables, migrator.Table{
|
||||
Name: TableNameKeeper,
|
||||
Columns: []*migrator.Column{
|
||||
// Kubernetes Metadata
|
||||
{Name: "guid", Type: migrator.DB_NVarchar, Length: 36, IsPrimaryKey: true}, // Fixed size of a UUID.
|
||||
{Name: "name", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Limit enforced by K8s.
|
||||
{Name: "namespace", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Limit enforced by K8s.
|
||||
{Name: "annotations", Type: migrator.DB_Text, Nullable: true},
|
||||
{Name: "labels", Type: migrator.DB_Text, Nullable: true},
|
||||
{Name: "created", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "created_by", Type: migrator.DB_Text, Nullable: false},
|
||||
{Name: "updated", Type: migrator.DB_BigInt, Nullable: false}, // Used as RV (ResourceVersion)
|
||||
{Name: "updated_by", Type: migrator.DB_Text, Nullable: false},
|
||||
|
||||
// Spec
|
||||
{Name: "description", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Chosen arbitrarily, but should be enough.
|
||||
{Name: "type", Type: migrator.DB_Text, Nullable: false},
|
||||
// Each keeper has a different payload so we store the whole thing as a blob.
|
||||
{Name: "payload", Type: migrator.DB_Text, Nullable: true},
|
||||
},
|
||||
Indices: []*migrator.Index{
|
||||
{Cols: []string{"namespace", "name"}, Type: migrator.UniqueIndex},
|
||||
},
|
||||
})
|
||||
|
||||
tables = append(tables, migrator.Table{
|
||||
Name: TableNameSecureValue,
|
||||
Columns: []*migrator.Column{
|
||||
|
|
@ -100,6 +76,48 @@ func (*SecretDB) AddMigration(mg *migrator.Migrator) {
|
|||
},
|
||||
})
|
||||
|
||||
tables = append(tables, migrator.Table{
|
||||
Name: TableNameKeeper,
|
||||
Columns: []*migrator.Column{
|
||||
// Kubernetes Metadata
|
||||
{Name: "guid", Type: migrator.DB_NVarchar, Length: 36, IsPrimaryKey: true}, // Fixed size of a UUID.
|
||||
{Name: "name", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Limit enforced by K8s.
|
||||
{Name: "namespace", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Limit enforced by K8s.
|
||||
{Name: "annotations", Type: migrator.DB_Text, Nullable: true},
|
||||
{Name: "labels", Type: migrator.DB_Text, Nullable: true},
|
||||
{Name: "created", Type: migrator.DB_BigInt, Nullable: false},
|
||||
{Name: "created_by", Type: migrator.DB_Text, Nullable: false},
|
||||
{Name: "updated", Type: migrator.DB_BigInt, Nullable: false}, // Used as RV (ResourceVersion)
|
||||
{Name: "updated_by", Type: migrator.DB_Text, Nullable: false},
|
||||
|
||||
// Spec
|
||||
{Name: "description", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Chosen arbitrarily, but should be enough.
|
||||
{Name: "type", Type: migrator.DB_Text, Nullable: false},
|
||||
// Each keeper has a different payload so we store the whole thing as a blob.
|
||||
{Name: "payload", Type: migrator.DB_Text, Nullable: true},
|
||||
},
|
||||
Indices: []*migrator.Index{
|
||||
{Cols: []string{"namespace", "name"}, Type: migrator.UniqueIndex},
|
||||
},
|
||||
})
|
||||
|
||||
// TODO -- document how the seemingly arbitrary column lengths were chosen
|
||||
// The answer for now is that they come from the legacy secrets service, but it would be good to know that they will still work in the new service
|
||||
tables = append(tables, migrator.Table{
|
||||
Name: TableNameDataKey,
|
||||
Columns: []*migrator.Column{
|
||||
{Name: "uid", Type: migrator.DB_NVarchar, Length: 100, IsPrimaryKey: true},
|
||||
{Name: "namespace", Type: migrator.DB_NVarchar, Length: 253, Nullable: false}, // Limit enforced by K8s.
|
||||
{Name: "label", Type: migrator.DB_NVarchar, Length: 100, IsPrimaryKey: false},
|
||||
{Name: "active", Type: migrator.DB_Bool, Nullable: false},
|
||||
{Name: "provider", Type: migrator.DB_NVarchar, Length: 50, Nullable: false},
|
||||
{Name: "encrypted_data", Type: migrator.DB_Blob, Nullable: false},
|
||||
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
|
||||
{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*migrator.Index{}, // TODO: add indexes based on the queries we make.
|
||||
})
|
||||
|
||||
tables = append(tables, migrator.Table{
|
||||
Name: TableNameEncryptedValue,
|
||||
Columns: []*migrator.Column{
|
||||
|
|
|
|||
Loading…
Reference in New Issue