package couchdb import ( "context" "reflect" "testing" "github.com/stretchr/testify/assert" "sigs.k8s.io/controller-runtime/pkg/client" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) func TestPBKDF2Gen(t *testing.T) { p := []byte("security_first!") u := "my_guy" hashed, err := pbkdf2Hash(p) if err != nil { t.Fatal(err) } if hashed == "" { t.Fail() } ini := string(toAdminIni(u, hashed)) if ini == "" { t.Fail() } } func TestFromSecret_MustPrehash(t *testing.T) { name := "test-secret" namespace := "test-namespace" cl := fake.NewClientBuilder().WithObjects(&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Data: map[string][]byte{ SecretUsername: []byte("edward"), SecretPassword: []byte("p@ssw0rd123"), SecretCookieName: SecretCookieData, }, }).Build() c := &AdminCredentials{} ctx := context.Background() secret, err := c.FromSecret(ctx, cl, types.NamespacedName{ Name: name, Namespace: namespace, }) assert.NoError(t, err) assert.NotNil(t, secret) n := len(c.PrehashedAdmins) // 89 is a magic number that varies based on length of username input if n != 89 { t.Fatalf("prehashed admin creds invalid. len was %d, expected %d", n, 89) } m := adminRegex.Match(c.PrehashedAdmins) if !m { t.Fatalf( "admins.ini was generated with incorrect format. was: %s, must match: %s", c.PrehashedAdmins, adminRegexStr, ) } } func TestFromSecret(t *testing.T) { name := "test-secret" namespace := "test-namespace" username := "edward" password := "p@ssw0rd123" adminsIni := "[admins]\nedward = -pbkdf2-4a4d6fdd0fac2433a9e9b21929b1f906e2cf1642,22387eb3a8e69880,4096\n" cl := fake.NewClientBuilder().WithObjects(&corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Data: map[string][]byte{ SecretUsername: []byte(username), SecretPassword: []byte(password), SecretAdminsIni: []byte(adminsIni), SecretCookieName: SecretCookieData, }, }).Build() c := &AdminCredentials{} ctx := context.Background() secret, err := c.FromSecret(ctx, cl, types.NamespacedName{ Name: name, Namespace: namespace, }) assert.NoError(t, err) assert.NotNil(t, secret) // all secret data is converted to credential if string(c.Username) != username { t.Fatalf("incorrect username. expected %s, got %s", username, c.Username) } if string(c.Password) != password { t.Fatalf("incorrect password. expected %s, got %s", password, c.Password) } if string(c.PrehashedAdmins) != adminsIni { t.Fatalf("incorrect admins. expected %s, got %s", adminsIni, c.PrehashedAdmins) } } func TestToSecret_MustPrehash(t *testing.T) { cl := fake.NewClientBuilder().Build() c := &AdminCredentials{ UsernamePassword: UsernamePassword{ Username: []byte("david"), Password: []byte("p@55w0rd"), }, } ctx := context.Background() nsn := types.NamespacedName{ Name: "test-tosecret", Namespace: "test-tosecret-ns", } s, err := c.ToSecret(ctx, cl, nsn) if err != nil { t.Fatal(err) } m := adminRegex.Match(s.Data[SecretAdminsIni]) if !m { t.Fatalf( "admins.ini was generated with incorrect format. was: %s, must match: %s", s.Data[SecretAdminsIni], adminRegexStr, ) } } func TestToSecret(t *testing.T) { client := fake.NewClientBuilder().Build() username := "edward" password := "p@ssw0rd123" adminsIni := "[admins]\n-pbkdf2-too-long" c := &AdminCredentials{ UsernamePassword: UsernamePassword{ Username: []byte(username), Password: []byte(password), }, PrehashedAdmins: []byte(adminsIni), } ctx := context.Background() nsn := types.NamespacedName{ Name: "test-tosecret", Namespace: "test-tosecret-ns", } s, err := c.ToSecret(ctx, client, nsn) if err != nil { t.Fatal(err) } // all credential data is converted to secret if string(s.Data[SecretUsername]) != username { t.Fatalf("incorrect username. expected %s, got %s", username, s.Data[SecretUsername]) } if string(s.Data[SecretPassword]) != password { t.Fatalf("incorrect password. expected %s, got %s", password, s.Data[SecretPassword]) } if string(s.Data[SecretAdminsIni]) != adminsIni { t.Fatalf("incorrect admins. expected %s, got %s", adminsIni, s.Data[SecretAdminsIni]) } } func TestCredentialsManagerFromSecret(t *testing.T) { ctx := context.Background() cl := fake.NewClientBuilder().Build() hashed, err := pbkdf2Hash(SecretPasswordData) assert.NoError(t, err) testData := map[CredentialsManager]map[string][]byte{ &AdminCredentials{ UsernamePassword: UsernamePassword{ Username: SecretUsernameData, Password: SecretPasswordData, }, PrehashedAdmins: toAdminIni(string(SecretUsernameData), hashed), CookieAuthSecret: SecretCookieData, }: { SecretUsername: SecretUsernameData, SecretPassword: SecretPasswordData, SecretAdminsIni: toAdminIni(string(SecretUsernameData), hashed), SecretCookieName: SecretCookieData, }, &UserCredentials{ UsernamePassword: UsernamePassword{ Username: SecretUsernameData, Password: SecretPasswordData, }, URI: SecretURIData, }: { SecretUsername: SecretUsernameData, SecretPassword: SecretPasswordData, SecretURI: SecretURIData, }, &ReplicationCredentials{ UserCredentials: UserCredentials{ UsernamePassword: UsernamePassword{ Username: SecretUsernameData, Password: SecretPasswordData, }, URI: SecretURIData, }, DBName: SecretDBNameData, }: { SecretUsername: SecretUsernameData, SecretPassword: SecretPasswordData, SecretURI: SecretURIData, SecretDBName: SecretDBNameData, }, } for cred, secretData := range testData { credType := reflect.TypeOf(cred).Elem() credName := credType.Name() newCred := reflect.New(credType).Interface().(CredentialsManager) nsn := types.NamespacedName{ Name: credName + string(randStr(10)), Namespace: "test", } secret, err := cred.ToSecret(ctx, cl, nsn) assert.NoError(t, err, "ToSecret: error caught for %v", nsn) assert.NoError(t, cl.Create(ctx, secret)) secret, err = newCred.FromSecret(ctx, cl, nsn) assert.NoError(t, err, "FromSecret: error caught for %v", nsn) assert.NotNil(t, secret) assert.Equal(t, cred, newCred) assertSecretData(ctx, t, cl, nsn, secretData) } } func assertSecretData(ctx context.Context, t *testing.T, cl client.Client, nn types.NamespacedName, data map[string][]byte) { secret := &corev1.Secret{} err := cl.Get(ctx, nn, secret) assert.NoError(t, err, "fail to get secret: error caught for %v", nn) assert.Equal(t, len(data), len(secret.Data)) for key, value := range secret.Data { assert.Equal(t, string(data[key]), string(value), "error for key: %s, and type %v", key, nn) } }