mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2024-11-30 09:16:52 +02:00
Move SessionStore tests to independent package
This commit is contained in:
parent
d9a45a3b47
commit
34137f7305
@ -33,3 +33,9 @@ issues:
|
|||||||
- bodyclose
|
- bodyclose
|
||||||
- unconvert
|
- unconvert
|
||||||
- gocritic
|
- gocritic
|
||||||
|
# If we have tests in shared test folders, these can be less strictly linted
|
||||||
|
- path: tests/.*_tests\.go
|
||||||
|
linters:
|
||||||
|
- golint
|
||||||
|
- bodyclose
|
||||||
|
- stylecheck
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
## Changes since v6.0.0
|
## Changes since v6.0.0
|
||||||
|
|
||||||
|
- [#542](https://github.com/oauth2-proxy/oauth2-proxy/pull/542) Move SessionStore tests to independent package (@JoelSpeed)
|
||||||
- [#577](https://github.com/oauth2-proxy/oauth2-proxy/pull/577) Move Cipher and Session Store initialisation out of Validation (@JoelSpeed)
|
- [#577](https://github.com/oauth2-proxy/oauth2-proxy/pull/577) Move Cipher and Session Store initialisation out of Validation (@JoelSpeed)
|
||||||
|
|
||||||
# v6.0.0
|
# v6.0.0
|
||||||
|
6
go.mod
6
go.mod
@ -4,8 +4,7 @@ go 1.14
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
|
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
|
||||||
github.com/alicebob/miniredis v2.5.0+incompatible // indirect
|
github.com/alicebob/miniredis/v2 v2.13.0
|
||||||
github.com/alicebob/miniredis/v2 v2.11.2
|
|
||||||
github.com/bitly/go-simplejson v0.5.0
|
github.com/bitly/go-simplejson v0.5.0
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||||
@ -15,7 +14,6 @@ require (
|
|||||||
github.com/justinas/alice v1.2.0
|
github.com/justinas/alice v1.2.0
|
||||||
github.com/kr/pretty v0.2.0 // indirect
|
github.com/kr/pretty v0.2.0 // indirect
|
||||||
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa
|
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa
|
||||||
github.com/mdempsky/maligned v0.0.0-20180708014732-6e39bd26a8c8 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2
|
github.com/mitchellh/mapstructure v1.1.2
|
||||||
github.com/onsi/ginkgo v1.12.0
|
github.com/onsi/ginkgo v1.12.0
|
||||||
github.com/onsi/gomega v1.9.0
|
github.com/onsi/gomega v1.9.0
|
||||||
@ -27,8 +25,8 @@ require (
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||||
google.golang.org/api v0.20.0
|
google.golang.org/api v0.20.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
gopkg.in/square/go-jose.v2 v2.4.1
|
gopkg.in/square/go-jose.v2 v2.4.1
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 // indirect
|
|
||||||
)
|
)
|
||||||
|
27
go.sum
27
go.sum
@ -6,17 +6,18 @@ github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWO
|
|||||||
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
|
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
|
||||||
github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
|
github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
|
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
|
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
||||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE=
|
github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE=
|
||||||
github.com/alicebob/miniredis/v2 v2.11.2 h1:OtWO7akm5otuhssnE/sNfsWxG4gZ8DbGhShDtRrByJs=
|
github.com/alicebob/miniredis/v2 v2.13.0 h1:QPosMaxm+r6Qs+YcCtL2Z2a2RSdC9VfXJLpd80l8ICU=
|
||||||
github.com/alicebob/miniredis/v2 v2.11.2/go.mod h1:VL3UDEfAH59bSa7MuHMuFToxkqyHh69s/WUbYlOAuyg=
|
github.com/alicebob/miniredis/v2 v2.13.0/go.mod h1:0UIBNuf97uxrWhdVBpJvPtafKyGpL2NS2pYe0tYM97k=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
@ -70,6 +71,8 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
|
|||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
|
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3 h1:6amM4HsNPOvMLVc2ZnyqrjeQ92YAVWn7T4WBKK87inY=
|
||||||
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
|
github.com/gomodule/redigo v1.8.1 h1:Abmo0bI7Xf0IhdIPc7HZQzZcShdnmxeoVuDDtIQp8N8=
|
||||||
|
github.com/gomodule/redigo v1.8.1/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
@ -117,12 +120,11 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa h1:hI1uC2A3vJFjwvBn0G0a7QBRdBUp6Y048BtLAHRTKPo=
|
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa h1:hI1uC2A3vJFjwvBn0G0a7QBRdBUp6Y048BtLAHRTKPo=
|
||||||
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa/go.mod h1:8vxFeeg++MqgCHwehSuwTlYCF0ALyDJbYJ1JsKi7v6s=
|
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa/go.mod h1:8vxFeeg++MqgCHwehSuwTlYCF0ALyDJbYJ1JsKi7v6s=
|
||||||
github.com/mdempsky/maligned v0.0.0-20180708014732-6e39bd26a8c8 h1:zvpKif6gkrh82wAd2JIffdLyCL52N8r+ABwHxdIOvWM=
|
|
||||||
github.com/mdempsky/maligned v0.0.0-20180708014732-6e39bd26a8c8/go.mod h1:oGVD62YTpMEWw0JqJ2Vl48dzHywJBMlapkfsmhtokOU=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@ -134,7 +136,6 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||||
github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ=
|
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
|
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
|
||||||
@ -157,7 +158,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
@ -188,7 +188,6 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
|||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997 h1:1+FQ4Ns+UZtUiQ4lP0sTCyKSQ0EXoiwAdHZB0Pd5t9Q=
|
github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997 h1:1+FQ4Ns+UZtUiQ4lP0sTCyKSQ0EXoiwAdHZB0Pd5t9Q=
|
||||||
github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997/go.mod h1:DIGbh/f5XMAessMV/uaIik81gkDVjUeQ9ApdaU7wRKE=
|
github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997/go.mod h1:DIGbh/f5XMAessMV/uaIik81gkDVjUeQ9ApdaU7wRKE=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
||||||
@ -210,7 +209,6 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
|||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -223,7 +221,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||||
@ -239,7 +236,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -269,12 +265,8 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b h1:zSzQJAznWxAh9fZxiPy2FZo+ZZEYoYFYYDYdOrU7AaM=
|
|
||||||
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
@ -300,7 +292,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||||
@ -321,5 +312,3 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 h1:kAREL6MPwpsk1/PQPFD3Eg7WAQR5mPTWZJaBiG5LDbY=
|
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc=
|
|
||||||
|
@ -5,9 +5,30 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
||||||
|
sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/tests"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestSessionStore(t *testing.T) {
|
||||||
|
logger.SetOutput(GinkgoWriter)
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Cookie SessionStore")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Describe("Cookie SessionStore Tests", func() {
|
||||||
|
tests.RunSessionStoreTests(
|
||||||
|
func(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessionsapi.SessionStore, error) {
|
||||||
|
// Set the connection URL
|
||||||
|
opts.Type = options.CookieSessionStoreType
|
||||||
|
return NewCookieSessionStore(opts, cookieOpts)
|
||||||
|
}, nil)
|
||||||
|
})
|
||||||
|
|
||||||
func Test_copyCookie(t *testing.T) {
|
func Test_copyCookie(t *testing.T) {
|
||||||
expire, _ := time.Parse(time.RFC3339, "2020-03-17T00:00:00Z")
|
expire, _ := time.Parse(time.RFC3339, "2020-03-17T00:00:00Z")
|
||||||
c := &http.Cookie{
|
c := &http.Cookie{
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Bose/minisentinel"
|
|
||||||
"github.com/alicebob/miniredis/v2"
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRedisStore(t *testing.T) {
|
|
||||||
secret := make([]byte, 32)
|
|
||||||
_, err := rand.Read(secret)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("save session on redis standalone", func(t *testing.T) {
|
|
||||||
redisServer, err := miniredis.Run()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer redisServer.Close()
|
|
||||||
opts := options.NewOptions()
|
|
||||||
redisURL := url.URL{
|
|
||||||
Scheme: "redis",
|
|
||||||
Host: redisServer.Addr(),
|
|
||||||
}
|
|
||||||
opts.Session.Redis.ConnectionURL = redisURL.String()
|
|
||||||
|
|
||||||
opts.Cookie.Secret = string(secret)
|
|
||||||
redisStore, err := NewRedisSessionStore(&opts.Session, &opts.Cookie)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = redisStore.Save(
|
|
||||||
httptest.NewRecorder(),
|
|
||||||
httptest.NewRequest(http.MethodGet, "/", nil),
|
|
||||||
&sessions.SessionState{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
t.Run("save session on redis sentinel", func(t *testing.T) {
|
|
||||||
redisServer, err := miniredis.Run()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer redisServer.Close()
|
|
||||||
sentinel := minisentinel.NewSentinel(redisServer)
|
|
||||||
err = sentinel.Start()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer sentinel.Close()
|
|
||||||
opts := options.NewOptions()
|
|
||||||
sentinelURL := url.URL{
|
|
||||||
Scheme: "redis",
|
|
||||||
Host: sentinel.Addr(),
|
|
||||||
}
|
|
||||||
opts.Session.Redis.SentinelConnectionURLs = []string{sentinelURL.String()}
|
|
||||||
opts.Session.Redis.UseSentinel = true
|
|
||||||
opts.Session.Redis.SentinelMasterName = sentinel.MasterInfo().Name
|
|
||||||
|
|
||||||
opts.Cookie.Secret = string(secret)
|
|
||||||
redisStore, err := NewRedisSessionStore(&opts.Session, &opts.Cookie)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = redisStore.Save(
|
|
||||||
httptest.NewRecorder(),
|
|
||||||
httptest.NewRequest(http.MethodGet, "/", nil),
|
|
||||||
&sessions.SessionState{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
101
pkg/sessions/redis/session_store_test.go
Normal file
101
pkg/sessions/redis/session_store_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Bose/minisentinel"
|
||||||
|
"github.com/alicebob/miniredis/v2"
|
||||||
|
"github.com/go-redis/redis/v7"
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
||||||
|
sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/tests"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSessionStore(t *testing.T) {
|
||||||
|
logger.SetOutput(GinkgoWriter)
|
||||||
|
|
||||||
|
redisLogger := log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile)
|
||||||
|
redisLogger.SetOutput(GinkgoWriter)
|
||||||
|
redis.SetLogger(redisLogger)
|
||||||
|
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Redis SessionStore")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Describe("Redis SessionStore Tests", func() {
|
||||||
|
var mr *miniredis.Miniredis
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
var err error
|
||||||
|
mr, err = miniredis.Run()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
mr.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
tests.RunSessionStoreTests(
|
||||||
|
func(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessionsapi.SessionStore, error) {
|
||||||
|
// Set the connection URL
|
||||||
|
opts.Type = options.RedisSessionStoreType
|
||||||
|
opts.Redis.ConnectionURL = "redis://" + mr.Addr()
|
||||||
|
return NewRedisSessionStore(opts, cookieOpts)
|
||||||
|
},
|
||||||
|
func(d time.Duration) error {
|
||||||
|
mr.FastForward(d)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Context("with sentinel", func() {
|
||||||
|
var ms *minisentinel.Sentinel
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
ms = minisentinel.NewSentinel(mr)
|
||||||
|
Expect(ms.Start()).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
go ms.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
tests.RunSessionStoreTests(
|
||||||
|
func(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessionsapi.SessionStore, error) {
|
||||||
|
// Set the sentinel connection URL
|
||||||
|
sentinelAddr := "redis://" + ms.Addr()
|
||||||
|
opts.Type = options.RedisSessionStoreType
|
||||||
|
opts.Redis.SentinelConnectionURLs = []string{sentinelAddr}
|
||||||
|
opts.Redis.UseSentinel = true
|
||||||
|
opts.Redis.SentinelMasterName = ms.MasterInfo().Name
|
||||||
|
return NewRedisSessionStore(opts, cookieOpts)
|
||||||
|
},
|
||||||
|
func(d time.Duration) error {
|
||||||
|
mr.FastForward(d)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("with cluster", func() {
|
||||||
|
tests.RunSessionStoreTests(
|
||||||
|
func(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessionsapi.SessionStore, error) {
|
||||||
|
clusterAddr := "redis://" + mr.Addr()
|
||||||
|
opts.Type = options.RedisSessionStoreType
|
||||||
|
opts.Redis.ClusterConnectionURLs = []string{clusterAddr}
|
||||||
|
opts.Redis.UseCluster = true
|
||||||
|
return NewRedisSessionStore(opts, cookieOpts)
|
||||||
|
},
|
||||||
|
func(d time.Duration) error {
|
||||||
|
mr.FastForward(d)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
@ -1,20 +1,12 @@
|
|||||||
package sessions_test
|
package sessions_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net/http"
|
"math/rand"
|
||||||
"net/http/httptest"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
miniredis "github.com/alicebob/miniredis/v2"
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
||||||
sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
|
|
||||||
cookiesapi "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies"
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions"
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions"
|
||||||
sessionscookie "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie"
|
sessionscookie "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie"
|
||||||
@ -34,341 +26,7 @@ var _ = Describe("NewSessionStore", func() {
|
|||||||
var opts *options.SessionOptions
|
var opts *options.SessionOptions
|
||||||
var cookieOpts *options.CookieOptions
|
var cookieOpts *options.CookieOptions
|
||||||
|
|
||||||
var request *http.Request
|
|
||||||
var response *httptest.ResponseRecorder
|
|
||||||
var session *sessionsapi.SessionState
|
|
||||||
var ss sessionsapi.SessionStore
|
|
||||||
var mr *miniredis.Miniredis
|
|
||||||
|
|
||||||
CheckCookieOptions := func() {
|
|
||||||
Context("the cookies returned", func() {
|
|
||||||
var cookies []*http.Cookie
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
cookies = response.Result().Cookies()
|
|
||||||
})
|
|
||||||
|
|
||||||
It("have the correct name set", func() {
|
|
||||||
if len(cookies) == 1 {
|
|
||||||
Expect(cookies[0].Name).To(Equal(cookieOpts.Name))
|
|
||||||
} else {
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
Expect(cookie.Name).To(ContainSubstring(cookieOpts.Name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("have the correct path set", func() {
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
Expect(cookie.Path).To(Equal(cookieOpts.Path))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("have the correct domain set", func() {
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
specifiedDomain := ""
|
|
||||||
if len(cookieOpts.Domains) > 0 {
|
|
||||||
specifiedDomain = cookieOpts.Domains[0]
|
|
||||||
}
|
|
||||||
Expect(cookie.Domain).To(Equal(specifiedDomain))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("have the correct HTTPOnly set", func() {
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
Expect(cookie.HttpOnly).To(Equal(cookieOpts.HTTPOnly))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("have the correct secure set", func() {
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
Expect(cookie.Secure).To(Equal(cookieOpts.Secure))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("have the correct SameSite set", func() {
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
Expect(cookie.SameSite).To(Equal(cookiesapi.ParseSameSite(cookieOpts.SameSite)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("have a signature timestamp matching session.CreatedAt", func() {
|
|
||||||
for _, cookie := range cookies {
|
|
||||||
if cookie.Value != "" {
|
|
||||||
parts := strings.Split(cookie.Value, "|")
|
|
||||||
Expect(parts).To(HaveLen(3))
|
|
||||||
Expect(parts[1]).To(Equal(strconv.Itoa(int(session.CreatedAt.Unix()))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following should only be for server stores
|
|
||||||
PersistentSessionStoreTests := func() {
|
|
||||||
Context("when Clear is called on a persistent store", func() {
|
|
||||||
var resultCookies []*http.Cookie
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
|
||||||
saveResp := httptest.NewRecorder()
|
|
||||||
err := ss.Save(saveResp, req, session)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
|
|
||||||
resultCookies = saveResp.Result().Cookies()
|
|
||||||
for _, c := range resultCookies {
|
|
||||||
request.AddCookie(c)
|
|
||||||
}
|
|
||||||
err = ss.Clear(response, request)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("attempting to Load", func() {
|
|
||||||
var loadedAfterClear *sessionsapi.SessionState
|
|
||||||
var loadErr error
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
loadReq := httptest.NewRequest("GET", "http://example.com/", nil)
|
|
||||||
for _, c := range resultCookies {
|
|
||||||
loadReq.AddCookie(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedAfterClear, loadErr = ss.Load(loadReq)
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns an empty session", func() {
|
|
||||||
Expect(loadedAfterClear).To(BeNil())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns an error", func() {
|
|
||||||
Expect(loadErr).To(HaveOccurred())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
CheckCookieOptions()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionStoreInterfaceTests := func(persistent bool) {
|
|
||||||
Context("when Save is called", func() {
|
|
||||||
Context("with no existing session", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
err := ss.Save(response, request, session)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("sets a `set-cookie` header in the response", func() {
|
|
||||||
Expect(response.Header().Get("set-cookie")).ToNot(BeEmpty())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("Ensures the session CreatedAt is not zero", func() {
|
|
||||||
Expect(session.CreatedAt.IsZero()).To(BeFalse())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("with a broken session", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
By("Using a valid cookie with a different providers session encoding")
|
|
||||||
broken := "BrokenSessionFromADifferentSessionImplementation"
|
|
||||||
value := encryption.SignedValue(cookieOpts.Secret, cookieOpts.Name, []byte(broken), time.Now())
|
|
||||||
cookie := cookiesapi.MakeCookieFromOptions(request, cookieOpts.Name, value, cookieOpts, cookieOpts.Expire, time.Now())
|
|
||||||
request.AddCookie(cookie)
|
|
||||||
|
|
||||||
err := ss.Save(response, request, session)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("sets a `set-cookie` header in the response", func() {
|
|
||||||
Expect(response.Header().Get("set-cookie")).ToNot(BeEmpty())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("Ensures the session CreatedAt is not zero", func() {
|
|
||||||
Expect(session.CreatedAt.IsZero()).To(BeFalse())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("with an expired saved session", func() {
|
|
||||||
var err error
|
|
||||||
BeforeEach(func() {
|
|
||||||
By("saving a session")
|
|
||||||
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
|
||||||
saveResp := httptest.NewRecorder()
|
|
||||||
err = ss.Save(saveResp, req, session)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
|
|
||||||
By("and clearing the session")
|
|
||||||
for _, c := range saveResp.Result().Cookies() {
|
|
||||||
request.AddCookie(c)
|
|
||||||
}
|
|
||||||
clearResp := httptest.NewRecorder()
|
|
||||||
err = ss.Clear(clearResp, request)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
|
|
||||||
By("then saving a request with the cleared session")
|
|
||||||
err = ss.Save(response, request, session)
|
|
||||||
})
|
|
||||||
|
|
||||||
It("no error should occur", func() {
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
CheckCookieOptions()
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("when Clear is called", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
|
||||||
saveResp := httptest.NewRecorder()
|
|
||||||
err := ss.Save(saveResp, req, session)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
|
|
||||||
for _, c := range saveResp.Result().Cookies() {
|
|
||||||
request.AddCookie(c)
|
|
||||||
}
|
|
||||||
err = ss.Clear(response, request)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("sets a `set-cookie` header in the response", func() {
|
|
||||||
Expect(response.Header().Get("Set-Cookie")).ToNot(BeEmpty())
|
|
||||||
})
|
|
||||||
|
|
||||||
CheckCookieOptions()
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("when Load is called", func() {
|
|
||||||
LoadSessionTests := func() {
|
|
||||||
var loadedSession *sessionsapi.SessionState
|
|
||||||
BeforeEach(func() {
|
|
||||||
var err error
|
|
||||||
loadedSession, err = ss.Load(request)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("loads a session equal to the original session", func() {
|
|
||||||
if cookieOpts.Secret == "" {
|
|
||||||
// Only Email and User stored in session when encrypted
|
|
||||||
Expect(loadedSession.Email).To(Equal(session.Email))
|
|
||||||
Expect(loadedSession.User).To(Equal(session.User))
|
|
||||||
} else {
|
|
||||||
// All fields stored in session if encrypted
|
|
||||||
|
|
||||||
// Can't compare time.Time using Equal() so remove ExpiresOn from sessions
|
|
||||||
l := *loadedSession
|
|
||||||
l.CreatedAt = nil
|
|
||||||
l.ExpiresOn = nil
|
|
||||||
s := *session
|
|
||||||
s.CreatedAt = nil
|
|
||||||
s.ExpiresOn = nil
|
|
||||||
Expect(l).To(Equal(s))
|
|
||||||
|
|
||||||
// Compare time.Time separately
|
|
||||||
Expect(loadedSession.CreatedAt.Equal(*session.CreatedAt)).To(BeTrue())
|
|
||||||
Expect(loadedSession.ExpiresOn.Equal(*session.ExpiresOn)).To(BeTrue())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
|
||||||
resp := httptest.NewRecorder()
|
|
||||||
err := ss.Save(resp, req, session)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
|
|
||||||
for _, cookie := range resp.Result().Cookies() {
|
|
||||||
request.AddCookie(cookie)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("before the refresh period", func() {
|
|
||||||
LoadSessionTests()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test TTLs and cleanup of persistent session storage
|
|
||||||
// For non-persistent we rely on the browser cookie lifecycle
|
|
||||||
if persistent {
|
|
||||||
Context("after the refresh period, but before the cookie expire period", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
switch ss.(type) {
|
|
||||||
case *redis.SessionStore:
|
|
||||||
mr.FastForward(cookieOpts.Refresh + time.Minute)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
LoadSessionTests()
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("after the cookie expire period", func() {
|
|
||||||
var loadedSession *sessionsapi.SessionState
|
|
||||||
var err error
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
switch ss.(type) {
|
|
||||||
case *redis.SessionStore:
|
|
||||||
mr.FastForward(cookieOpts.Expire + time.Minute)
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedSession, err = ss.Load(request)
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns an error loading the session", func() {
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns an empty session", func() {
|
|
||||||
Expect(loadedSession).To(BeNil())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if persistent {
|
|
||||||
PersistentSessionStoreTests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RunSessionTests := func(persistent bool) {
|
|
||||||
Context("with default options", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
var err error
|
|
||||||
ss, err = sessions.NewSessionStore(opts, cookieOpts)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
SessionStoreInterfaceTests(persistent)
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("with non-default options", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
cookieOpts = &options.CookieOptions{
|
|
||||||
Name: "_cookie_name",
|
|
||||||
Path: "/path",
|
|
||||||
Expire: time.Duration(72) * time.Hour,
|
|
||||||
Refresh: time.Duration(2) * time.Hour,
|
|
||||||
Secure: false,
|
|
||||||
HTTPOnly: false,
|
|
||||||
Domains: []string{"example.com"},
|
|
||||||
SameSite: "strict",
|
|
||||||
}
|
|
||||||
|
|
||||||
// A secret is required but not defaulted
|
|
||||||
secret := make([]byte, 32)
|
|
||||||
_, err := rand.Read(secret)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
cookieOpts.Secret = base64.URLEncoding.EncodeToString(secret)
|
|
||||||
|
|
||||||
ss, err = sessions.NewSessionStore(opts, cookieOpts)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
SessionStoreInterfaceTests(persistent)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
ss = nil
|
|
||||||
opts = &options.SessionOptions{}
|
opts = &options.SessionOptions{}
|
||||||
|
|
||||||
// A secret is required to create a Cipher, validation ensures it is the correct
|
// A secret is required to create a Cipher, validation ensures it is the correct
|
||||||
@ -388,19 +46,6 @@ var _ = Describe("NewSessionStore", func() {
|
|||||||
HTTPOnly: true,
|
HTTPOnly: true,
|
||||||
SameSite: "",
|
SameSite: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
expires := time.Now().Add(1 * time.Hour)
|
|
||||||
session = &sessionsapi.SessionState{
|
|
||||||
AccessToken: "AccessToken",
|
|
||||||
IDToken: "IDToken",
|
|
||||||
ExpiresOn: &expires,
|
|
||||||
RefreshToken: "RefreshToken",
|
|
||||||
Email: "john.doe@example.com",
|
|
||||||
User: "john.doe",
|
|
||||||
}
|
|
||||||
|
|
||||||
request = httptest.NewRequest("GET", "http://example.com/", nil)
|
|
||||||
response = httptest.NewRecorder()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("with type 'cookie'", func() {
|
Context("with type 'cookie'", func() {
|
||||||
@ -413,36 +58,12 @@ var _ = Describe("NewSessionStore", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ss).To(BeAssignableToTypeOf(&sessionscookie.SessionStore{}))
|
Expect(ss).To(BeAssignableToTypeOf(&sessionscookie.SessionStore{}))
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("the cookie.SessionStore", func() {
|
|
||||||
RunSessionTests(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("with an invalid cookie secret", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
cookieOpts.Secret = "invalid"
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns an error", func() {
|
|
||||||
ss, err := sessions.NewSessionStore(opts, cookieOpts)
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err.Error()).To(Equal("error initialising cipher: crypto/aes: invalid key size 7"))
|
|
||||||
Expect(ss).To(BeNil())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("with type 'redis'", func() {
|
Context("with type 'redis'", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
var err error
|
|
||||||
mr, err = miniredis.Run()
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
opts.Type = options.RedisSessionStoreType
|
opts.Type = options.RedisSessionStoreType
|
||||||
opts.Redis.ConnectionURL = "redis://" + mr.Addr()
|
opts.Redis.ConnectionURL = "redis://"
|
||||||
})
|
|
||||||
|
|
||||||
AfterEach(func() {
|
|
||||||
mr.Close()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("creates a redis.SessionStore", func() {
|
It("creates a redis.SessionStore", func() {
|
||||||
@ -450,23 +71,6 @@ var _ = Describe("NewSessionStore", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(ss).To(BeAssignableToTypeOf(&redis.SessionStore{}))
|
Expect(ss).To(BeAssignableToTypeOf(&redis.SessionStore{}))
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("the redis.SessionStore", func() {
|
|
||||||
RunSessionTests(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("with an invalid cookie secret", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
cookieOpts.Secret = "invalid"
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns an error", func() {
|
|
||||||
ss, err := sessions.NewSessionStore(opts, cookieOpts)
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err.Error()).To(Equal("error initialising cipher: crypto/aes: invalid key size 7"))
|
|
||||||
Expect(ss).To(BeNil())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("with an invalid type", func() {
|
Context("with an invalid type", func() {
|
||||||
|
435
pkg/sessions/tests/session_store_tests.go
Normal file
435
pkg/sessions/tests/session_store_tests.go
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
||||||
|
sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
|
||||||
|
cookiesapi "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies"
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
// testInput is passed to test function as a pointer.
|
||||||
|
// This allows BeforeEach blocks to initialise and use these values after
|
||||||
|
// Ginkgo has unpacked the tests.
|
||||||
|
// Interfaces have to be wrapped in closures otherwise nil pointers are thrown.
|
||||||
|
type testInput struct {
|
||||||
|
cookieOpts *options.CookieOptions
|
||||||
|
ss sessionStoreFunc
|
||||||
|
session *sessionsapi.SessionState
|
||||||
|
request *http.Request
|
||||||
|
response *httptest.ResponseRecorder
|
||||||
|
persistentFastForward PersistentStoreFastForwardFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// sessionStoreFunc is used in testInput to wrap the SessionStore interface.
|
||||||
|
type sessionStoreFunc func() sessionsapi.SessionStore
|
||||||
|
|
||||||
|
// PersistentStoreFastForwardFunc is used to adjust the time of the persistent
|
||||||
|
// store to fast forward expiry of sessions.
|
||||||
|
type PersistentStoreFastForwardFunc func(time.Duration) error
|
||||||
|
|
||||||
|
// NewSessionStoreFunc allows any session store implementation to configure their
|
||||||
|
// own session store before each test.
|
||||||
|
type NewSessionStoreFunc func(sessionOpts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessionsapi.SessionStore, error)
|
||||||
|
|
||||||
|
func RunSessionStoreTests(newSS NewSessionStoreFunc, persistentFastForward PersistentStoreFastForwardFunc) {
|
||||||
|
Describe("Session Store Suite", func() {
|
||||||
|
var opts *options.SessionOptions
|
||||||
|
var ss sessionsapi.SessionStore
|
||||||
|
var input testInput
|
||||||
|
var cookieSecret []byte
|
||||||
|
|
||||||
|
getSessionStore := func() sessionsapi.SessionStore {
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
ss = nil
|
||||||
|
opts = &options.SessionOptions{}
|
||||||
|
|
||||||
|
// A secret is required to create a Cipher, validation ensures it is the correct
|
||||||
|
// length before a session store is initialised.
|
||||||
|
cookieSecret = make([]byte, 32)
|
||||||
|
_, err := rand.Read(cookieSecret)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
// Set default options in CookieOptions
|
||||||
|
cookieOpts := &options.CookieOptions{
|
||||||
|
Name: "_oauth2_proxy",
|
||||||
|
Path: "/",
|
||||||
|
Expire: time.Duration(168) * time.Hour,
|
||||||
|
Refresh: time.Duration(1) * time.Hour,
|
||||||
|
Secure: true,
|
||||||
|
HTTPOnly: true,
|
||||||
|
SameSite: "",
|
||||||
|
Secret: string(cookieSecret),
|
||||||
|
}
|
||||||
|
|
||||||
|
expires := time.Now().Add(1 * time.Hour)
|
||||||
|
session := &sessionsapi.SessionState{
|
||||||
|
AccessToken: "AccessToken",
|
||||||
|
IDToken: "IDToken",
|
||||||
|
ExpiresOn: &expires,
|
||||||
|
RefreshToken: "RefreshToken",
|
||||||
|
Email: "john.doe@example.com",
|
||||||
|
User: "john.doe",
|
||||||
|
}
|
||||||
|
|
||||||
|
request := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
input = testInput{
|
||||||
|
cookieOpts: cookieOpts,
|
||||||
|
ss: getSessionStore,
|
||||||
|
session: session,
|
||||||
|
request: request,
|
||||||
|
response: response,
|
||||||
|
persistentFastForward: persistentFastForward,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("with default options", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
var err error
|
||||||
|
ss, err = newSS(opts, input.cookieOpts)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
SessionStoreInterfaceTests(&input)
|
||||||
|
if persistentFastForward != nil {
|
||||||
|
PersistentSessionStoreInterfaceTests(&input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("with non-default options", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
input.cookieOpts = &options.CookieOptions{
|
||||||
|
Name: "_cookie_name",
|
||||||
|
Path: "/path",
|
||||||
|
Expire: time.Duration(72) * time.Hour,
|
||||||
|
Refresh: time.Duration(2) * time.Hour,
|
||||||
|
Secure: false,
|
||||||
|
HTTPOnly: false,
|
||||||
|
Domains: []string{"example.com"},
|
||||||
|
SameSite: "strict",
|
||||||
|
Secret: string(cookieSecret),
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ss, err = newSS(opts, input.cookieOpts)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
SessionStoreInterfaceTests(&input)
|
||||||
|
if persistentFastForward != nil {
|
||||||
|
PersistentSessionStoreInterfaceTests(&input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("with an invalid cookie secret", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
input.cookieOpts.Secret = "invalid"
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error when initialising the session store", func() {
|
||||||
|
ss, err := newSS(opts, input.cookieOpts)
|
||||||
|
Expect(err).To(MatchError("error initialising cipher: crypto/aes: invalid key size 7"))
|
||||||
|
Expect(ss).To(BeNil())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckCookieOptions(in *testInput) {
|
||||||
|
Context("the cookies returned", func() {
|
||||||
|
var cookies []*http.Cookie
|
||||||
|
BeforeEach(func() {
|
||||||
|
cookies = in.response.Result().Cookies()
|
||||||
|
})
|
||||||
|
|
||||||
|
It("have the correct name set", func() {
|
||||||
|
if len(cookies) == 1 {
|
||||||
|
Expect(cookies[0].Name).To(Equal(in.cookieOpts.Name))
|
||||||
|
} else {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
Expect(cookie.Name).To(ContainSubstring(in.cookieOpts.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("have the correct path set", func() {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
Expect(cookie.Path).To(Equal(in.cookieOpts.Path))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("have the correct domain set", func() {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
specifiedDomain := ""
|
||||||
|
if len(in.cookieOpts.Domains) > 0 {
|
||||||
|
specifiedDomain = in.cookieOpts.Domains[0]
|
||||||
|
}
|
||||||
|
Expect(cookie.Domain).To(Equal(specifiedDomain))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("have the correct HTTPOnly set", func() {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
Expect(cookie.HttpOnly).To(Equal(in.cookieOpts.HTTPOnly))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("have the correct secure set", func() {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
Expect(cookie.Secure).To(Equal(in.cookieOpts.Secure))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("have the correct SameSite set", func() {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
Expect(cookie.SameSite).To(Equal(cookiesapi.ParseSameSite(in.cookieOpts.SameSite)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("have a signature timestamp matching session.CreatedAt", func() {
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
if cookie.Value != "" {
|
||||||
|
parts := strings.Split(cookie.Value, "|")
|
||||||
|
Expect(parts).To(HaveLen(3))
|
||||||
|
Expect(parts[1]).To(Equal(strconv.Itoa(int(in.session.CreatedAt.Unix()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func PersistentSessionStoreInterfaceTests(in *testInput) {
|
||||||
|
// Check that a stale cookie can't load an already cleared session
|
||||||
|
Context("when Clear is called on a persistent store", func() {
|
||||||
|
var resultCookies []*http.Cookie
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
saveResp := httptest.NewRecorder()
|
||||||
|
err := in.ss().Save(saveResp, req, in.session)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
resultCookies = saveResp.Result().Cookies()
|
||||||
|
for _, c := range resultCookies {
|
||||||
|
in.request.AddCookie(c)
|
||||||
|
}
|
||||||
|
err = in.ss().Clear(in.response, in.request)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("attempting to Load", func() {
|
||||||
|
var loadedAfterClear *sessionsapi.SessionState
|
||||||
|
var loadErr error
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
loadReq := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
for _, c := range resultCookies {
|
||||||
|
loadReq.AddCookie(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedAfterClear, loadErr = in.ss().Load(loadReq)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an empty session", func() {
|
||||||
|
Expect(loadedAfterClear).To(BeNil())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error", func() {
|
||||||
|
Expect(loadErr).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
CheckCookieOptions(in)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test TTLs and cleanup of persistent session storage
|
||||||
|
// For non-persistent we rely on the browser cookie lifecycle
|
||||||
|
Context("when Load is called on a persistent store", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
err := in.ss().Save(resp, req, in.session)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
for _, cookie := range resp.Result().Cookies() {
|
||||||
|
in.request.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("after the refresh period, but before the cookie expire period", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
Expect(in.persistentFastForward(in.cookieOpts.Refresh + time.Minute)).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
LoadSessionTests(in)
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("after the cookie expire period", func() {
|
||||||
|
var loadedSession *sessionsapi.SessionState
|
||||||
|
var err error
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
Expect(in.persistentFastForward(in.cookieOpts.Expire + time.Minute)).To(Succeed())
|
||||||
|
|
||||||
|
loadedSession, err = in.ss().Load(in.request)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error loading the session", func() {
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an empty session", func() {
|
||||||
|
Expect(loadedSession).To(BeNil())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SessionStoreInterfaceTests(in *testInput) {
|
||||||
|
Context("when Save is called", func() {
|
||||||
|
Context("with no existing session", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
err := in.ss().Save(in.response, in.request, in.session)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("sets a `set-cookie` header in the response", func() {
|
||||||
|
Expect(in.response.Header().Get("set-cookie")).ToNot(BeEmpty())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Ensures the session CreatedAt is not zero", func() {
|
||||||
|
Expect(in.session.CreatedAt.IsZero()).To(BeFalse())
|
||||||
|
})
|
||||||
|
|
||||||
|
CheckCookieOptions(in)
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("with a broken session", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
By("Using a valid cookie with a different providers session encoding")
|
||||||
|
broken := "BrokenSessionFromADifferentSessionImplementation"
|
||||||
|
value := encryption.SignedValue(in.cookieOpts.Secret, in.cookieOpts.Name, []byte(broken), time.Now())
|
||||||
|
cookie := cookiesapi.MakeCookieFromOptions(in.request, in.cookieOpts.Name, value, in.cookieOpts, in.cookieOpts.Expire, time.Now())
|
||||||
|
in.request.AddCookie(cookie)
|
||||||
|
|
||||||
|
err := in.ss().Save(in.response, in.request, in.session)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("sets a `set-cookie` header in the response", func() {
|
||||||
|
Expect(in.response.Header().Get("set-cookie")).ToNot(BeEmpty())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Ensures the session CreatedAt is not zero", func() {
|
||||||
|
Expect(in.session.CreatedAt.IsZero()).To(BeFalse())
|
||||||
|
})
|
||||||
|
|
||||||
|
CheckCookieOptions(in)
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("with an expired saved session", func() {
|
||||||
|
var err error
|
||||||
|
BeforeEach(func() {
|
||||||
|
By("saving a session")
|
||||||
|
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
saveResp := httptest.NewRecorder()
|
||||||
|
err = in.ss().Save(saveResp, req, in.session)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
By("and clearing the session")
|
||||||
|
for _, c := range saveResp.Result().Cookies() {
|
||||||
|
in.request.AddCookie(c)
|
||||||
|
}
|
||||||
|
clearResp := httptest.NewRecorder()
|
||||||
|
err = in.ss().Clear(clearResp, in.request)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
By("then saving a request with the cleared session")
|
||||||
|
err = in.ss().Save(in.response, in.request, in.session)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("no error should occur", func() {
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("when Clear is called", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
saveResp := httptest.NewRecorder()
|
||||||
|
err := in.ss().Save(saveResp, req, in.session)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
for _, c := range saveResp.Result().Cookies() {
|
||||||
|
in.request.AddCookie(c)
|
||||||
|
}
|
||||||
|
err = in.ss().Clear(in.response, in.request)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("sets a `set-cookie` header in the response", func() {
|
||||||
|
Expect(in.response.Header().Get("Set-Cookie")).ToNot(BeEmpty())
|
||||||
|
})
|
||||||
|
|
||||||
|
CheckCookieOptions(in)
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("when Load is called", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
req := httptest.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
err := in.ss().Save(resp, req, in.session)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
for _, cookie := range resp.Result().Cookies() {
|
||||||
|
in.request.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("before the refresh period", func() {
|
||||||
|
LoadSessionTests(in)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSessionTests(in *testInput) {
|
||||||
|
var loadedSession *sessionsapi.SessionState
|
||||||
|
BeforeEach(func() {
|
||||||
|
var err error
|
||||||
|
loadedSession, err = in.ss().Load(in.request)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("loads a session equal to the original session", func() {
|
||||||
|
// Can't compare time.Time using Equal() so remove ExpiresOn from sessions
|
||||||
|
l := *loadedSession
|
||||||
|
l.CreatedAt = nil
|
||||||
|
l.ExpiresOn = nil
|
||||||
|
s := *in.session
|
||||||
|
s.CreatedAt = nil
|
||||||
|
s.ExpiresOn = nil
|
||||||
|
Expect(l).To(Equal(s))
|
||||||
|
|
||||||
|
// Compare time.Time separately
|
||||||
|
Expect(loadedSession.CreatedAt.Equal(*in.session.CreatedAt)).To(BeTrue())
|
||||||
|
Expect(loadedSession.ExpiresOn.Equal(*in.session.ExpiresOn)).To(BeTrue())
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user