2020-07-14 15:02:10 -07:00
package validation
import (
"time"
2020-08-06 15:43:01 -07:00
"github.com/Bose/minisentinel"
"github.com/alicebob/miniredis/v2"
2020-09-30 01:44:42 +09:00
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
2024-07-18 22:41:02 +02:00
. "github.com/onsi/ginkgo/v2"
2020-07-14 15:02:10 -07:00
. "github.com/onsi/gomega"
)
2020-08-06 15:43:01 -07:00
var _ = Describe ( "Sessions" , func ( ) {
2020-07-14 15:02:10 -07:00
const (
2020-07-29 20:10:14 +01:00
idTokenConflictMsg = "id_token claim for header \"X-ID-Token\" requires oauth tokens in sessions. session_cookie_minimal cannot be set"
accessTokenConflictMsg = "access_token claim for header \"X-Access-Token\" requires oauth tokens in sessions. session_cookie_minimal cannot be set"
cookieRefreshMsg = "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set"
2020-07-14 15:02:10 -07:00
)
2020-08-06 15:43:01 -07:00
type cookieMinimalTableInput struct {
2020-07-14 15:02:10 -07:00
opts * options . Options
errStrings [ ] string
2020-08-06 15:43:01 -07:00
}
DescribeTable ( "validateSessionCookieMinimal" ,
func ( o * cookieMinimalTableInput ) {
Expect ( validateSessionCookieMinimal ( o . opts ) ) . To ( ConsistOf ( o . errStrings ) )
} ,
Entry ( "No minimal cookie session" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : false ,
} ,
} ,
} ,
errStrings : [ ] string { } ,
2020-08-06 15:43:01 -07:00
} ) ,
2020-07-29 20:10:14 +01:00
Entry ( "No minimal cookie session & request header has access_token claim" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : false ,
} ,
} ,
2020-07-29 20:10:14 +01:00
InjectRequestHeaders : [ ] options . Header {
{
Name : "X-Access-Token" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "access_token" ,
} ,
} ,
} ,
} ,
} ,
2020-07-14 15:02:10 -07:00
} ,
errStrings : [ ] string { } ,
2020-08-06 15:43:01 -07:00
} ) ,
Entry ( "Minimal cookie session no conflicts" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : true ,
} ,
} ,
} ,
errStrings : [ ] string { } ,
2020-08-06 15:43:01 -07:00
} ) ,
2020-07-29 20:10:14 +01:00
Entry ( "Request Header id_token conflict" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : true ,
} ,
} ,
2020-07-29 20:10:14 +01:00
InjectRequestHeaders : [ ] options . Header {
{
Name : "X-ID-Token" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "id_token" ,
} ,
} ,
} ,
} ,
} ,
2020-07-14 15:02:10 -07:00
} ,
2020-07-29 20:10:14 +01:00
errStrings : [ ] string { idTokenConflictMsg } ,
2020-08-06 15:43:01 -07:00
} ) ,
2020-07-29 20:10:14 +01:00
Entry ( "Response Header id_token conflict" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : true ,
} ,
} ,
2020-07-29 20:10:14 +01:00
InjectResponseHeaders : [ ] options . Header {
{
Name : "X-ID-Token" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "id_token" ,
} ,
} ,
} ,
} ,
} ,
2020-07-14 15:02:10 -07:00
} ,
2020-07-29 20:10:14 +01:00
errStrings : [ ] string { idTokenConflictMsg } ,
2020-08-06 15:43:01 -07:00
} ) ,
2020-07-29 20:10:14 +01:00
Entry ( "Request Header access_token conflict" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : true ,
} ,
} ,
2020-07-29 20:10:14 +01:00
InjectRequestHeaders : [ ] options . Header {
{
Name : "X-Access-Token" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "access_token" ,
} ,
} ,
} ,
} ,
} ,
2020-07-14 15:02:10 -07:00
} ,
2020-07-29 20:10:14 +01:00
errStrings : [ ] string { accessTokenConflictMsg } ,
2020-08-06 15:43:01 -07:00
} ) ,
Entry ( "CookieRefresh conflict" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Cookie : options . Cookie {
Refresh : time . Hour ,
} ,
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : true ,
} ,
} ,
} ,
errStrings : [ ] string { cookieRefreshMsg } ,
2020-08-06 15:43:01 -07:00
} ) ,
Entry ( "Multiple conflicts" , & cookieMinimalTableInput {
2020-07-14 15:02:10 -07:00
opts : & options . Options {
Session : options . SessionOptions {
Cookie : options . CookieStoreOptions {
Minimal : true ,
} ,
} ,
2020-07-29 20:10:14 +01:00
InjectResponseHeaders : [ ] options . Header {
{
Name : "X-ID-Token" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "id_token" ,
} ,
} ,
} ,
} ,
} ,
InjectRequestHeaders : [ ] options . Header {
{
Name : "X-Access-Token" ,
Values : [ ] options . HeaderValue {
{
ClaimSource : & options . ClaimSource {
Claim : "access_token" ,
} ,
} ,
} ,
} ,
} ,
2020-07-14 15:02:10 -07:00
} ,
2020-07-29 20:10:14 +01:00
errStrings : [ ] string { idTokenConflictMsg , accessTokenConflictMsg } ,
2020-08-06 15:43:01 -07:00
} ) ,
)
2020-07-14 15:02:10 -07:00
2020-08-06 15:43:01 -07:00
const (
clusterAndSentinelMsg = "unable to initialize a redis client: options redis-use-sentinel and redis-use-cluster are mutually exclusive"
2020-10-07 19:49:27 +09:00
parseWrongSchemeMsg = "unable to initialize a redis client: unable to parse redis url: redis: invalid URL scheme: https"
parseWrongFormatMsg = "unable to initialize a redis client: unable to parse redis url: redis: invalid database number: \"wrong\""
2020-08-06 15:43:01 -07:00
invalidPasswordSetMsg = "unable to set a redis initialization key: WRONGPASS invalid username-password pair"
invalidPasswordDelMsg = "unable to delete the redis initialization key: WRONGPASS invalid username-password pair"
unreachableRedisSetMsg = "unable to set a redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
unreachableRedisDelMsg = "unable to delete the redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
2025-07-13 14:55:57 -05:00
unreachableSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
unrechableSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
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"
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"
2020-08-06 15:43:01 -07:00
)
type redisStoreTableInput struct {
// miniredis setup details
password string
useSentinel bool
setAddr bool
setSentinelAddr bool
setMasterName bool
opts * options . Options
errStrings [ ] string
2020-07-14 15:02:10 -07:00
}
2020-08-06 15:43:01 -07:00
DescribeTable ( "validateRedisSessionStore" ,
func ( o * redisStoreTableInput ) {
mr , err := miniredis . Run ( )
Expect ( err ) . ToNot ( HaveOccurred ( ) )
mr . RequireAuth ( o . password )
defer mr . Close ( )
if o . setAddr && ! o . useSentinel {
o . opts . Session . Redis . ConnectionURL = "redis://" + mr . Addr ( )
}
if o . useSentinel {
ms := minisentinel . NewSentinel ( mr )
Expect ( ms . Start ( ) ) . To ( Succeed ( ) )
defer ms . Close ( )
if o . setSentinelAddr {
o . opts . Session . Redis . SentinelConnectionURLs = [ ] string { "redis://" + ms . Addr ( ) }
}
if o . setMasterName {
o . opts . Session . Redis . SentinelMasterName = ms . MasterInfo ( ) . Name
}
}
Expect ( validateRedisSessionStore ( o . opts ) ) . To ( ConsistOf ( o . errStrings ) )
} ,
Entry ( "cookie sessions are skipped" , & redisStoreTableInput {
opts : & options . Options {
Session : options . SessionOptions {
Type : options . CookieSessionStoreType ,
} ,
} ,
errStrings : [ ] string { } ,
} ) ,
Entry ( "connect successfully to pure redis" , & redisStoreTableInput {
setAddr : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
} ,
} ,
errStrings : [ ] string { } ,
} ) ,
Entry ( "failed redis connection with wrong address" , & redisStoreTableInput {
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
ConnectionURL : "redis://127.0.0.1:65535" ,
} ,
} ,
} ,
errStrings : [ ] string { unreachableRedisSetMsg , unreachableRedisDelMsg } ,
} ) ,
Entry ( "fail to parse an invalid connection URL with wrong scheme" , & redisStoreTableInput {
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
ConnectionURL : "https://example.com" ,
} ,
} ,
} ,
errStrings : [ ] string { parseWrongSchemeMsg } ,
} ) ,
Entry ( "fail to parse an invalid connection URL with invalid format" , & redisStoreTableInput {
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
ConnectionURL : "redis://127.0.0.1:6379/wrong" ,
} ,
} ,
} ,
errStrings : [ ] string { parseWrongFormatMsg } ,
} ) ,
Entry ( "connect successfully to pure redis with password" , & redisStoreTableInput {
password : "abcdef123" ,
setAddr : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
Password : "abcdef123" ,
} ,
} ,
} ,
errStrings : [ ] string { } ,
} ) ,
Entry ( "failed connection with wrong password" , & redisStoreTableInput {
password : "abcdef123" ,
setAddr : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
Password : "zyxwtuv987" ,
} ,
} ,
} ,
errStrings : [ ] string { invalidPasswordSetMsg , invalidPasswordDelMsg } ,
} ) ,
Entry ( "connect successfully to sentinel redis" , & redisStoreTableInput {
useSentinel : true ,
setSentinelAddr : true ,
setMasterName : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
UseSentinel : true ,
} ,
} ,
} ,
errStrings : [ ] string { } ,
} ) ,
Entry ( "connect successfully to sentinel redis with password" , & redisStoreTableInput {
password : "abcdef123" ,
useSentinel : true ,
setSentinelAddr : true ,
setMasterName : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
Password : "abcdef123" ,
UseSentinel : true ,
} ,
} ,
} ,
errStrings : [ ] string { } ,
} ) ,
Entry ( "failed connection to sentinel redis with wrong password" , & redisStoreTableInput {
password : "abcdef123" ,
useSentinel : true ,
setSentinelAddr : true ,
setMasterName : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
Password : "zyxwtuv987" ,
UseSentinel : true ,
} ,
} ,
} ,
errStrings : [ ] string { invalidPasswordSetMsg , invalidPasswordDelMsg } ,
} ) ,
Entry ( "failed connection to sentinel redis with wrong master name" , & redisStoreTableInput {
useSentinel : true ,
setSentinelAddr : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
UseSentinel : true ,
SentinelMasterName : "WRONG" ,
} ,
} ,
} ,
errStrings : [ ] string { unreachableSentinelSetMsg , unrechableSentinelDelMsg } ,
} ) ,
Entry ( "failed connection to sentinel redis with wrong address" , & redisStoreTableInput {
useSentinel : true ,
setMasterName : true ,
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
UseSentinel : true ,
SentinelConnectionURLs : [ ] string { "redis://127.0.0.1:65535" } ,
} ,
} ,
} ,
2025-07-13 14:55:57 -05:00
errStrings : [ ] string { refusedSentinelSetMsg , refusedSentinelDelMsg } ,
2020-08-06 15:43:01 -07:00
} ) ,
Entry ( "sentinel and cluster both enabled fails" , & redisStoreTableInput {
opts : & options . Options {
Session : options . SessionOptions {
Type : options . RedisSessionStoreType ,
Redis : options . RedisStoreOptions {
UseSentinel : true ,
UseCluster : true ,
} ,
} ,
} ,
errStrings : [ ] string { clusterAndSentinelMsg } ,
} ) ,
)
} )