Secret: add ability to configure extra owner decrypters (#111301)
--------- Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>
This commit is contained in:
co-authored by
Ryan McKinley
Stephanie Hingtgen
parent
b63e3fd3ae
commit
e69cc03ef9
@@ -4,10 +4,12 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
|
||||
@@ -19,143 +21,143 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
|
||||
t.Run("when no auth info is present, it returns false", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when token permissions are empty, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when service identity is empty, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when service identity is empty string, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), " ", []string{})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when permission format is malformed (missing verb), it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when permission verb is not exactly `decrypt`, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:*"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:something"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when permission does not have 2 or 3 parts, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when permission has group that is not `secret.grafana.app`, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"wrong.group/securevalues/invalid:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when permission has resource that is not `securevalues`, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/invalid-resource:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/invalid-resource/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil)
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", nil, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when the allow list is empty, it allows all identities", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"}, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.True(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when the identity doesn't match any allowed decrypters, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"group2"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"group2"}, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"group2"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"group2"}, nil)
|
||||
require.NotEmpty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when the identity matches an allowed decrypter, it returns true", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
// nameless
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"}, nil)
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
|
||||
// named
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "name", []string{"identity"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "name", []string{"identity"}, nil)
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
})
|
||||
@@ -168,77 +170,134 @@ func TestDecryptAuthorizer(t *testing.T) {
|
||||
"wrong.group/securevalues/group2:decrypt",
|
||||
"secret.grafana.app/securevalues/identity:decrypt", // old style of identity+permission
|
||||
})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "name1", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "name1", []string{"identity"}, nil)
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "name2", []string{"identity"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "name2", []string{"identity"}, nil)
|
||||
require.True(t, allowed)
|
||||
require.Equal(t, "identity", identity)
|
||||
})
|
||||
|
||||
t.Run("when empty secure value name with specific permission, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/name:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"}, nil)
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when permission has an extra / but no name, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues/:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"}, nil)
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when the decrypters list is empty, meaning nothing can decrypt the secure value, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "name", []string{})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "name", []string{}, nil)
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when one of decrypters matches the identity, it returns true", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity1", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity1", "identity2", "identity3"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity1", "identity2", "identity3"}, nil)
|
||||
require.Equal(t, "identity1", identity)
|
||||
require.True(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when one of extra owner decrypters matches the identity, it returns true", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity1", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, []ExtraOwnerDecrypter{
|
||||
{
|
||||
Identity: "identity1",
|
||||
Group: "test.grafana.app",
|
||||
},
|
||||
})
|
||||
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{}, []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "test.grafana.app/v1",
|
||||
Kind: "Test",
|
||||
Name: "test",
|
||||
},
|
||||
})
|
||||
require.Equal(t, "identity1", identity)
|
||||
require.True(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when there are extra owner decrypters but it does not match the identity, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity1", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, []ExtraOwnerDecrypter{
|
||||
{
|
||||
Identity: "identity2",
|
||||
Group: "test.grafana.app",
|
||||
},
|
||||
})
|
||||
|
||||
_, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{}, []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "test.grafana.app/v1",
|
||||
Kind: "Test",
|
||||
Name: "test",
|
||||
},
|
||||
})
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when one of extra owner decrypters matches the identity but not the group, it returns false", func(t *testing.T) {
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity1", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, []ExtraOwnerDecrypter{
|
||||
{
|
||||
Identity: "identity1",
|
||||
Group: "wrong.grafana.app",
|
||||
},
|
||||
})
|
||||
|
||||
_, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{}, []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "test.grafana.app/v1",
|
||||
Kind: "Test",
|
||||
Name: "test",
|
||||
},
|
||||
})
|
||||
require.False(t, allowed)
|
||||
})
|
||||
t.Run("permissions must be case-sensitive and return false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
ctx := createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"SECRET.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, defaultNs, "", []string{"identity"}, nil)
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/SECUREVALUES:decrypt"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"identity"}, nil)
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
|
||||
ctx = createAuthContext(context.Background(), defaultNs.String(), "identity", []string{"secret.grafana.app/securevalues:DECRYPT"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"identity"})
|
||||
identity, allowed = authorizer.Authorize(ctx, defaultNs, "", []string{"identity"}, nil)
|
||||
require.Equal(t, "identity", identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
t.Run("when namespace doesn't match the token's, it returns false", func(t *testing.T) {
|
||||
authorizer := ProvideDecryptAuthorizer(tracer)
|
||||
authorizer := ProvideDecryptAuthorizer(tracer, nil)
|
||||
|
||||
ctx := createAuthContext(context.Background(), "namespace1", "identity", []string{"secret.grafana.app/securevalues:decrypt"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "namespace2", "", []string{"identity"})
|
||||
identity, allowed := authorizer.Authorize(ctx, "namespace2", "", []string{"identity"}, nil)
|
||||
require.Empty(t, identity)
|
||||
require.False(t, allowed)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user