git.iko.soy/typeable/oauth2-proxy
on master help sign in
409 lines 12 kB history raw
1package validation
3import (
4 "time"
6 "github.com/Bose/minisentinel"
7 "github.com/alicebob/miniredis/v2"
8 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
9 . "github.com/onsi/ginkgo/v2"
10 . "github.com/onsi/gomega"
13var _ = Describe("Sessions", func() {
14 const (
15 idTokenConflictMsg = "id_token claim for header \"X-ID-Token\" requires oauth tokens in sessions. session_cookie_minimal cannot be set"
16 accessTokenConflictMsg = "access_token claim for header \"X-Access-Token\" requires oauth tokens in sessions. session_cookie_minimal cannot be set"
17 cookieRefreshMsg = "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set"
18 )
20 type cookieMinimalTableInput struct {
21 opts *options.Options
22 errStrings []string
23 }
25 DescribeTable("validateSessionCookieMinimal",
26 func(o *cookieMinimalTableInput) {
27 Expect(validateSessionCookieMinimal(o.opts)).To(ConsistOf(o.errStrings))
28 },
29 Entry("No minimal cookie session", &cookieMinimalTableInput{
30 opts: &options.Options{
31 Session: options.SessionOptions{
32 Cookie: options.CookieStoreOptions{
33 Minimal: false,
34 },
35 },
36 },
37 errStrings: []string{},
38 }),
39 Entry("No minimal cookie session & request header has access_token claim", &cookieMinimalTableInput{
40 opts: &options.Options{
41 Session: options.SessionOptions{
42 Cookie: options.CookieStoreOptions{
43 Minimal: false,
44 },
45 },
46 InjectRequestHeaders: []options.Header{
47 {
48 Name: "X-Access-Token",
49 Values: []options.HeaderValue{
50 {
51 ClaimSource: &options.ClaimSource{
52 Claim: "access_token",
53 },
54 },
55 },
56 },
57 },
58 },
59 errStrings: []string{},
60 }),
61 Entry("Minimal cookie session no conflicts", &cookieMinimalTableInput{
62 opts: &options.Options{
63 Session: options.SessionOptions{
64 Cookie: options.CookieStoreOptions{
65 Minimal: true,
66 },
67 },
68 },
69 errStrings: []string{},
70 }),
71 Entry("Request Header id_token conflict", &cookieMinimalTableInput{
72 opts: &options.Options{
73 Session: options.SessionOptions{
74 Cookie: options.CookieStoreOptions{
75 Minimal: true,
76 },
77 },
78 InjectRequestHeaders: []options.Header{
79 {
80 Name: "X-ID-Token",
81 Values: []options.HeaderValue{
82 {
83 ClaimSource: &options.ClaimSource{
84 Claim: "id_token",
85 },
86 },
87 },
88 },
89 },
90 },
91 errStrings: []string{idTokenConflictMsg},
92 }),
93 Entry("Response Header id_token conflict", &cookieMinimalTableInput{
94 opts: &options.Options{
95 Session: options.SessionOptions{
96 Cookie: options.CookieStoreOptions{
97 Minimal: true,
98 },
99 },
100 InjectResponseHeaders: []options.Header{
101 {
102 Name: "X-ID-Token",
103 Values: []options.HeaderValue{
104 {
105 ClaimSource: &options.ClaimSource{
106 Claim: "id_token",
107 },
108 },
109 },
110 },
111 },
112 },
113 errStrings: []string{idTokenConflictMsg},
114 }),
115 Entry("Request Header access_token conflict", &cookieMinimalTableInput{
116 opts: &options.Options{
117 Session: options.SessionOptions{
118 Cookie: options.CookieStoreOptions{
119 Minimal: true,
120 },
121 },
122 InjectRequestHeaders: []options.Header{
123 {
124 Name: "X-Access-Token",
125 Values: []options.HeaderValue{
126 {
127 ClaimSource: &options.ClaimSource{
128 Claim: "access_token",
129 },
130 },
131 },
132 },
133 },
134 },
135 errStrings: []string{accessTokenConflictMsg},
136 }),
137 Entry("CookieRefresh conflict", &cookieMinimalTableInput{
138 opts: &options.Options{
139 Cookie: options.Cookie{
140 Refresh: time.Hour,
141 },
142 Session: options.SessionOptions{
143 Cookie: options.CookieStoreOptions{
144 Minimal: true,
145 },
146 },
147 },
148 errStrings: []string{cookieRefreshMsg},
149 }),
150 Entry("Multiple conflicts", &cookieMinimalTableInput{
151 opts: &options.Options{
152 Session: options.SessionOptions{
153 Cookie: options.CookieStoreOptions{
154 Minimal: true,
155 },
156 },
157 InjectResponseHeaders: []options.Header{
158 {
159 Name: "X-ID-Token",
160 Values: []options.HeaderValue{
161 {
162 ClaimSource: &options.ClaimSource{
163 Claim: "id_token",
164 },
165 },
166 },
167 },
168 },
169 InjectRequestHeaders: []options.Header{
170 {
171 Name: "X-Access-Token",
172 Values: []options.HeaderValue{
173 {
174 ClaimSource: &options.ClaimSource{
175 Claim: "access_token",
176 },
177 },
178 },
179 },
180 },
181 },
182 errStrings: []string{idTokenConflictMsg, accessTokenConflictMsg},
183 }),
184 )
186 const (
187 clusterAndSentinelMsg = "unable to initialize a redis client: options redis-use-sentinel and redis-use-cluster are mutually exclusive"
188 parseWrongSchemeMsg = "unable to initialize a redis client: unable to parse redis url: redis: invalid URL scheme: https"
189 parseWrongFormatMsg = "unable to initialize a redis client: unable to parse redis url: redis: invalid database number: \"wrong\""
190 invalidPasswordSetMsg = "unable to set a redis initialization key: WRONGPASS invalid username-password pair"
191 invalidPasswordDelMsg = "unable to delete the redis initialization key: WRONGPASS invalid username-password pair"
192 unreachableRedisSetMsg = "unable to set a redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
193 unreachableRedisDelMsg = "unable to delete the redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
194 unreachableSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
195 unrechableSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
196 refusedSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: dial tcp 127.0.0.1:65535: connect: connection refused"
197 refusedSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: dial tcp 127.0.0.1:65535: connect: connection refused"
198 )
200 type redisStoreTableInput struct {
201 // miniredis setup details
202 password string
203 useSentinel bool
204 setAddr bool
205 setSentinelAddr bool
206 setMasterName bool
208 opts *options.Options
209 errStrings []string
210 }
212 DescribeTable("validateRedisSessionStore",
213 func(o *redisStoreTableInput) {
214 mr, err := miniredis.Run()
215 Expect(err).ToNot(HaveOccurred())
216 mr.RequireAuth(o.password)
217 defer mr.Close()
219 if o.setAddr && !o.useSentinel {
220 o.opts.Session.Redis.ConnectionURL = "redis://" + mr.Addr()
221 }
223 if o.useSentinel {
224 ms := minisentinel.NewSentinel(mr)
225 Expect(ms.Start()).To(Succeed())
226 defer ms.Close()
228 if o.setSentinelAddr {
229 o.opts.Session.Redis.SentinelConnectionURLs = []string{"redis://" + ms.Addr()}
230 }
231 if o.setMasterName {
232 o.opts.Session.Redis.SentinelMasterName = ms.MasterInfo().Name
233 }
234 }
236 Expect(validateRedisSessionStore(o.opts)).To(ConsistOf(o.errStrings))
237 },
238 Entry("cookie sessions are skipped", &redisStoreTableInput{
239 opts: &options.Options{
240 Session: options.SessionOptions{
241 Type: options.CookieSessionStoreType,
242 },
243 },
244 errStrings: []string{},
245 }),
246 Entry("connect successfully to pure redis", &redisStoreTableInput{
247 setAddr: true,
249 opts: &options.Options{
250 Session: options.SessionOptions{
251 Type: options.RedisSessionStoreType,
252 },
253 },
254 errStrings: []string{},
255 }),
256 Entry("failed redis connection with wrong address", &redisStoreTableInput{
257 opts: &options.Options{
258 Session: options.SessionOptions{
259 Type: options.RedisSessionStoreType,
260 Redis: options.RedisStoreOptions{
261 ConnectionURL: "redis://127.0.0.1:65535",
262 },
263 },
264 },
265 errStrings: []string{unreachableRedisSetMsg, unreachableRedisDelMsg},
266 }),
267 Entry("fail to parse an invalid connection URL with wrong scheme", &redisStoreTableInput{
268 opts: &options.Options{
269 Session: options.SessionOptions{
270 Type: options.RedisSessionStoreType,
271 Redis: options.RedisStoreOptions{
272 ConnectionURL: "https://example.com",
273 },
274 },
275 },
276 errStrings: []string{parseWrongSchemeMsg},
277 }),
278 Entry("fail to parse an invalid connection URL with invalid format", &redisStoreTableInput{
279 opts: &options.Options{
280 Session: options.SessionOptions{
281 Type: options.RedisSessionStoreType,
282 Redis: options.RedisStoreOptions{
283 ConnectionURL: "redis://127.0.0.1:6379/wrong",
284 },
285 },
286 },
287 errStrings: []string{parseWrongFormatMsg},
288 }),
289 Entry("connect successfully to pure redis with password", &redisStoreTableInput{
290 password: "abcdef123",
291 setAddr: true,
293 opts: &options.Options{
294 Session: options.SessionOptions{
295 Type: options.RedisSessionStoreType,
296 Redis: options.RedisStoreOptions{
297 Password: "abcdef123",
298 },
299 },
300 },
301 errStrings: []string{},
302 }),
303 Entry("failed connection with wrong password", &redisStoreTableInput{
304 password: "abcdef123",
305 setAddr: true,
307 opts: &options.Options{
308 Session: options.SessionOptions{
309 Type: options.RedisSessionStoreType,
310 Redis: options.RedisStoreOptions{
311 Password: "zyxwtuv987",
312 },
313 },
314 },
315 errStrings: []string{invalidPasswordSetMsg, invalidPasswordDelMsg},
316 }),
317 Entry("connect successfully to sentinel redis", &redisStoreTableInput{
318 useSentinel: true,
319 setSentinelAddr: true,
320 setMasterName: true,
322 opts: &options.Options{
323 Session: options.SessionOptions{
324 Type: options.RedisSessionStoreType,
325 Redis: options.RedisStoreOptions{
326 UseSentinel: true,
327 },
328 },
329 },
330 errStrings: []string{},
331 }),
332 Entry("connect successfully to sentinel redis with password", &redisStoreTableInput{
333 password: "abcdef123",
334 useSentinel: true,
335 setSentinelAddr: true,
336 setMasterName: true,
338 opts: &options.Options{
339 Session: options.SessionOptions{
340 Type: options.RedisSessionStoreType,
341 Redis: options.RedisStoreOptions{
342 Password: "abcdef123",
343 UseSentinel: true,
344 },
345 },
346 },
347 errStrings: []string{},
348 }),
349 Entry("failed connection to sentinel redis with wrong password", &redisStoreTableInput{
350 password: "abcdef123",
351 useSentinel: true,
352 setSentinelAddr: true,
353 setMasterName: true,
355 opts: &options.Options{
356 Session: options.SessionOptions{
357 Type: options.RedisSessionStoreType,
358 Redis: options.RedisStoreOptions{
359 Password: "zyxwtuv987",
360 UseSentinel: true,
361 },
362 },
363 },
364 errStrings: []string{invalidPasswordSetMsg, invalidPasswordDelMsg},
365 }),
366 Entry("failed connection to sentinel redis with wrong master name", &redisStoreTableInput{
367 useSentinel: true,
368 setSentinelAddr: true,
370 opts: &options.Options{
371 Session: options.SessionOptions{
372 Type: options.RedisSessionStoreType,
373 Redis: options.RedisStoreOptions{
374 UseSentinel: true,
375 SentinelMasterName: "WRONG",
376 },
377 },
378 },
379 errStrings: []string{unreachableSentinelSetMsg, unrechableSentinelDelMsg},
380 }),
381 Entry("failed connection to sentinel redis with wrong address", &redisStoreTableInput{
382 useSentinel: true,
383 setMasterName: true,
385 opts: &options.Options{
386 Session: options.SessionOptions{
387 Type: options.RedisSessionStoreType,
388 Redis: options.RedisStoreOptions{
389 UseSentinel: true,
390 SentinelConnectionURLs: []string{"redis://127.0.0.1:65535"},
391 },
392 },
393 },
394 errStrings: []string{refusedSentinelSetMsg, refusedSentinelDelMsg},
395 }),
396 Entry("sentinel and cluster both enabled fails", &redisStoreTableInput{
397 opts: &options.Options{
398 Session: options.SessionOptions{
399 Type: options.RedisSessionStoreType,
400 Redis: options.RedisStoreOptions{
401 UseSentinel: true,
402 UseCluster: true,
403 },
404 },
405 },
406 errStrings: []string{clusterAndSentinelMsg},
407 }),
408 )
409})