git.iko.soy/typeable/oauth2-proxy
on master help sign in
335 lines 8.1 kB history raw
1package validation
3import (
4 "os"
5 "strings"
6 "testing"
7 "time"
9 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
10 . "github.com/onsi/gomega"
13func TestValidateCookie(t *testing.T) {
14 alphabet := "abcdefghijklmnopqrstuvwxyz"
16 validName := "_oauth2_proxy"
17 invalidName := "_oauth2;proxy" // Separater character not allowed
18 // 10 times the alphabet should be longer than 256 characters
19 longName := strings.Repeat(alphabet, 10)
20 validSecret := "secretthirtytwobytes+abcdefghijk"
21 invalidSecret := "abcdef" // 6 bytes is not a valid size
22 validBase64Secret := "c2VjcmV0dGhpcnR5dHdvYnl0ZXMrYWJjZGVmZ2hpams" // Base64 encoding of "secretthirtytwobytes+abcdefghijk"
23 invalidBase64Secret := "YWJjZGVmCg" // Base64 encoding of "abcdef"
24 emptyDomains := []string{}
25 domains := []string{
26 "a.localhost",
27 "ba.localhost",
28 "ca.localhost",
29 "cba.localhost",
30 "a.cba.localhost",
31 }
33 // Create a temporary file for the valid secret file test
34 tmpfile, err := os.CreateTemp("", "cookie-secret-test")
35 if err != nil {
36 t.Fatalf("Failed to create temporary file: %v", err)
37 }
38 defer os.Remove(tmpfile.Name())
40 // Write a valid 32-byte secret to the file
41 _, err = tmpfile.Write([]byte(validSecret))
42 if err != nil {
43 t.Fatalf("Failed to write to temporary file: %v", err)
44 }
45 tmpfile.Close()
47 invalidNameMsg := "invalid cookie name: \"_oauth2;proxy\""
48 longNameMsg := "cookie name should be under 256 characters: cookie name is 260 characters"
49 missingSecretMsg := "missing setting: cookie-secret or cookie-secret-file"
50 invalidSecretMsg := "cookie_secret must be 16, 24, or 32 bytes to create an AES cipher, but is 6 bytes"
51 invalidBase64SecretMsg := "cookie_secret must be 16, 24, or 32 bytes to create an AES cipher, but is 10 bytes"
52 refreshLongerThanExpireMsg := "cookie_refresh (\"1h0m0s\") must be less than cookie_expire (\"15m0s\")"
53 invalidSameSiteMsg := "cookie_samesite (\"invalid\") must be one of ['', 'lax', 'strict', 'none']"
55 testCases := []struct {
56 name string
57 cookie options.Cookie
58 errStrings []string
59 }{
60 {
61 name: "with valid configuration",
62 cookie: options.Cookie{
63 Name: validName,
64 Secret: validSecret,
65 Domains: domains,
66 Path: "",
67 Expire: time.Hour,
68 Refresh: 15 * time.Minute,
69 Secure: true,
70 HTTPOnly: false,
71 SameSite: "",
72 },
73 errStrings: []string{},
74 },
75 {
76 name: "with no cookie secret",
77 cookie: options.Cookie{
78 Name: validName,
79 Secret: "",
80 Domains: emptyDomains,
81 Path: "",
82 Expire: time.Hour,
83 Refresh: 15 * time.Minute,
84 Secure: true,
85 HTTPOnly: false,
86 SameSite: "",
87 },
88 errStrings: []string{
89 missingSecretMsg,
90 },
91 },
92 {
93 name: "with an invalid cookie secret",
94 cookie: options.Cookie{
95 Name: validName,
96 Secret: invalidSecret,
97 Domains: emptyDomains,
98 Path: "",
99 Expire: time.Hour,
100 Refresh: 15 * time.Minute,
101 Secure: true,
102 HTTPOnly: false,
103 SameSite: "",
104 },
105 errStrings: []string{
106 invalidSecretMsg,
107 },
108 },
109 {
110 name: "with a valid Base64 secret",
111 cookie: options.Cookie{
112 Name: validName,
113 Secret: validBase64Secret,
114 Domains: emptyDomains,
115 Path: "",
116 Expire: time.Hour,
117 Refresh: 15 * time.Minute,
118 Secure: true,
119 HTTPOnly: false,
120 SameSite: "",
121 },
122 errStrings: []string{},
123 },
124 {
125 name: "with an invalid Base64 secret",
126 cookie: options.Cookie{
127 Name: validName,
128 Secret: invalidBase64Secret,
129 Domains: emptyDomains,
130 Path: "",
131 Expire: time.Hour,
132 Refresh: 15 * time.Minute,
133 Secure: true,
134 HTTPOnly: false,
135 SameSite: "",
136 },
137 errStrings: []string{
138 invalidBase64SecretMsg,
139 },
140 },
141 {
142 name: "with an invalid name",
143 cookie: options.Cookie{
144 Name: invalidName,
145 Secret: validSecret,
146 Domains: emptyDomains,
147 Path: "",
148 Expire: time.Hour,
149 Refresh: 15 * time.Minute,
150 Secure: true,
151 HTTPOnly: false,
152 SameSite: "",
153 },
154 errStrings: []string{
155 invalidNameMsg,
156 },
157 },
158 {
159 name: "with a name that is too long",
160 cookie: options.Cookie{
161 Name: longName,
162 Secret: validSecret,
163 Domains: emptyDomains,
164 Path: "",
165 Expire: time.Hour,
166 Refresh: 15 * time.Minute,
167 Secure: true,
168 HTTPOnly: false,
169 SameSite: "",
170 },
171 errStrings: []string{
172 longNameMsg,
173 },
174 },
175 {
176 name: "with refresh longer than expire",
177 cookie: options.Cookie{
178 Name: validName,
179 Secret: validSecret,
180 Domains: emptyDomains,
181 Path: "",
182 Expire: 15 * time.Minute,
183 Refresh: time.Hour,
184 Secure: true,
185 HTTPOnly: false,
186 SameSite: "",
187 },
188 errStrings: []string{
189 refreshLongerThanExpireMsg,
190 },
191 },
192 {
193 name: "with samesite \"none\"",
194 cookie: options.Cookie{
195 Name: validName,
196 Secret: validSecret,
197 Domains: emptyDomains,
198 Path: "",
199 Expire: time.Hour,
200 Refresh: 15 * time.Minute,
201 Secure: true,
202 HTTPOnly: false,
203 SameSite: "none",
204 },
205 errStrings: []string{},
206 },
207 {
208 name: "with samesite \"lax\"",
209 cookie: options.Cookie{
210 Name: validName,
211 Secret: validSecret,
212 Domains: emptyDomains,
213 Path: "",
214 Expire: time.Hour,
215 Refresh: 15 * time.Minute,
216 Secure: true,
217 HTTPOnly: false,
218 SameSite: "none",
219 },
220 errStrings: []string{},
221 },
222 {
223 name: "with samesite \"strict\"",
224 cookie: options.Cookie{
225 Name: validName,
226 Secret: validSecret,
227 Domains: emptyDomains,
228 Path: "",
229 Expire: time.Hour,
230 Refresh: 15 * time.Minute,
231 Secure: true,
232 HTTPOnly: false,
233 SameSite: "none",
234 },
235 errStrings: []string{},
236 },
237 {
238 name: "with samesite \"invalid\"",
239 cookie: options.Cookie{
240 Name: validName,
241 Secret: validSecret,
242 Domains: emptyDomains,
243 Path: "",
244 Expire: time.Hour,
245 Refresh: 15 * time.Minute,
246 Secure: true,
247 HTTPOnly: false,
248 SameSite: "invalid",
249 },
250 errStrings: []string{
251 invalidSameSiteMsg,
252 },
253 },
254 {
255 name: "with a combination of configuration errors",
256 cookie: options.Cookie{
257 Name: invalidName,
258 Secret: invalidSecret,
259 Domains: domains,
260 Path: "",
261 Expire: 15 * time.Minute,
262 Refresh: time.Hour,
263 Secure: true,
264 HTTPOnly: false,
265 SameSite: "invalid",
266 },
267 errStrings: []string{
268 invalidNameMsg,
269 invalidSecretMsg,
270 refreshLongerThanExpireMsg,
271 invalidSameSiteMsg,
272 },
273 },
274 {
275 name: "with session cookie configuration",
276 cookie: options.Cookie{
277 Name: validName,
278 Secret: validSecret,
279 Domains: domains,
280 Path: "",
281 Expire: 0,
282 Refresh: 15 * time.Minute,
283 Secure: true,
284 HTTPOnly: false,
285 SameSite: "",
286 },
287 errStrings: []string{},
288 },
289 {
290 name: "with valid secret file",
291 cookie: options.Cookie{
292 Name: validName,
293 Secret: "",
294 SecretFile: tmpfile.Name(),
295 Domains: domains,
296 Path: "",
297 Expire: 24 * time.Hour,
298 Refresh: 0,
299 Secure: true,
300 HTTPOnly: true,
301 SameSite: "",
302 },
303 errStrings: []string{},
304 },
305 {
306 name: "with nonexistent secret file",
307 cookie: options.Cookie{
308 Name: validName,
309 Secret: "",
310 SecretFile: "/nonexistent/file.txt",
311 Domains: domains,
312 Path: "",
313 Expire: 24 * time.Hour,
314 Refresh: 0,
315 Secure: true,
316 HTTPOnly: true,
317 SameSite: "",
318 },
319 errStrings: []string{"could not read cookie secret file: /nonexistent/file.txt"},
320 },
321 }
323 for _, tc := range testCases {
324 t.Run(tc.name, func(t *testing.T) {
325 errStrings := validateCookie(tc.cookie)
326 g := NewWithT(t)
328 g.Expect(errStrings).To(ConsistOf(tc.errStrings))
329 // Check domains were sorted to the right lengths
330 for i := 0; i < len(tc.cookie.Domains)-1; i++ {
331 g.Expect(len(tc.cookie.Domains[i])).To(BeNumerically(">=", len(tc.cookie.Domains[i+1])))
332 }
333 })
334 }