ContainerImage.Pinniped/internal/controller/supervisorconfig/generator/secret_helper.go
aram price e03e344dcd SecretHelper depends less on OIDCProvider
This should allow the helper to be more generic so that it can be used
with the SupervisorSecretsController
2020-12-14 19:35:45 -08:00

122 lines
3.5 KiB
Go

// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package generator
import (
"fmt"
"io"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
configv1alpha1 "go.pinniped.dev/generated/1.19/apis/supervisor/config/v1alpha1"
)
// SecretHelper describes an object that can Generate() a Secret and determine whether a Secret
// IsValid(). It can also be Notify()'d about a Secret being persisted.
//
// A SecretHelper has a NamePrefix() that can be used to identify it from other SecretHelper instances.
type SecretHelper interface {
NamePrefix() string
Generate(*configv1alpha1.OIDCProvider) (*corev1.Secret, error)
IsValid(*configv1alpha1.OIDCProvider, *corev1.Secret) bool
Notify(*configv1alpha1.OIDCProvider, *corev1.Secret)
}
const (
// SymmetricSecretType is corev1.Secret.Type of all corev1.Secret's generated by this helper.
SymmetricSecretType = "secrets.pinniped.dev/symmetric"
// SymmetricSecretDataKey is the corev1.Secret.Data key for the symmetric key value generated by this helper.
SymmetricSecretDataKey = "key"
// symmetricKeySize is the default length, in bytes, of generated keys. It is set to 32 since this
// seems like reasonable entropy for our keys, and a 32-byte key will allow for AES-256
// to be used in our codecs (see dynamiccodec.Codec).
symmetricKeySize = 32
)
// New returns a SecretHelper that has been parameterized with common symmetric secret generation
// knobs.
func NewSymmetricSecretHelper(
namePrefix string,
labels map[string]string,
rand io.Reader,
updateCacheFunc func(cacheKey string, cacheValue []byte),
) SecretHelper {
return &symmetricSecretHelper{
namePrefix: namePrefix,
labels: labels,
rand: rand,
updateCacheFunc: updateCacheFunc,
}
}
type symmetricSecretHelper struct {
namePrefix string
labels map[string]string
rand io.Reader
updateCacheFunc func(cacheKey string, cacheValue []byte)
}
func (s *symmetricSecretHelper) NamePrefix() string { return s.namePrefix }
// Generate implements SecretHelper.Generate().
func (s *symmetricSecretHelper) Generate(parent *configv1alpha1.OIDCProvider) (*corev1.Secret, error) {
key := make([]byte, symmetricKeySize)
if _, err := s.rand.Read(key); err != nil {
return nil, err
}
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s%s", s.namePrefix, parent.UID),
Namespace: parent.Namespace,
Labels: s.labels,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(parent, schema.GroupVersionKind{
Group: configv1alpha1.SchemeGroupVersion.Group,
Version: configv1alpha1.SchemeGroupVersion.Version,
Kind: "OIDCProvider",
}),
},
},
Type: SymmetricSecretType,
Data: map[string][]byte{
SymmetricSecretDataKey: key,
},
}, nil
}
// IsValid implements SecretHelper.IsValid().
func (s *symmetricSecretHelper) IsValid(parent *configv1alpha1.OIDCProvider, secret *corev1.Secret) bool {
if !metav1.IsControlledBy(secret, parent) {
return false
}
if secret.Type != SymmetricSecretType {
return false
}
key, ok := secret.Data[SymmetricSecretDataKey]
if !ok {
return false
}
if len(key) != symmetricKeySize {
return false
}
return true
}
// Notify implements SecretHelper.Notify().
func (s *symmetricSecretHelper) Notify(op *configv1alpha1.OIDCProvider, secret *corev1.Secret) {
var cacheKey string
if op != nil {
cacheKey = op.Spec.Issuer
}
s.updateCacheFunc(cacheKey, secret.Data[SymmetricSecretDataKey])
}