@@ -1,14 +1,17 @@
1package validation
1package validation
6+ "github.com/Bose/minisentinel"
7+ "github.com/alicebob/miniredis/v2"
7 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
8 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
9+ . "github.com/onsi/ginkgo"
10+ . "github.com/onsi/ginkgo/extensions/table"
8 . "github.com/onsi/gomega"
11 . "github.com/onsi/gomega"
11−func Test_validateSessionCookieMinimal(t *testing.T) {
14+var _ = Describe("Sessions", func() {
13 passAuthorizationMsg = "pass_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set"
16 passAuthorizationMsg = "pass_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set"
14 setAuthorizationMsg = "set_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set"
17 setAuthorizationMsg = "set_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set"
@@ -16,11 +19,16 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
16 cookieRefreshMsg = "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set"
19 cookieRefreshMsg = "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set"
19− testCases := map[string]struct {
22+ type cookieMinimalTableInput struct {
20 opts *options.Options
23 opts *options.Options
21 errStrings []string
24 errStrings []string
27+ DescribeTable("validateSessionCookieMinimal",
28+ func(o *cookieMinimalTableInput) {
29+ Expect(validateSessionCookieMinimal(o.opts)).To(ConsistOf(o.errStrings))
23− "No minimal cookie session": {
31+ Entry("No minimal cookie session", &cookieMinimalTableInput{
24 opts: &options.Options{
32 opts: &options.Options{
25 Session: options.SessionOptions{
33 Session: options.SessionOptions{
26 Cookie: options.CookieStoreOptions{
34 Cookie: options.CookieStoreOptions{
@@ -29,8 +37,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
31 errStrings: []string{},
39 errStrings: []string{},
33− "No minimal cookie session & passAuthorization": {
41+ Entry("No minimal cookie session & passAuthorization", &cookieMinimalTableInput{
34 opts: &options.Options{
42 opts: &options.Options{
35 Session: options.SessionOptions{
43 Session: options.SessionOptions{
36 Cookie: options.CookieStoreOptions{
44 Cookie: options.CookieStoreOptions{
@@ -40,8 +48,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
40 PassAuthorization: true,
48 PassAuthorization: true,
42 errStrings: []string{},
50 errStrings: []string{},
44− "Minimal cookie session no conflicts": {
52+ Entry("Minimal cookie session no conflicts", &cookieMinimalTableInput{
45 opts: &options.Options{
53 opts: &options.Options{
46 Session: options.SessionOptions{
54 Session: options.SessionOptions{
47 Cookie: options.CookieStoreOptions{
55 Cookie: options.CookieStoreOptions{
@@ -50,8 +58,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
52 errStrings: []string{},
60 errStrings: []string{},
54− "PassAuthorization conflict": {
62+ Entry("PassAuthorization conflict", &cookieMinimalTableInput{
55 opts: &options.Options{
63 opts: &options.Options{
56 Session: options.SessionOptions{
64 Session: options.SessionOptions{
57 Cookie: options.CookieStoreOptions{
65 Cookie: options.CookieStoreOptions{
@@ -61,8 +69,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
61 PassAuthorization: true,
69 PassAuthorization: true,
63 errStrings: []string{passAuthorizationMsg},
71 errStrings: []string{passAuthorizationMsg},
65− "SetAuthorization conflict": {
73+ Entry("SetAuthorization conflict", &cookieMinimalTableInput{
66 opts: &options.Options{
74 opts: &options.Options{
67 Session: options.SessionOptions{
75 Session: options.SessionOptions{
68 Cookie: options.CookieStoreOptions{
76 Cookie: options.CookieStoreOptions{
@@ -72,8 +80,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
72 SetAuthorization: true,
80 SetAuthorization: true,
74 errStrings: []string{setAuthorizationMsg},
82 errStrings: []string{setAuthorizationMsg},
76− "PassAccessToken conflict": {
84+ Entry("PassAccessToken conflict", &cookieMinimalTableInput{
77 opts: &options.Options{
85 opts: &options.Options{
78 Session: options.SessionOptions{
86 Session: options.SessionOptions{
79 Cookie: options.CookieStoreOptions{
87 Cookie: options.CookieStoreOptions{
@@ -83,8 +91,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
83 PassAccessToken: true,
91 PassAccessToken: true,
85 errStrings: []string{passAccessTokenMsg},
93 errStrings: []string{passAccessTokenMsg},
87− "CookieRefresh conflict": {
95+ Entry("CookieRefresh conflict", &cookieMinimalTableInput{
88 opts: &options.Options{
96 opts: &options.Options{
89 Cookie: options.Cookie{
97 Cookie: options.Cookie{
90 Refresh: time.Hour,
98 Refresh: time.Hour,
@@ -96,8 +104,8 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
98 errStrings: []string{cookieRefreshMsg},
106 errStrings: []string{cookieRefreshMsg},
100− "Multiple conflicts": {
108+ Entry("Multiple conflicts", &cookieMinimalTableInput{
101 opts: &options.Options{
109 opts: &options.Options{
102 Session: options.SessionOptions{
110 Session: options.SessionOptions{
103 Cookie: options.CookieStoreOptions{
111 Cookie: options.CookieStoreOptions{
@@ -108,14 +116,228 @@ func Test_validateSessionCookieMinimal(t *testing.T) {
108 PassAccessToken: true,
116 PassAccessToken: true,
110 errStrings: []string{passAuthorizationMsg, passAccessTokenMsg},
118 errStrings: []string{passAuthorizationMsg, passAccessTokenMsg},
114− for testName, tc := range testCases {
122+ const (
115− t.Run(testName, func(t *testing.T) {
123+ clusterAndSentinelMsg = "unable to initialize a redis client: options redis-use-sentinel and redis-use-cluster are mutually exclusive"
116− errStrings := validateSessionCookieMinimal(tc.opts)
124+ parseWrongSchemeMsg = "unable to initialize a redis client: unable to parse redis url: invalid redis URL scheme: https"
117− g := NewWithT(t)
125+ parseWrongFormatMsg = "unable to initialize a redis client: unable to parse redis url: invalid redis database number: \"wrong\""
118− g.Expect(errStrings).To(ConsistOf(tc.errStrings))
126+ invalidPasswordSetMsg = "unable to set a redis initialization key: WRONGPASS invalid username-password pair"
119− })
127+ invalidPasswordDelMsg = "unable to delete the redis initialization key: WRONGPASS invalid username-password pair"
128+ unreachableRedisSetMsg = "unable to set a redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
129+ unreachableRedisDelMsg = "unable to delete the redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
130+ unreachableSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels are unreachable"
131+ unrechableSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels are unreachable"
134+ type redisStoreTableInput struct {
135+ // miniredis setup details
139+ setSentinelAddr bool
142+ opts *options.Options
146+ DescribeTable("validateRedisSessionStore",
147+ func(o *redisStoreTableInput) {
148+ mr, err := miniredis.Run()
149+ Expect(err).ToNot(HaveOccurred())
150+ mr.RequireAuth(o.password)
153+ if o.setAddr && !o.useSentinel {
154+ o.opts.Session.Redis.ConnectionURL = "redis://" + mr.Addr()
158+ ms := minisentinel.NewSentinel(mr)
159+ Expect(ms.Start()).To(Succeed())
162+ if o.setSentinelAddr {
163+ o.opts.Session.Redis.SentinelConnectionURLs = []string{"redis://" + ms.Addr()}
165+ if o.setMasterName {
166+ o.opts.Session.Redis.SentinelMasterName = ms.MasterInfo().Name
170+ Expect(validateRedisSessionStore(o.opts)).To(ConsistOf(o.errStrings))
172+ Entry("cookie sessions are skipped", &redisStoreTableInput{
173+ opts: &options.Options{
174+ Session: options.SessionOptions{
175+ Type: options.CookieSessionStoreType,
178+ errStrings: []string{},
180+ Entry("connect successfully to pure redis", &redisStoreTableInput{
183+ opts: &options.Options{
184+ Session: options.SessionOptions{
185+ Type: options.RedisSessionStoreType,
188+ errStrings: []string{},
190+ Entry("failed redis connection with wrong address", &redisStoreTableInput{
191+ opts: &options.Options{
192+ Session: options.SessionOptions{
193+ Type: options.RedisSessionStoreType,
194+ Redis: options.RedisStoreOptions{
195+ ConnectionURL: "redis://127.0.0.1:65535",
199+ errStrings: []string{unreachableRedisSetMsg, unreachableRedisDelMsg},
201+ Entry("fail to parse an invalid connection URL with wrong scheme", &redisStoreTableInput{
202+ opts: &options.Options{
203+ Session: options.SessionOptions{
204+ Type: options.RedisSessionStoreType,
205+ Redis: options.RedisStoreOptions{
206+ ConnectionURL: "https://example.com",
210+ errStrings: []string{parseWrongSchemeMsg},
212+ Entry("fail to parse an invalid connection URL with invalid format", &redisStoreTableInput{
213+ opts: &options.Options{
214+ Session: options.SessionOptions{
215+ Type: options.RedisSessionStoreType,
216+ Redis: options.RedisStoreOptions{
217+ ConnectionURL: "redis://127.0.0.1:6379/wrong",
221+ errStrings: []string{parseWrongFormatMsg},
223+ Entry("connect successfully to pure redis with password", &redisStoreTableInput{
224+ password: "abcdef123",
227+ opts: &options.Options{
228+ Session: options.SessionOptions{
229+ Type: options.RedisSessionStoreType,
230+ Redis: options.RedisStoreOptions{
231+ Password: "abcdef123",
235+ errStrings: []string{},
237+ Entry("failed connection with wrong password", &redisStoreTableInput{
238+ password: "abcdef123",
241+ opts: &options.Options{
242+ Session: options.SessionOptions{
243+ Type: options.RedisSessionStoreType,
244+ Redis: options.RedisStoreOptions{
245+ Password: "zyxwtuv987",
249+ errStrings: []string{invalidPasswordSetMsg, invalidPasswordDelMsg},
251+ Entry("connect successfully to sentinel redis", &redisStoreTableInput{
253+ setSentinelAddr: true,
254+ setMasterName: true,
256+ opts: &options.Options{
257+ Session: options.SessionOptions{
258+ Type: options.RedisSessionStoreType,
259+ Redis: options.RedisStoreOptions{
264+ errStrings: []string{},
266+ Entry("connect successfully to sentinel redis with password", &redisStoreTableInput{
267+ password: "abcdef123",
269+ setSentinelAddr: true,
270+ setMasterName: true,
272+ opts: &options.Options{
273+ Session: options.SessionOptions{
274+ Type: options.RedisSessionStoreType,
275+ Redis: options.RedisStoreOptions{
276+ Password: "abcdef123",
281+ errStrings: []string{},
283+ Entry("failed connection to sentinel redis with wrong password", &redisStoreTableInput{
284+ password: "abcdef123",
286+ setSentinelAddr: true,
287+ setMasterName: true,
289+ opts: &options.Options{
290+ Session: options.SessionOptions{
291+ Type: options.RedisSessionStoreType,
292+ Redis: options.RedisStoreOptions{
293+ Password: "zyxwtuv987",
298+ errStrings: []string{invalidPasswordSetMsg, invalidPasswordDelMsg},
300+ Entry("failed connection to sentinel redis with wrong master name", &redisStoreTableInput{
302+ setSentinelAddr: true,
304+ opts: &options.Options{
305+ Session: options.SessionOptions{
306+ Type: options.RedisSessionStoreType,
307+ Redis: options.RedisStoreOptions{
309+ SentinelMasterName: "WRONG",
313+ errStrings: []string{unreachableSentinelSetMsg, unrechableSentinelDelMsg},
315+ Entry("failed connection to sentinel redis with wrong address", &redisStoreTableInput{
317+ setMasterName: true,
319+ opts: &options.Options{
320+ Session: options.SessionOptions{
321+ Type: options.RedisSessionStoreType,
322+ Redis: options.RedisStoreOptions{
324+ SentinelConnectionURLs: []string{"redis://127.0.0.1:65535"},
328+ errStrings: []string{unreachableSentinelSetMsg, unrechableSentinelDelMsg},
330+ Entry("sentinel and cluster both enabled fails", &redisStoreTableInput{
331+ opts: &options.Options{
332+ Session: options.SessionOptions{
333+ Type: options.RedisSessionStoreType,
334+ Redis: options.RedisStoreOptions{
340+ errStrings: []string{clusterAndSentinelMsg},