1 package couchdb
2
3 import (
4 "context"
5 "reflect"
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 "sigs.k8s.io/controller-runtime/pkg/client"
10
11 corev1 "k8s.io/api/core/v1"
12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13 "k8s.io/apimachinery/pkg/types"
14 "sigs.k8s.io/controller-runtime/pkg/client/fake"
15 )
16
17 func TestPBKDF2Gen(t *testing.T) {
18 p := []byte("security_first!")
19 u := "my_guy"
20 hashed, err := pbkdf2Hash(p)
21 if err != nil {
22 t.Fatal(err)
23 }
24 if hashed == "" {
25 t.Fail()
26 }
27 ini := string(toAdminIni(u, hashed))
28 if ini == "" {
29 t.Fail()
30 }
31 }
32
33 func TestFromSecret_MustPrehash(t *testing.T) {
34 name := "test-secret"
35 namespace := "test-namespace"
36 cl := fake.NewClientBuilder().WithObjects(&corev1.Secret{
37 ObjectMeta: metav1.ObjectMeta{
38 Name: name,
39 Namespace: namespace,
40 },
41 Data: map[string][]byte{
42 SecretUsername: []byte("edward"),
43 SecretPassword: []byte("p@ssw0rd123"),
44 SecretCookieName: SecretCookieData,
45 },
46 }).Build()
47
48 c := &AdminCredentials{}
49 ctx := context.Background()
50 secret, err := c.FromSecret(ctx, cl, types.NamespacedName{
51 Name: name,
52 Namespace: namespace,
53 })
54 assert.NoError(t, err)
55 assert.NotNil(t, secret)
56
57 n := len(c.PrehashedAdmins)
58
59 if n != 89 {
60 t.Fatalf("prehashed admin creds invalid. len was %d, expected %d", n, 89)
61 }
62 m := adminRegex.Match(c.PrehashedAdmins)
63 if !m {
64 t.Fatalf(
65 "admins.ini was generated with incorrect format. was: %s, must match: %s",
66 c.PrehashedAdmins,
67 adminRegexStr,
68 )
69 }
70 }
71
72 func TestFromSecret(t *testing.T) {
73 name := "test-secret"
74 namespace := "test-namespace"
75 username := "edward"
76 password := "p@ssw0rd123"
77 adminsIni := "[admins]\nedward = -pbkdf2-4a4d6fdd0fac2433a9e9b21929b1f906e2cf1642,22387eb3a8e69880,4096\n"
78 cl := fake.NewClientBuilder().WithObjects(&corev1.Secret{
79 ObjectMeta: metav1.ObjectMeta{
80 Name: name,
81 Namespace: namespace,
82 },
83 Data: map[string][]byte{
84 SecretUsername: []byte(username),
85 SecretPassword: []byte(password),
86 SecretAdminsIni: []byte(adminsIni),
87 SecretCookieName: SecretCookieData,
88 },
89 }).Build()
90
91 c := &AdminCredentials{}
92 ctx := context.Background()
93 secret, err := c.FromSecret(ctx, cl, types.NamespacedName{
94 Name: name,
95 Namespace: namespace,
96 })
97 assert.NoError(t, err)
98 assert.NotNil(t, secret)
99
100 if string(c.Username) != username {
101 t.Fatalf("incorrect username. expected %s, got %s", username, c.Username)
102 }
103 if string(c.Password) != password {
104 t.Fatalf("incorrect password. expected %s, got %s", password, c.Password)
105 }
106 if string(c.PrehashedAdmins) != adminsIni {
107 t.Fatalf("incorrect admins. expected %s, got %s", adminsIni, c.PrehashedAdmins)
108 }
109 }
110
111 func TestToSecret_MustPrehash(t *testing.T) {
112 cl := fake.NewClientBuilder().Build()
113 c := &AdminCredentials{
114 UsernamePassword: UsernamePassword{
115 Username: []byte("david"),
116 Password: []byte("p@55w0rd"),
117 },
118 }
119 ctx := context.Background()
120 nsn := types.NamespacedName{
121 Name: "test-tosecret",
122 Namespace: "test-tosecret-ns",
123 }
124 s, err := c.ToSecret(ctx, cl, nsn)
125 if err != nil {
126 t.Fatal(err)
127 }
128 m := adminRegex.Match(s.Data[SecretAdminsIni])
129 if !m {
130 t.Fatalf(
131 "admins.ini was generated with incorrect format. was: %s, must match: %s",
132 s.Data[SecretAdminsIni],
133 adminRegexStr,
134 )
135 }
136 }
137
138 func TestToSecret(t *testing.T) {
139 client := fake.NewClientBuilder().Build()
140 username := "edward"
141 password := "p@ssw0rd123"
142 adminsIni := "[admins]\n-pbkdf2-too-long"
143 c := &AdminCredentials{
144 UsernamePassword: UsernamePassword{
145 Username: []byte(username),
146 Password: []byte(password),
147 },
148 PrehashedAdmins: []byte(adminsIni),
149 }
150 ctx := context.Background()
151 nsn := types.NamespacedName{
152 Name: "test-tosecret",
153 Namespace: "test-tosecret-ns",
154 }
155 s, err := c.ToSecret(ctx, client, nsn)
156 if err != nil {
157 t.Fatal(err)
158 }
159
160 if string(s.Data[SecretUsername]) != username {
161 t.Fatalf("incorrect username. expected %s, got %s", username, s.Data[SecretUsername])
162 }
163 if string(s.Data[SecretPassword]) != password {
164 t.Fatalf("incorrect password. expected %s, got %s", password, s.Data[SecretPassword])
165 }
166 if string(s.Data[SecretAdminsIni]) != adminsIni {
167 t.Fatalf("incorrect admins. expected %s, got %s", adminsIni, s.Data[SecretAdminsIni])
168 }
169 }
170
171 func TestCredentialsManagerFromSecret(t *testing.T) {
172 ctx := context.Background()
173 cl := fake.NewClientBuilder().Build()
174 hashed, err := pbkdf2Hash(SecretPasswordData)
175 assert.NoError(t, err)
176 testData := map[CredentialsManager]map[string][]byte{
177 &AdminCredentials{
178 UsernamePassword: UsernamePassword{
179 Username: SecretUsernameData,
180 Password: SecretPasswordData,
181 },
182 PrehashedAdmins: toAdminIni(string(SecretUsernameData), hashed),
183 CookieAuthSecret: SecretCookieData,
184 }: {
185 SecretUsername: SecretUsernameData,
186 SecretPassword: SecretPasswordData,
187 SecretAdminsIni: toAdminIni(string(SecretUsernameData), hashed),
188 SecretCookieName: SecretCookieData,
189 },
190 &UserCredentials{
191 UsernamePassword: UsernamePassword{
192 Username: SecretUsernameData,
193 Password: SecretPasswordData,
194 },
195 URI: SecretURIData,
196 }: {
197 SecretUsername: SecretUsernameData,
198 SecretPassword: SecretPasswordData,
199 SecretURI: SecretURIData,
200 },
201 &ReplicationCredentials{
202 UserCredentials: UserCredentials{
203 UsernamePassword: UsernamePassword{
204 Username: SecretUsernameData,
205 Password: SecretPasswordData,
206 },
207 URI: SecretURIData,
208 },
209 DBName: SecretDBNameData,
210 }: {
211 SecretUsername: SecretUsernameData,
212 SecretPassword: SecretPasswordData,
213 SecretURI: SecretURIData,
214 SecretDBName: SecretDBNameData,
215 },
216 }
217
218 for cred, secretData := range testData {
219 credType := reflect.TypeOf(cred).Elem()
220 credName := credType.Name()
221 newCred := reflect.New(credType).Interface().(CredentialsManager)
222 nsn := types.NamespacedName{
223 Name: credName + string(randStr(10)),
224 Namespace: "test",
225 }
226 secret, err := cred.ToSecret(ctx, cl, nsn)
227 assert.NoError(t, err, "ToSecret: error caught for %v", nsn)
228 assert.NoError(t, cl.Create(ctx, secret))
229
230 secret, err = newCred.FromSecret(ctx, cl, nsn)
231 assert.NoError(t, err, "FromSecret: error caught for %v", nsn)
232 assert.NotNil(t, secret)
233 assert.Equal(t, cred, newCred)
234
235 assertSecretData(ctx, t, cl, nsn, secretData)
236 }
237 }
238
239 func assertSecretData(ctx context.Context, t *testing.T, cl client.Client, nn types.NamespacedName, data map[string][]byte) {
240 secret := &corev1.Secret{}
241 err := cl.Get(ctx, nn, secret)
242 assert.NoError(t, err, "fail to get secret: error caught for %v", nn)
243 assert.Equal(t, len(data), len(secret.Data))
244 for key, value := range secret.Data {
245 assert.Equal(t, string(data[key]), string(value), "error for key: %s, and type %v", key, nn)
246 }
247 }
248
View as plain text