mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2024-12-10 11:10:27 +02:00
Merge pull request #484 from oauth2-proxy/cookie-options-rename
Replace configuration loading with Viper
This commit is contained in:
commit
eae652d986
@ -11,14 +11,18 @@
|
||||
- Migration from Pusher to independent org may have introduced breaking changes for your environment.
|
||||
- See the changes listed below for PR [#464](https://github.com/oauth2-proxy/oauth2-proxy/pull/464) for full details
|
||||
- Binaries renamed from `oauth2_proxy` to `oauth2-proxy`
|
||||
|
||||
- [#440](https://github.com/oauth2-proxy/oauth2-proxy/pull/440) Switch Azure AD Graph API to Microsoft Graph API (@johejo)
|
||||
- The Azure AD Graph API has been [deprecated](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-graph-api) and is being replaced by the Microsoft Graph API.
|
||||
If your application relies on the access token being passed to it to access the Azure AD Graph API, you should migrate your application to use the Microsoft Graph API.
|
||||
Existing behaviour can be retained by setting `-resource=https://graph.windows.net`.
|
||||
- [#484](https://github.com/oauth2-proxy/oauth2-proxy/pull/484) Configuration loading has been replaced with Viper and PFlag
|
||||
- Flags now require a `--` prefix before the option
|
||||
- Previously flags allowed either `-` or `--` to prefix the option name
|
||||
- Eg `-provider` must now be `--provider`
|
||||
|
||||
## Changes since v5.1.0
|
||||
|
||||
- [#484](https://github.com/oauth2-proxy/oauth2-proxy/pull/484) Replace configuration loading with Viper (@JoelSpeed)
|
||||
- [#499](https://github.com/oauth2-proxy/oauth2-proxy/pull/469) Add `-user-id-claim` to support generic claims in addition to email
|
||||
- [#486](https://github.com/oauth2-proxy/oauth2-proxy/pull/486) Add new linters (@johejo)
|
||||
- [#440](https://github.com/oauth2-proxy/oauth2-proxy/pull/440) Switch Azure AD Graph API to Microsoft Graph API (@johejo)
|
||||
|
@ -1,54 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EnvOptions holds program options loaded from the process environment
|
||||
type EnvOptions map[string]interface{}
|
||||
|
||||
// LoadEnvForStruct loads environment variables for each field in an options
|
||||
// struct passed into it.
|
||||
//
|
||||
// Fields in the options struct must have an `env` and `cfg` tag to be read
|
||||
// from the environment
|
||||
func (cfg EnvOptions) LoadEnvForStruct(options interface{}) {
|
||||
val := reflect.ValueOf(options)
|
||||
var typ reflect.Type
|
||||
if val.Kind() == reflect.Ptr {
|
||||
typ = val.Elem().Type()
|
||||
} else {
|
||||
typ = val.Type()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
// pull out the struct tags:
|
||||
// flag - the name of the command line flag
|
||||
// deprecated - (optional) the name of the deprecated command line flag
|
||||
// cfg - (optional, defaults to underscored flag) the name of the config file option
|
||||
field := typ.Field(i)
|
||||
fieldV := reflect.Indirect(val).Field(i)
|
||||
|
||||
if field.Type.Kind() == reflect.Struct && field.Anonymous {
|
||||
cfg.LoadEnvForStruct(fieldV.Interface())
|
||||
continue
|
||||
}
|
||||
|
||||
flagName := field.Tag.Get("flag")
|
||||
envName := field.Tag.Get("env")
|
||||
cfgName := field.Tag.Get("cfg")
|
||||
if cfgName == "" && flagName != "" {
|
||||
cfgName = strings.ReplaceAll(flagName, "-", "_")
|
||||
}
|
||||
if envName == "" || cfgName == "" {
|
||||
// resolvable fields must have the `env` and `cfg` struct tag
|
||||
continue
|
||||
}
|
||||
v := os.Getenv(envName)
|
||||
if v != "" {
|
||||
cfg[cfgName] = v
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
proxy "github.com/oauth2-proxy/oauth2-proxy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type EnvTest struct {
|
||||
TestField string `cfg:"target_field" env:"TEST_ENV_FIELD"`
|
||||
EnvTestEmbed
|
||||
}
|
||||
|
||||
type EnvTestEmbed struct {
|
||||
TestFieldEmbed string `cfg:"target_field_embed" env:"TEST_ENV_FIELD_EMBED"`
|
||||
}
|
||||
|
||||
func TestLoadEnvForStruct(t *testing.T) {
|
||||
|
||||
cfg := make(proxy.EnvOptions)
|
||||
cfg.LoadEnvForStruct(&EnvTest{})
|
||||
|
||||
_, ok := cfg["target_field"]
|
||||
assert.Equal(t, ok, false)
|
||||
|
||||
os.Setenv("TEST_ENV_FIELD", "1234abcd")
|
||||
cfg.LoadEnvForStruct(&EnvTest{})
|
||||
v := cfg["target_field"]
|
||||
assert.Equal(t, v, "1234abcd")
|
||||
}
|
||||
|
||||
func TestLoadEnvForStructWithEmbeddedFields(t *testing.T) {
|
||||
|
||||
cfg := make(proxy.EnvOptions)
|
||||
cfg.LoadEnvForStruct(&EnvTest{})
|
||||
|
||||
_, ok := cfg["target_field_embed"]
|
||||
assert.Equal(t, ok, false)
|
||||
|
||||
os.Setenv("TEST_ENV_FIELD_EMBED", "1234abcd")
|
||||
cfg.LoadEnvForStruct(&EnvTest{})
|
||||
v := cfg["target_field_embed"]
|
||||
assert.Equal(t, v, "1234abcd")
|
||||
}
|
5
go.mod
5
go.mod
@ -3,7 +3,6 @@ module github.com/oauth2-proxy/oauth2-proxy
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/alicebob/miniredis/v2 v2.11.2
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
@ -13,10 +12,12 @@ require (
|
||||
github.com/go-redis/redis/v7 v7.2.0
|
||||
github.com/kr/pretty v0.2.0 // indirect
|
||||
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa
|
||||
github.com/mreiferson/go-options v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/onsi/ginkgo v1.12.0
|
||||
github.com/onsi/gomega v1.9.0
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/spf13/pflag v1.0.3
|
||||
github.com/spf13/viper v1.6.3
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
|
114
go.sum
114
go.sum
@ -4,67 +4,118 @@ cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
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/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/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.11.2 h1:OtWO7akm5otuhssnE/sNfsWxG4gZ8DbGhShDtRrByJs=
|
||||
github.com/alicebob/miniredis/v2 v2.11.2/go.mod h1:VL3UDEfAH59bSa7MuHMuFToxkqyHh69s/WUbYlOAuyg=
|
||||
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 v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
|
||||
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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/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/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/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
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/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
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/go.mod h1:8vxFeeg++MqgCHwehSuwTlYCF0ALyDJbYJ1JsKi7v6s=
|
||||
github.com/mreiferson/go-options v1.0.0 h1:RMLidydGlDWpL+lQTXo0bVIf/XT2CTq7AEJMoz5/VWs=
|
||||
github.com/mreiferson/go-options v1.0.0/go.mod h1:zHtCks/HQvOt8ATyfwVe3JJq2PPuImzXINPRTC03+9w=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
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/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/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=
|
||||
@ -73,20 +124,63 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
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/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
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/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
|
||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
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/go.mod h1:DIGbh/f5XMAessMV/uaIik81gkDVjUeQ9ApdaU7wRKE=
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -98,11 +192,14 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -117,7 +214,10 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
|
||||
@ -131,11 +231,14 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/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/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
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=
|
||||
@ -152,21 +255,28 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
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/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
|
||||
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
22
main.go
22
main.go
@ -12,9 +12,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
options "github.com/mreiferson/go-options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -149,7 +149,10 @@ func main() {
|
||||
|
||||
flagSet.String("user-id-claim", "email", "which claim contains the user ID")
|
||||
|
||||
flagSet.Parse(os.Args[1:])
|
||||
pflagSet := pflag.NewFlagSet("oauth2-proxy", pflag.ExitOnError)
|
||||
pflagSet.AddGoFlagSet(flagSet)
|
||||
|
||||
pflagSet.Parse(os.Args[1:])
|
||||
|
||||
if *showVersion {
|
||||
fmt.Printf("oauth2-proxy %s (built with %s)\n", VERSION, runtime.Version())
|
||||
@ -157,18 +160,13 @@ func main() {
|
||||
}
|
||||
|
||||
opts := NewOptions()
|
||||
|
||||
cfg := make(EnvOptions)
|
||||
if *config != "" {
|
||||
_, err := toml.DecodeFile(*config, &cfg)
|
||||
err := options.Load(*config, pflagSet, opts)
|
||||
if err != nil {
|
||||
logger.Fatalf("ERROR: failed to load config file %s - %s", *config, err)
|
||||
logger.Printf("ERROR: Failed to load config: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
cfg.LoadEnvForStruct(opts)
|
||||
options.Resolve(opts, flagSet, cfg)
|
||||
|
||||
err := opts.Validate()
|
||||
err = opts.Validate()
|
||||
if err != nil {
|
||||
logger.Printf("%s", err)
|
||||
os.Exit(1)
|
||||
|
@ -247,7 +247,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
||||
panic(fmt.Sprintf("unknown upstream protocol %s", u.Scheme))
|
||||
}
|
||||
}
|
||||
for _, u := range opts.CompiledRegex {
|
||||
for _, u := range opts.compiledRegex {
|
||||
logger.Printf("compiled skip-auth-regex => %q", u)
|
||||
}
|
||||
|
||||
@ -264,23 +264,23 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
||||
|
||||
logger.Printf("OAuthProxy configured for %s Client ID: %s", opts.provider.Data().ProviderName, opts.ClientID)
|
||||
refresh := "disabled"
|
||||
if opts.CookieRefresh != time.Duration(0) {
|
||||
refresh = fmt.Sprintf("after %s", opts.CookieRefresh)
|
||||
if opts.Cookie.Refresh != time.Duration(0) {
|
||||
refresh = fmt.Sprintf("after %s", opts.Cookie.Refresh)
|
||||
}
|
||||
|
||||
logger.Printf("Cookie settings: name:%s secure(https):%v httponly:%v expiry:%s domains:%s path:%s samesite:%s refresh:%s", opts.CookieName, opts.CookieSecure, opts.CookieHTTPOnly, opts.CookieExpire, strings.Join(opts.CookieDomains, ","), opts.CookiePath, opts.CookieSameSite, refresh)
|
||||
logger.Printf("Cookie settings: name:%s secure(https):%v httponly:%v expiry:%s domains:%s path:%s samesite:%s refresh:%s", opts.Cookie.Name, opts.Cookie.Secure, opts.Cookie.HTTPOnly, opts.Cookie.Expire, strings.Join(opts.Cookie.Domains, ","), opts.Cookie.Path, opts.Cookie.SameSite, refresh)
|
||||
|
||||
return &OAuthProxy{
|
||||
CookieName: opts.CookieName,
|
||||
CSRFCookieName: fmt.Sprintf("%v_%v", opts.CookieName, "csrf"),
|
||||
CookieSeed: opts.CookieSecret,
|
||||
CookieDomains: opts.CookieDomains,
|
||||
CookiePath: opts.CookiePath,
|
||||
CookieSecure: opts.CookieSecure,
|
||||
CookieHTTPOnly: opts.CookieHTTPOnly,
|
||||
CookieExpire: opts.CookieExpire,
|
||||
CookieRefresh: opts.CookieRefresh,
|
||||
CookieSameSite: opts.CookieSameSite,
|
||||
CookieName: opts.Cookie.Name,
|
||||
CSRFCookieName: fmt.Sprintf("%v_%v", opts.Cookie.Name, "csrf"),
|
||||
CookieSeed: opts.Cookie.Secret,
|
||||
CookieDomains: opts.Cookie.Domains,
|
||||
CookiePath: opts.Cookie.Path,
|
||||
CookieSecure: opts.Cookie.Secure,
|
||||
CookieHTTPOnly: opts.Cookie.HTTPOnly,
|
||||
CookieExpire: opts.Cookie.Expire,
|
||||
CookieRefresh: opts.Cookie.Refresh,
|
||||
CookieSameSite: opts.Cookie.SameSite,
|
||||
Validator: validator,
|
||||
|
||||
RobotsPath: "/robots.txt",
|
||||
@ -303,7 +303,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
||||
skipAuthPreflight: opts.SkipAuthPreflight,
|
||||
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
|
||||
jwtBearerVerifiers: opts.jwtBearerVerifiers,
|
||||
compiledRegex: opts.CompiledRegex,
|
||||
compiledRegex: opts.compiledRegex,
|
||||
SetXAuthRequest: opts.SetXAuthRequest,
|
||||
PassBasicAuth: opts.PassBasicAuth,
|
||||
SetBasicAuth: opts.SetBasicAuth,
|
||||
|
@ -164,7 +164,7 @@ func TestRobotsTxt(t *testing.T) {
|
||||
opts := NewOptions()
|
||||
opts.ClientID = "asdlkjx"
|
||||
opts.ClientSecret = "alkgks"
|
||||
opts.CookieSecret = "asdkugkj"
|
||||
opts.Cookie.Secret = "asdkugkj"
|
||||
opts.Validate()
|
||||
|
||||
proxy := NewOAuthProxy(opts, func(string) bool { return true })
|
||||
@ -179,7 +179,7 @@ func TestIsValidRedirect(t *testing.T) {
|
||||
opts := NewOptions()
|
||||
opts.ClientID = "skdlfj"
|
||||
opts.ClientSecret = "fgkdsgj"
|
||||
opts.CookieSecret = "ljgiogbj"
|
||||
opts.Cookie.Secret = "ljgiogbj"
|
||||
// Should match domains that are exactly foo.bar and any subdomain of bar.foo
|
||||
opts.WhitelistDomains = []string{
|
||||
"foo.bar",
|
||||
@ -398,10 +398,10 @@ func TestBasicAuthPassword(t *testing.T) {
|
||||
opts.Upstreams = append(opts.Upstreams, providerServer.URL)
|
||||
// The CookieSecret must be 32 bytes in order to create the AES
|
||||
// cipher.
|
||||
opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
||||
opts.Cookie.Secret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
||||
opts.ClientID = "dlgkj"
|
||||
opts.ClientSecret = "alkgret"
|
||||
opts.CookieSecure = false
|
||||
opts.Cookie.Secure = false
|
||||
opts.PassBasicAuth = true
|
||||
opts.SetBasicAuth = true
|
||||
opts.PassUserHeaders = true
|
||||
@ -582,10 +582,10 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTes
|
||||
}
|
||||
// The CookieSecret must be 32 bytes in order to create the AES
|
||||
// cipher.
|
||||
t.opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
||||
t.opts.Cookie.Secret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
||||
t.opts.ClientID = "slgkj"
|
||||
t.opts.ClientSecret = "gfjgojl"
|
||||
t.opts.CookieSecure = false
|
||||
t.opts.Cookie.Secure = false
|
||||
t.opts.PassAccessToken = opts.PassAccessToken
|
||||
t.opts.Validate()
|
||||
|
||||
@ -735,7 +735,7 @@ func NewSignInPageTest(skipProvider bool) *SignInPageTest {
|
||||
var sipTest SignInPageTest
|
||||
|
||||
sipTest.opts = NewOptions()
|
||||
sipTest.opts.CookieSecret = "adklsj2"
|
||||
sipTest.opts.Cookie.Secret = "adklsj2"
|
||||
sipTest.opts.ClientID = "lkdgj"
|
||||
sipTest.opts.ClientSecret = "sgiufgoi"
|
||||
sipTest.opts.SkipProviderButton = skipProvider
|
||||
@ -841,10 +841,10 @@ func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifi
|
||||
}
|
||||
pcTest.opts.ClientID = "asdfljk"
|
||||
pcTest.opts.ClientSecret = "lkjfdsig"
|
||||
pcTest.opts.CookieSecret = "0123456789abcdefabcd"
|
||||
pcTest.opts.Cookie.Secret = "0123456789abcdefabcd"
|
||||
// First, set the CookieRefresh option so proxy.AesCipher is created,
|
||||
// needed to encrypt the access_token.
|
||||
pcTest.opts.CookieRefresh = time.Hour
|
||||
pcTest.opts.Cookie.Refresh = time.Hour
|
||||
pcTest.opts.Validate()
|
||||
|
||||
pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool {
|
||||
@ -915,7 +915,7 @@ func TestProcessCookieNoCookieError(t *testing.T) {
|
||||
|
||||
func TestProcessCookieRefreshNotSet(t *testing.T) {
|
||||
pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
|
||||
opts.CookieExpire = time.Duration(23) * time.Hour
|
||||
opts.Cookie.Expire = time.Duration(23) * time.Hour
|
||||
})
|
||||
reference := time.Now().Add(time.Duration(-2) * time.Hour)
|
||||
|
||||
@ -932,7 +932,7 @@ func TestProcessCookieRefreshNotSet(t *testing.T) {
|
||||
|
||||
func TestProcessCookieFailIfCookieExpired(t *testing.T) {
|
||||
pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
|
||||
opts.CookieExpire = time.Duration(24) * time.Hour
|
||||
opts.Cookie.Expire = time.Duration(24) * time.Hour
|
||||
})
|
||||
reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
|
||||
startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: reference}
|
||||
@ -947,7 +947,7 @@ func TestProcessCookieFailIfCookieExpired(t *testing.T) {
|
||||
|
||||
func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
|
||||
pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
|
||||
opts.CookieExpire = time.Duration(24) * time.Hour
|
||||
opts.Cookie.Expire = time.Duration(24) * time.Hour
|
||||
})
|
||||
reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
|
||||
startSession := &sessions.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: reference}
|
||||
@ -1017,7 +1017,7 @@ func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
|
||||
|
||||
func TestAuthOnlyEndpointUnauthorizedOnExpiration(t *testing.T) {
|
||||
test := NewAuthOnlyEndpointTest(func(opts *Options) {
|
||||
opts.CookieExpire = time.Duration(24) * time.Hour
|
||||
opts.Cookie.Expire = time.Duration(24) * time.Hour
|
||||
})
|
||||
reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
|
||||
startSession := &sessions.SessionState{
|
||||
@ -1149,7 +1149,7 @@ func TestAuthSkippedForPreflightRequests(t *testing.T) {
|
||||
opts.Upstreams = append(opts.Upstreams, upstream.URL)
|
||||
opts.ClientID = "aljsal"
|
||||
opts.ClientSecret = "jglkfsdgj"
|
||||
opts.CookieSecret = "dkfjgdls"
|
||||
opts.Cookie.Secret = "dkfjgdls"
|
||||
opts.SkipAuthPreflight = true
|
||||
opts.Validate()
|
||||
|
||||
@ -1196,7 +1196,7 @@ type SignatureTest struct {
|
||||
|
||||
func NewSignatureTest() *SignatureTest {
|
||||
opts := NewOptions()
|
||||
opts.CookieSecret = "cookie secret"
|
||||
opts.Cookie.Secret = "cookie secret"
|
||||
opts.ClientID = "client ID"
|
||||
opts.ClientSecret = "client secret"
|
||||
opts.EmailDomains = []string{"acm.org"}
|
||||
@ -1343,7 +1343,7 @@ type ajaxRequestTest struct {
|
||||
func newAjaxRequestTest() *ajaxRequestTest {
|
||||
test := &ajaxRequestTest{}
|
||||
test.opts = NewOptions()
|
||||
test.opts.CookieSecret = "sdflsw"
|
||||
test.opts.Cookie.Secret = "sdflsw"
|
||||
test.opts.ClientID = "gkljfdl"
|
||||
test.opts.ClientSecret = "sdflkjs"
|
||||
test.opts.Validate()
|
||||
@ -1401,11 +1401,11 @@ func TestAjaxForbiddendRequest(t *testing.T) {
|
||||
|
||||
func TestClearSplitCookie(t *testing.T) {
|
||||
opts := NewOptions()
|
||||
opts.CookieName = "oauth2"
|
||||
opts.CookieDomains = []string{"abc"}
|
||||
store, err := cookie.NewCookieSessionStore(&opts.SessionOptions, &opts.CookieOptions)
|
||||
opts.Cookie.Name = "oauth2"
|
||||
opts.Cookie.Domains = []string{"abc"}
|
||||
store, err := cookie.NewCookieSessionStore(&opts.Session, &opts.Cookie)
|
||||
assert.Equal(t, err, nil)
|
||||
p := OAuthProxy{CookieName: opts.CookieName, CookieDomains: opts.CookieDomains, sessionStore: store}
|
||||
p := OAuthProxy{CookieName: opts.Cookie.Name, CookieDomains: opts.Cookie.Domains, sessionStore: store}
|
||||
var rw = httptest.NewRecorder()
|
||||
req := httptest.NewRequest("get", "/", nil)
|
||||
|
||||
@ -1430,11 +1430,11 @@ func TestClearSplitCookie(t *testing.T) {
|
||||
|
||||
func TestClearSingleCookie(t *testing.T) {
|
||||
opts := NewOptions()
|
||||
opts.CookieName = "oauth2"
|
||||
opts.CookieDomains = []string{"abc"}
|
||||
store, err := cookie.NewCookieSessionStore(&opts.SessionOptions, &opts.CookieOptions)
|
||||
opts.Cookie.Name = "oauth2"
|
||||
opts.Cookie.Domains = []string{"abc"}
|
||||
store, err := cookie.NewCookieSessionStore(&opts.Session, &opts.Cookie)
|
||||
assert.Equal(t, err, nil)
|
||||
p := OAuthProxy{CookieName: opts.CookieName, CookieDomains: opts.CookieDomains, sessionStore: store}
|
||||
p := OAuthProxy{CookieName: opts.Cookie.Name, CookieDomains: opts.Cookie.Domains, sessionStore: store}
|
||||
var rw = httptest.NewRecorder()
|
||||
req := httptest.NewRequest("get", "/", nil)
|
||||
|
||||
|
63
options.go
63
options.go
@ -64,11 +64,8 @@ type Options struct {
|
||||
Banner string `flag:"banner" cfg:"banner" env:"OAUTH2_PROXY_BANNER"`
|
||||
Footer string `flag:"footer" cfg:"footer" env:"OAUTH2_PROXY_FOOTER"`
|
||||
|
||||
// Embed CookieOptions
|
||||
options.CookieOptions
|
||||
|
||||
// Embed SessionOptions
|
||||
options.SessionOptions
|
||||
Cookie options.CookieOptions `cfg:",squash"`
|
||||
Session options.SessionOptions `cfg:",squash"`
|
||||
|
||||
Upstreams []string `flag:"upstream" cfg:"upstreams" env:"OAUTH2_PROXY_UPSTREAMS"`
|
||||
SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex" env:"OAUTH2_PROXY_SKIP_AUTH_REGEX"`
|
||||
@ -134,7 +131,7 @@ type Options struct {
|
||||
// internal values that are set after config validation
|
||||
redirectURL *url.URL
|
||||
proxyURLs []*url.URL
|
||||
CompiledRegex []*regexp.Regexp
|
||||
compiledRegex []*regexp.Regexp
|
||||
provider providers.Provider
|
||||
sessionStore sessionsapi.SessionStore
|
||||
signatureData *SignatureData
|
||||
@ -158,14 +155,14 @@ func NewOptions() *Options {
|
||||
HTTPSAddress: ":443",
|
||||
ForceHTTPS: false,
|
||||
DisplayHtpasswdForm: true,
|
||||
CookieOptions: options.CookieOptions{
|
||||
CookieName: "_oauth2_proxy",
|
||||
CookieSecure: true,
|
||||
CookieHTTPOnly: true,
|
||||
CookieExpire: time.Duration(168) * time.Hour,
|
||||
CookieRefresh: time.Duration(0),
|
||||
Cookie: options.CookieOptions{
|
||||
Name: "_oauth2_proxy",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
Expire: time.Duration(168) * time.Hour,
|
||||
Refresh: time.Duration(0),
|
||||
},
|
||||
SessionOptions: options.SessionOptions{
|
||||
Session: options.SessionOptions{
|
||||
Type: "cookie",
|
||||
},
|
||||
SetXAuthRequest: false,
|
||||
@ -227,7 +224,7 @@ func (o *Options) Validate() error {
|
||||
}
|
||||
|
||||
msgs := make([]string, 0)
|
||||
if o.CookieSecret == "" {
|
||||
if o.Cookie.Secret == "" {
|
||||
msgs = append(msgs, "missing setting: cookie-secret")
|
||||
}
|
||||
if o.ClientID == "" {
|
||||
@ -372,61 +369,61 @@ func (o *Options) Validate() error {
|
||||
}
|
||||
|
||||
for _, u := range o.SkipAuthRegex {
|
||||
CompiledRegex, err := regexp.Compile(u)
|
||||
compiledRegex, err := regexp.Compile(u)
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("error compiling regex=%q %s", u, err))
|
||||
continue
|
||||
}
|
||||
o.CompiledRegex = append(o.CompiledRegex, CompiledRegex)
|
||||
o.compiledRegex = append(o.compiledRegex, compiledRegex)
|
||||
}
|
||||
msgs = parseProviderInfo(o, msgs)
|
||||
|
||||
var cipher *encryption.Cipher
|
||||
if o.PassAccessToken || o.SetAuthorization || o.PassAuthorization || (o.CookieRefresh != time.Duration(0)) {
|
||||
if o.PassAccessToken || o.SetAuthorization || o.PassAuthorization || (o.Cookie.Refresh != time.Duration(0)) {
|
||||
validCookieSecretSize := false
|
||||
for _, i := range []int{16, 24, 32} {
|
||||
if len(secretBytes(o.CookieSecret)) == i {
|
||||
if len(secretBytes(o.Cookie.Secret)) == i {
|
||||
validCookieSecretSize = true
|
||||
}
|
||||
}
|
||||
var decoded bool
|
||||
if string(secretBytes(o.CookieSecret)) != o.CookieSecret {
|
||||
if string(secretBytes(o.Cookie.Secret)) != o.Cookie.Secret {
|
||||
decoded = true
|
||||
}
|
||||
if !validCookieSecretSize {
|
||||
var suffix string
|
||||
if decoded {
|
||||
suffix = fmt.Sprintf(" note: cookie secret was base64 decoded from %q", o.CookieSecret)
|
||||
suffix = fmt.Sprintf(" note: cookie secret was base64 decoded from %q", o.Cookie.Secret)
|
||||
}
|
||||
msgs = append(msgs, fmt.Sprintf(
|
||||
"cookie_secret must be 16, 24, or 32 bytes "+
|
||||
"to create an AES cipher when "+
|
||||
"pass_access_token == true or "+
|
||||
"cookie_refresh != 0, but is %d bytes.%s",
|
||||
len(secretBytes(o.CookieSecret)), suffix))
|
||||
len(secretBytes(o.Cookie.Secret)), suffix))
|
||||
} else {
|
||||
var err error
|
||||
cipher, err = encryption.NewCipher(secretBytes(o.CookieSecret))
|
||||
cipher, err = encryption.NewCipher(secretBytes(o.Cookie.Secret))
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("cookie-secret error: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o.SessionOptions.Cipher = cipher
|
||||
sessionStore, err := sessions.NewSessionStore(&o.SessionOptions, &o.CookieOptions)
|
||||
o.Session.Cipher = cipher
|
||||
sessionStore, err := sessions.NewSessionStore(&o.Session, &o.Cookie)
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("error initialising session storage: %v", err))
|
||||
} else {
|
||||
o.sessionStore = sessionStore
|
||||
}
|
||||
|
||||
if o.CookieRefresh >= o.CookieExpire {
|
||||
if o.Cookie.Refresh >= o.Cookie.Expire {
|
||||
msgs = append(msgs, fmt.Sprintf(
|
||||
"cookie_refresh (%s) must be less than "+
|
||||
"cookie_expire (%s)",
|
||||
o.CookieRefresh.String(),
|
||||
o.CookieExpire.String()))
|
||||
o.Cookie.Refresh.String(),
|
||||
o.Cookie.Expire.String()))
|
||||
}
|
||||
|
||||
if len(o.GoogleGroups) > 0 || o.GoogleAdminEmail != "" || o.GoogleServiceAccountJSON != "" {
|
||||
@ -441,16 +438,16 @@ func (o *Options) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
switch o.CookieSameSite {
|
||||
switch o.Cookie.SameSite {
|
||||
case "", "none", "lax", "strict":
|
||||
default:
|
||||
msgs = append(msgs, fmt.Sprintf("cookie_samesite (%s) must be one of ['', 'lax', 'strict', 'none']", o.CookieSameSite))
|
||||
msgs = append(msgs, fmt.Sprintf("cookie_samesite (%s) must be one of ['', 'lax', 'strict', 'none']", o.Cookie.SameSite))
|
||||
}
|
||||
|
||||
// Sort cookie domains by length, so that we try longer (and more specific)
|
||||
// domains first
|
||||
sort.Slice(o.CookieDomains, func(i, j int) bool {
|
||||
return len(o.CookieDomains[i]) > len(o.CookieDomains[j])
|
||||
sort.Slice(o.Cookie.Domains, func(i, j int) bool {
|
||||
return len(o.Cookie.Domains[i]) > len(o.Cookie.Domains[j])
|
||||
})
|
||||
|
||||
msgs = parseSignatureKey(o, msgs)
|
||||
@ -627,9 +624,9 @@ func newVerifierFromJwtIssuer(jwtIssuer jwtIssuer) (*oidc.IDTokenVerifier, error
|
||||
}
|
||||
|
||||
func validateCookieName(o *Options, msgs []string) []string {
|
||||
cookie := &http.Cookie{Name: o.CookieName}
|
||||
cookie := &http.Cookie{Name: o.Cookie.Name}
|
||||
if cookie.String() == "" {
|
||||
return append(msgs, fmt.Sprintf("invalid cookie name: %q", o.CookieName))
|
||||
return append(msgs, fmt.Sprintf("invalid cookie name: %q", o.Cookie.Name))
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ const (
|
||||
func testOptions() *Options {
|
||||
o := NewOptions()
|
||||
o.Upstreams = append(o.Upstreams, "http://127.0.0.1:8080/")
|
||||
o.CookieSecret = cookieSecret
|
||||
o.Cookie.Secret = cookieSecret
|
||||
o.ClientID = clientID
|
||||
o.ClientSecret = clientSecret
|
||||
o.EmailDomains = []string{"*"}
|
||||
@ -51,7 +51,7 @@ func TestNewOptions(t *testing.T) {
|
||||
|
||||
func TestClientSecretFileOptionFails(t *testing.T) {
|
||||
o := NewOptions()
|
||||
o.CookieSecret = cookieSecret
|
||||
o.Cookie.Secret = cookieSecret
|
||||
o.ClientID = clientID
|
||||
o.ClientSecretFile = clientSecret
|
||||
o.EmailDomains = []string{"*"}
|
||||
@ -81,7 +81,7 @@ func TestClientSecretFileOption(t *testing.T) {
|
||||
defer os.Remove(clientSecretFileName)
|
||||
|
||||
o := NewOptions()
|
||||
o.CookieSecret = cookieSecret
|
||||
o.Cookie.Secret = cookieSecret
|
||||
o.ClientID = clientID
|
||||
o.ClientSecretFile = clientSecretFileName
|
||||
o.EmailDomains = []string{"*"}
|
||||
@ -165,7 +165,7 @@ func TestCompiledRegex(t *testing.T) {
|
||||
o.SkipAuthRegex = regexps
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
actual := make([]string, 0)
|
||||
for _, regex := range o.CompiledRegex {
|
||||
for _, regex := range o.compiledRegex {
|
||||
actual = append(actual, regex.String())
|
||||
}
|
||||
assert.Equal(t, regexps, actual)
|
||||
@ -212,20 +212,20 @@ func TestPassAccessTokenRequiresSpecificCookieSecretLengths(t *testing.T) {
|
||||
|
||||
assert.Equal(t, false, o.PassAccessToken)
|
||||
o.PassAccessToken = true
|
||||
o.CookieSecret = "cookie of invalid length-"
|
||||
o.Cookie.Secret = "cookie of invalid length-"
|
||||
assert.NotEqual(t, nil, o.Validate())
|
||||
|
||||
o.PassAccessToken = false
|
||||
o.CookieRefresh = time.Duration(24) * time.Hour
|
||||
o.Cookie.Refresh = time.Duration(24) * time.Hour
|
||||
assert.NotEqual(t, nil, o.Validate())
|
||||
|
||||
o.CookieSecret = "16 bytes AES-128"
|
||||
o.Cookie.Secret = "16 bytes AES-128"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
o.CookieSecret = "24 byte secret AES-192--"
|
||||
o.Cookie.Secret = "24 byte secret AES-192--"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
o.CookieSecret = "32 byte secret for AES-256------"
|
||||
o.Cookie.Secret = "32 byte secret for AES-256------"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
}
|
||||
|
||||
@ -233,11 +233,11 @@ func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
|
||||
o := testOptions()
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
o.CookieSecret = "0123456789abcdefabcd"
|
||||
o.CookieRefresh = o.CookieExpire
|
||||
o.Cookie.Secret = "0123456789abcdefabcd"
|
||||
o.Cookie.Refresh = o.Cookie.Expire
|
||||
assert.NotEqual(t, nil, o.Validate())
|
||||
|
||||
o.CookieRefresh -= time.Duration(1)
|
||||
o.Cookie.Refresh -= time.Duration(1)
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
}
|
||||
|
||||
@ -246,23 +246,23 @@ func TestBase64CookieSecret(t *testing.T) {
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 32 byte, base64 (urlsafe) encoded key
|
||||
o.CookieSecret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ="
|
||||
o.Cookie.Secret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ="
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 32 byte, base64 (urlsafe) encoded key, w/o padding
|
||||
o.CookieSecret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ"
|
||||
o.Cookie.Secret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 24 byte, base64 (urlsafe) encoded key
|
||||
o.CookieSecret = "Kp33Gj-GQmYtz4zZUyUDdqQKx5_Hgkv3"
|
||||
o.Cookie.Secret = "Kp33Gj-GQmYtz4zZUyUDdqQKx5_Hgkv3"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 16 byte, base64 (urlsafe) encoded key
|
||||
o.CookieSecret = "LFEqZYvYUwKwzn0tEuTpLA=="
|
||||
o.Cookie.Secret = "LFEqZYvYUwKwzn0tEuTpLA=="
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 16 byte, base64 (urlsafe) encoded key, w/o padding
|
||||
o.CookieSecret = "LFEqZYvYUwKwzn0tEuTpLA"
|
||||
o.Cookie.Secret = "LFEqZYvYUwKwzn0tEuTpLA"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
}
|
||||
|
||||
@ -292,16 +292,16 @@ func TestValidateSignatureKeyUnsupportedAlgorithm(t *testing.T) {
|
||||
|
||||
func TestValidateCookie(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.CookieName = "_valid_cookie_name"
|
||||
o.Cookie.Name = "_valid_cookie_name"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
}
|
||||
|
||||
func TestValidateCookieBadName(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.CookieName = "_bad_cookie_name{}"
|
||||
o.Cookie.Name = "_bad_cookie_name{}"
|
||||
err := o.Validate()
|
||||
assert.Equal(t, err.Error(), "invalid configuration:\n"+
|
||||
fmt.Sprintf(" invalid cookie name: %q", o.CookieName))
|
||||
fmt.Sprintf(" invalid cookie name: %q", o.Cookie.Name))
|
||||
}
|
||||
|
||||
func TestSkipOIDCDiscovery(t *testing.T) {
|
||||
|
@ -4,13 +4,13 @@ import "time"
|
||||
|
||||
// CookieOptions contains configuration options relating to Cookie configuration
|
||||
type CookieOptions struct {
|
||||
CookieName string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
|
||||
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
|
||||
CookieDomains []string `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
|
||||
CookiePath string `flag:"cookie-path" cfg:"cookie_path" env:"OAUTH2_PROXY_COOKIE_PATH"`
|
||||
CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE"`
|
||||
CookieRefresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH"`
|
||||
CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure" env:"OAUTH2_PROXY_COOKIE_SECURE"`
|
||||
CookieHTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly" env:"OAUTH2_PROXY_COOKIE_HTTPONLY"`
|
||||
CookieSameSite string `flag:"cookie-samesite" cfg:"cookie_samesite" env:"OAUTH2_PROXY_COOKIE_SAMESITE"`
|
||||
Name string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
|
||||
Secret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
|
||||
Domains []string `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
|
||||
Path string `flag:"cookie-path" cfg:"cookie_path" env:"OAUTH2_PROXY_COOKIE_PATH"`
|
||||
Expire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE"`
|
||||
Refresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH"`
|
||||
Secure bool `flag:"cookie-secure" cfg:"cookie_secure" env:"OAUTH2_PROXY_COOKIE_SECURE"`
|
||||
HTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly" env:"OAUTH2_PROXY_COOKIE_HTTPONLY"`
|
||||
SameSite string `flag:"cookie-samesite" cfg:"cookie_samesite" env:"OAUTH2_PROXY_COOKIE_SAMESITE"`
|
||||
}
|
||||
|
134
pkg/apis/options/load.go
Normal file
134
pkg/apis/options/load.go
Normal file
@ -0,0 +1,134 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Load reads in the config file at the path given, then merges in environment
|
||||
// variables (prefixed with `OAUTH2_PROXY`) and finally merges in flags from the flagSet.
|
||||
// If a config value is unset and the flag has a non-zero value default, this default will be used.
|
||||
// Eg. A field defined:
|
||||
// FooBar `cfg:"foo_bar" flag:"foo-bar"`
|
||||
// Can be set in the config file as `foo_bar="baz"`, in the environment as `OAUTH2_PROXY_FOO_BAR=baz`,
|
||||
// or via the command line flag `--foo-bar=baz`.
|
||||
func Load(configFileName string, flagSet *pflag.FlagSet, into interface{}) error {
|
||||
v := viper.New()
|
||||
v.SetConfigFile(configFileName)
|
||||
v.SetConfigType("toml") // Config is in toml format
|
||||
v.SetEnvPrefix("OAUTH2_PROXY")
|
||||
v.AutomaticEnv()
|
||||
v.SetTypeByDefaultValue(true)
|
||||
|
||||
if configFileName != "" {
|
||||
err := v.ReadInConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err := registerFlags(v, "", flagSet, into)
|
||||
if err != nil {
|
||||
// This should only happen if there is a programming error
|
||||
return fmt.Errorf("unable to register flags: %w", err)
|
||||
}
|
||||
|
||||
// UnmarhsalExact will return an error if the config includes options that are
|
||||
// not mapped to felds of the into struct
|
||||
err = v.UnmarshalExact(into, decodeFromCfgTag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error unmarshalling config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// registerFlags uses `cfg` and `flag` tags to associate flags in the flagSet
|
||||
// to the fields in the options interface provided.
|
||||
// Each exported field in the options must have a `cfg` tag otherwise an error will occur.
|
||||
// - For fields, set `cfg` and `flag` so that `flag` is the name of the flag associated to this config option
|
||||
// - For exported fields that are not user facing, set the `cfg` to `,internal`
|
||||
// - For structs containing user facing fields, set the `cfg` to `,squash`
|
||||
func registerFlags(v *viper.Viper, prefix string, flagSet *pflag.FlagSet, options interface{}) error {
|
||||
val := reflect.ValueOf(options)
|
||||
var typ reflect.Type
|
||||
if val.Kind() == reflect.Ptr {
|
||||
typ = val.Elem().Type()
|
||||
} else {
|
||||
typ = val.Type()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
// pull out the struct tags:
|
||||
// flag - the name of the command line flag
|
||||
// cfg - the name of the config file option
|
||||
field := typ.Field(i)
|
||||
fieldV := reflect.Indirect(val).Field(i)
|
||||
fieldName := strings.Join([]string{prefix, field.Name}, ".")
|
||||
|
||||
cfgName := field.Tag.Get("cfg")
|
||||
if cfgName == ",internal" {
|
||||
// Public but internal types that should not be exposed to users, skip them
|
||||
continue
|
||||
}
|
||||
|
||||
if isUnexported(field.Name) {
|
||||
// Unexported fields cannot be set by a user, so won't have tags or flags, skip them
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
if cfgName != ",squash" {
|
||||
return fmt.Errorf("field %q does not have required cfg tag: `,squash`", fieldName)
|
||||
}
|
||||
err := registerFlags(v, fieldName, flagSet, fieldV.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
flagName := field.Tag.Get("flag")
|
||||
if flagName == "" || cfgName == "" {
|
||||
return fmt.Errorf("field %q does not have required tags (cfg, flag)", fieldName)
|
||||
}
|
||||
|
||||
if flagSet == nil {
|
||||
return fmt.Errorf("flagset cannot be nil")
|
||||
}
|
||||
|
||||
f := flagSet.Lookup(flagName)
|
||||
if f == nil {
|
||||
return fmt.Errorf("field %q does not have a registered flag", flagName)
|
||||
}
|
||||
err := v.BindPFlag(cfgName, f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error binding flag for field %q: %w", fieldName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeFromCfgTag sets the Viper decoder to read the names from the `cfg` tag
|
||||
// on each struct entry.
|
||||
func decodeFromCfgTag(c *mapstructure.DecoderConfig) {
|
||||
c.TagName = "cfg"
|
||||
}
|
||||
|
||||
// isUnexported checks if a field name starts with a lowercase letter and therefore
|
||||
// if it is unexported.
|
||||
func isUnexported(name string) bool {
|
||||
if len(name) == 0 {
|
||||
// This should never happen
|
||||
panic("field name has len 0")
|
||||
}
|
||||
|
||||
first := string(name[0])
|
||||
return first == strings.ToLower(first)
|
||||
}
|
300
pkg/apis/options/load_test.go
Normal file
300
pkg/apis/options/load_test.go
Normal file
@ -0,0 +1,300 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestOptionsSuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Options Suite")
|
||||
}
|
||||
|
||||
var _ = Describe("Load", func() {
|
||||
Context("with a testOptions structure", func() {
|
||||
type TestOptionSubStruct struct {
|
||||
StringSliceOption []string `flag:"string-slice-option" cfg:"string_slice_option"`
|
||||
}
|
||||
|
||||
type TestOptions struct {
|
||||
StringOption string `flag:"string-option" cfg:"string_option"`
|
||||
Sub TestOptionSubStruct `cfg:",squash"`
|
||||
// Check exported but internal fields do not break loading
|
||||
Internal *string `cfg:",internal"`
|
||||
// Check unexported fields do not break loading
|
||||
unexported string
|
||||
}
|
||||
|
||||
type MissingSquashTestOptions struct {
|
||||
StringOption string `flag:"string-option" cfg:"string_option"`
|
||||
Sub TestOptionSubStruct
|
||||
}
|
||||
|
||||
type MissingCfgTestOptions struct {
|
||||
StringOption string `flag:"string-option"`
|
||||
Sub TestOptionSubStruct `cfg:",squash"`
|
||||
}
|
||||
|
||||
type MissingFlagTestOptions struct {
|
||||
StringOption string `cfg:"string_option"`
|
||||
Sub TestOptionSubStruct `cfg:",squash"`
|
||||
}
|
||||
|
||||
var testOptionsConfigBytes = []byte(`
|
||||
string_option="foo"
|
||||
string_slice_option="a,b,c,d"
|
||||
`)
|
||||
|
||||
var testOptionsFlagSet *pflag.FlagSet
|
||||
|
||||
type testOptionsTableInput struct {
|
||||
env map[string]string
|
||||
args []string
|
||||
configFile []byte
|
||||
flagSet func() *pflag.FlagSet
|
||||
expectedErr error
|
||||
input interface{}
|
||||
expectedOutput interface{}
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
testOptionsFlagSet = pflag.NewFlagSet("testFlagSet", pflag.ExitOnError)
|
||||
testOptionsFlagSet.String("string-option", "default", "")
|
||||
testOptionsFlagSet.StringSlice("string-slice-option", []string{"a", "b"}, "")
|
||||
})
|
||||
|
||||
DescribeTable("Load",
|
||||
func(o *testOptionsTableInput) {
|
||||
var configFileName string
|
||||
|
||||
if o.configFile != nil {
|
||||
By("Creating a config file")
|
||||
configFile, err := ioutil.TempFile("", "oauth2-proxy-test-legacy-config-file")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer configFile.Close()
|
||||
|
||||
_, err = configFile.Write(o.configFile)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.Remove(configFile.Name())
|
||||
|
||||
configFileName = configFile.Name()
|
||||
}
|
||||
|
||||
if len(o.env) > 0 {
|
||||
By("Setting environment variables")
|
||||
for k, v := range o.env {
|
||||
os.Setenv(k, v)
|
||||
defer os.Unsetenv(k)
|
||||
}
|
||||
}
|
||||
|
||||
Expect(o.flagSet).ToNot(BeNil())
|
||||
flagSet := o.flagSet()
|
||||
Expect(flagSet).ToNot(BeNil())
|
||||
|
||||
if len(o.args) > 0 {
|
||||
By("Parsing flag arguments")
|
||||
Expect(flagSet.Parse(o.args)).To(Succeed())
|
||||
}
|
||||
|
||||
var input interface{}
|
||||
if o.input != nil {
|
||||
input = o.input
|
||||
} else {
|
||||
input = &TestOptions{}
|
||||
}
|
||||
err := Load(configFileName, flagSet, input)
|
||||
if o.expectedErr != nil {
|
||||
Expect(err).To(MatchError(o.expectedErr.Error()))
|
||||
} else {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(input).To(Equal(o.expectedOutput))
|
||||
},
|
||||
Entry("with just a config file", &testOptionsTableInput{
|
||||
configFile: testOptionsConfigBytes,
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "foo",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c", "d"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("when setting env variables", &testOptionsTableInput{
|
||||
configFile: testOptionsConfigBytes,
|
||||
env: map[string]string{
|
||||
"OAUTH2_PROXY_STRING_OPTION": "bar",
|
||||
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
|
||||
},
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "bar",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("when setting flags", &testOptionsTableInput{
|
||||
configFile: testOptionsConfigBytes,
|
||||
env: map[string]string{
|
||||
"OAUTH2_PROXY_STRING_OPTION": "bar",
|
||||
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
|
||||
},
|
||||
args: []string{
|
||||
"--string-option", "baz",
|
||||
"--string-slice-option", "a,b,c,d,e",
|
||||
},
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "baz",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c", "d", "e"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("when setting flags multiple times", &testOptionsTableInput{
|
||||
configFile: testOptionsConfigBytes,
|
||||
env: map[string]string{
|
||||
"OAUTH2_PROXY_STRING_OPTION": "bar",
|
||||
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
|
||||
},
|
||||
args: []string{
|
||||
"--string-option", "baz",
|
||||
"--string-slice-option", "x",
|
||||
"--string-slice-option", "y",
|
||||
"--string-slice-option", "z",
|
||||
},
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "baz",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"x", "y", "z"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("when setting env variables without a config file", &testOptionsTableInput{
|
||||
env: map[string]string{
|
||||
"OAUTH2_PROXY_STRING_OPTION": "bar",
|
||||
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
|
||||
},
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "bar",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("when setting flags without a config file", &testOptionsTableInput{
|
||||
env: map[string]string{
|
||||
"OAUTH2_PROXY_STRING_OPTION": "bar",
|
||||
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
|
||||
},
|
||||
args: []string{
|
||||
"--string-option", "baz",
|
||||
"--string-slice-option", "a,b,c,d,e",
|
||||
},
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "baz",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c", "d", "e"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("when setting flags without a config file", &testOptionsTableInput{
|
||||
env: map[string]string{
|
||||
"OAUTH2_PROXY_STRING_OPTION": "bar",
|
||||
"OAUTH2_PROXY_STRING_SLICE_OPTION": "a,b,c",
|
||||
},
|
||||
args: []string{
|
||||
"--string-option", "baz",
|
||||
"--string-slice-option", "a,b,c,d,e",
|
||||
},
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "baz",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c", "d", "e"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("when nothing is set it should use flag defaults", &testOptionsTableInput{
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "default",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("with an invalid config file", &testOptionsTableInput{
|
||||
configFile: []byte(`slice_option = foo`),
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedErr: fmt.Errorf("unable to load config file: While parsing config: (1, 16): never reached"),
|
||||
expectedOutput: &TestOptions{},
|
||||
}),
|
||||
Entry("with an invalid flagset", &testOptionsTableInput{
|
||||
flagSet: func() *pflag.FlagSet {
|
||||
// Missing a flag
|
||||
f := pflag.NewFlagSet("testFlagSet", pflag.ExitOnError)
|
||||
f.String("string-option", "default", "")
|
||||
return f
|
||||
},
|
||||
expectedErr: fmt.Errorf("unable to register flags: field \"string-slice-option\" does not have a registered flag"),
|
||||
expectedOutput: &TestOptions{},
|
||||
}),
|
||||
Entry("with an struct is missing the squash tag", &testOptionsTableInput{
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedErr: fmt.Errorf("unable to register flags: field \".Sub\" does not have required cfg tag: `,squash`"),
|
||||
input: &MissingSquashTestOptions{},
|
||||
expectedOutput: &MissingSquashTestOptions{},
|
||||
}),
|
||||
Entry("with a field is missing the cfg tag", &testOptionsTableInput{
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedErr: fmt.Errorf("unable to register flags: field \".StringOption\" does not have required tags (cfg, flag)"),
|
||||
input: &MissingCfgTestOptions{},
|
||||
expectedOutput: &MissingCfgTestOptions{},
|
||||
}),
|
||||
Entry("with a field is missing the flag tag", &testOptionsTableInput{
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedErr: fmt.Errorf("unable to register flags: field \".StringOption\" does not have required tags (cfg, flag)"),
|
||||
input: &MissingFlagTestOptions{},
|
||||
expectedOutput: &MissingFlagTestOptions{},
|
||||
}),
|
||||
Entry("with existing unexported fields", &testOptionsTableInput{
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
input: &TestOptions{
|
||||
unexported: "unexported",
|
||||
},
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "default",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b"},
|
||||
},
|
||||
unexported: "unexported",
|
||||
},
|
||||
}),
|
||||
Entry("with an unknown option in the config file", &testOptionsTableInput{
|
||||
configFile: []byte(`unknown_option="foo"`),
|
||||
flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
|
||||
expectedErr: fmt.Errorf("error unmarshalling config: 1 error(s) decoding:\n\n* '' has invalid keys: unknown_option"),
|
||||
// Viper will unmarshal before returning the error, so this is the default output
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "default",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
@ -5,30 +5,26 @@ import "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
|
||||
// SessionOptions contains configuration options for the SessionStore providers.
|
||||
type SessionOptions struct {
|
||||
Type string `flag:"session-store-type" cfg:"session_store_type" env:"OAUTH2_PROXY_SESSION_STORE_TYPE"`
|
||||
Cipher *encryption.Cipher
|
||||
CookieStoreOptions
|
||||
RedisStoreOptions
|
||||
Cipher *encryption.Cipher `cfg:",internal"`
|
||||
Redis RedisStoreOptions `cfg:",squash"`
|
||||
}
|
||||
|
||||
// CookieSessionStoreType is used to indicate the CookieSessionStore should be
|
||||
// used for storing sessions.
|
||||
var CookieSessionStoreType = "cookie"
|
||||
|
||||
// CookieStoreOptions contains configuration options for the CookieSessionStore.
|
||||
type CookieStoreOptions struct{}
|
||||
|
||||
// RedisSessionStoreType is used to indicate the RedisSessionStore should be
|
||||
// used for storing sessions.
|
||||
var RedisSessionStoreType = "redis"
|
||||
|
||||
// RedisStoreOptions contains configuration options for the RedisSessionStore.
|
||||
type RedisStoreOptions struct {
|
||||
RedisConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url" env:"OAUTH2_PROXY_REDIS_CONNECTION_URL"`
|
||||
ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url" env:"OAUTH2_PROXY_REDIS_CONNECTION_URL"`
|
||||
UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel" env:"OAUTH2_PROXY_REDIS_USE_SENTINEL"`
|
||||
SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name" env:"OAUTH2_PROXY_REDIS_SENTINEL_MASTER_NAME"`
|
||||
SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls" env:"OAUTH2_PROXY_REDIS_SENTINEL_CONNECTION_URLS"`
|
||||
UseCluster bool `flag:"redis-use-cluster" cfg:"redis_use_cluster" env:"OAUTH2_PROXY_REDIS_USE_CLUSTER"`
|
||||
ClusterConnectionURLs []string `flag:"redis-cluster-connection-urls" cfg:"redis_cluster_connection_urls" env:"OAUTH2_PROXY_REDIS_CLUSTER_CONNECTION_URLS"`
|
||||
RedisCAPath string `flag:"redis-ca-path" cfg:"redis_ca_path" env:"OAUTH2_PROXY_REDIS_CA_PATH"`
|
||||
RedisInsecureTLS bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify" env:"OAUTH2_PROXY_REDIS_INSECURE_SKIP_TLS_VERIFY"`
|
||||
CAPath string `flag:"redis-ca-path" cfg:"redis_ca_path" env:"OAUTH2_PROXY_REDIS_CA_PATH"`
|
||||
InsecureSkipTLSVerify bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify" env:"OAUTH2_PROXY_REDIS_INSECURE_SKIP_TLS_VERIFY"`
|
||||
}
|
||||
|
@ -38,19 +38,19 @@ func MakeCookie(req *http.Request, name string, value string, path string, domai
|
||||
|
||||
// MakeCookieFromOptions constructs a cookie based on the given *options.CookieOptions,
|
||||
// value and creation time
|
||||
func MakeCookieFromOptions(req *http.Request, name string, value string, opts *options.CookieOptions, expiration time.Duration, now time.Time) *http.Cookie {
|
||||
domain := GetCookieDomain(req, opts.CookieDomains)
|
||||
func MakeCookieFromOptions(req *http.Request, name string, value string, cookieOpts *options.CookieOptions, expiration time.Duration, now time.Time) *http.Cookie {
|
||||
domain := GetCookieDomain(req, cookieOpts.Domains)
|
||||
|
||||
if domain != "" {
|
||||
return MakeCookie(req, name, value, opts.CookiePath, domain, opts.CookieHTTPOnly, opts.CookieSecure, expiration, now, ParseSameSite(opts.CookieSameSite))
|
||||
return MakeCookie(req, name, value, cookieOpts.Path, domain, cookieOpts.HTTPOnly, cookieOpts.Secure, expiration, now, ParseSameSite(cookieOpts.SameSite))
|
||||
}
|
||||
// If nothing matches, create the cookie with the shortest domain
|
||||
logger.Printf("Warning: request host %q did not match any of the specific cookie domains of %q", GetRequestHost(req), strings.Join(opts.CookieDomains, ","))
|
||||
logger.Printf("Warning: request host %q did not match any of the specific cookie domains of %q", GetRequestHost(req), strings.Join(cookieOpts.Domains, ","))
|
||||
defaultDomain := ""
|
||||
if len(opts.CookieDomains) > 0 {
|
||||
defaultDomain = opts.CookieDomains[len(opts.CookieDomains)-1]
|
||||
if len(cookieOpts.Domains) > 0 {
|
||||
defaultDomain = cookieOpts.Domains[len(cookieOpts.Domains)-1]
|
||||
}
|
||||
return MakeCookie(req, name, value, opts.CookiePath, defaultDomain, opts.CookieHTTPOnly, opts.CookieSecure, expiration, now, ParseSameSite(opts.CookieSameSite))
|
||||
return MakeCookie(req, name, value, cookieOpts.Path, defaultDomain, cookieOpts.HTTPOnly, cookieOpts.Secure, expiration, now, ParseSameSite(cookieOpts.SameSite))
|
||||
}
|
||||
|
||||
// GetCookieDomain returns the correct cookie domain given a list of domains
|
||||
|
@ -49,12 +49,12 @@ func (s *SessionStore) Save(rw http.ResponseWriter, req *http.Request, ss *sessi
|
||||
// Load reads sessions.SessionState information from Cookies within the
|
||||
// HTTP request object
|
||||
func (s *SessionStore) Load(req *http.Request) (*sessions.SessionState, error) {
|
||||
c, err := loadCookie(req, s.CookieOptions.CookieName)
|
||||
c, err := loadCookie(req, s.CookieOptions.Name)
|
||||
if err != nil {
|
||||
// always http.ErrNoCookie
|
||||
return nil, fmt.Errorf("cookie %q not present", s.CookieOptions.CookieName)
|
||||
return nil, fmt.Errorf("cookie %q not present", s.CookieOptions.Name)
|
||||
}
|
||||
val, _, ok := encryption.Validate(c, s.CookieOptions.CookieSecret, s.CookieOptions.CookieExpire)
|
||||
val, _, ok := encryption.Validate(c, s.CookieOptions.Secret, s.CookieOptions.Expire)
|
||||
if !ok {
|
||||
return nil, errors.New("cookie signature not valid")
|
||||
}
|
||||
@ -70,7 +70,7 @@ func (s *SessionStore) Load(req *http.Request) (*sessions.SessionState, error) {
|
||||
// clear the session
|
||||
func (s *SessionStore) Clear(rw http.ResponseWriter, req *http.Request) error {
|
||||
// matches CookieName, CookieName_<number>
|
||||
var cookieNameRegex = regexp.MustCompile(fmt.Sprintf("^%s(_\\d+)?$", s.CookieOptions.CookieName))
|
||||
var cookieNameRegex = regexp.MustCompile(fmt.Sprintf("^%s(_\\d+)?$", s.CookieOptions.Name))
|
||||
|
||||
for _, c := range req.Cookies() {
|
||||
if cookieNameRegex.MatchString(c.Name) {
|
||||
@ -94,10 +94,10 @@ func (s *SessionStore) setSessionCookie(rw http.ResponseWriter, req *http.Reques
|
||||
// authentication details
|
||||
func (s *SessionStore) makeSessionCookie(req *http.Request, value string, now time.Time) []*http.Cookie {
|
||||
if value != "" {
|
||||
value = encryption.SignedValue(s.CookieOptions.CookieSecret, s.CookieOptions.CookieName, value, now)
|
||||
value = encryption.SignedValue(s.CookieOptions.Secret, s.CookieOptions.Name, value, now)
|
||||
}
|
||||
c := s.makeCookie(req, s.CookieOptions.CookieName, value, s.CookieOptions.CookieExpire, now)
|
||||
if len(c.Value) > 4096-len(s.CookieOptions.CookieName) {
|
||||
c := s.makeCookie(req, s.CookieOptions.Name, value, s.CookieOptions.Expire, now)
|
||||
if len(c.Value) > 4096-len(s.CookieOptions.Name) {
|
||||
return splitCookie(c)
|
||||
}
|
||||
return []*http.Cookie{c}
|
||||
|
@ -40,7 +40,7 @@ type SessionStore struct {
|
||||
// NewRedisSessionStore initialises a new instance of the SessionStore from
|
||||
// the configuration given
|
||||
func NewRedisSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessions.SessionStore, error) {
|
||||
client, err := newRedisCmdable(opts.RedisStoreOptions)
|
||||
client, err := newRedisCmdable(opts.Redis)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error constructing redis client: %v", err)
|
||||
}
|
||||
@ -74,16 +74,16 @@ func newRedisCmdable(opts options.RedisStoreOptions) (Client, error) {
|
||||
return newClusterClient(client), nil
|
||||
}
|
||||
|
||||
opt, err := redis.ParseURL(opts.RedisConnectionURL)
|
||||
opt, err := redis.ParseURL(opts.ConnectionURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse redis url: %s", err)
|
||||
}
|
||||
|
||||
if opts.RedisInsecureTLS {
|
||||
if opts.InsecureSkipTLSVerify {
|
||||
opt.TLSConfig.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
if opts.RedisCAPath != "" {
|
||||
if opts.CAPath != "" {
|
||||
rootCAs, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
logger.Printf("failed to load system cert pool for redis connection, falling back to empty cert pool")
|
||||
@ -91,9 +91,9 @@ func newRedisCmdable(opts options.RedisStoreOptions) (Client, error) {
|
||||
if rootCAs == nil {
|
||||
rootCAs = x509.NewCertPool()
|
||||
}
|
||||
certs, err := ioutil.ReadFile(opts.RedisCAPath)
|
||||
certs, err := ioutil.ReadFile(opts.CAPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load %q, %v", opts.RedisCAPath, err)
|
||||
return nil, fmt.Errorf("failed to load %q, %v", opts.CAPath, err)
|
||||
}
|
||||
|
||||
// Append our cert to the system pool
|
||||
@ -117,13 +117,13 @@ func (store *SessionStore) Save(rw http.ResponseWriter, req *http.Request, s *se
|
||||
|
||||
// Old sessions that we are refreshing would have a request cookie
|
||||
// New sessions don't, so we ignore the error. storeValue will check requestCookie
|
||||
requestCookie, _ := req.Cookie(store.CookieOptions.CookieName)
|
||||
requestCookie, _ := req.Cookie(store.CookieOptions.Name)
|
||||
value, err := s.EncodeSessionState(store.CookieCipher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx := req.Context()
|
||||
ticketString, err := store.storeValue(ctx, value, store.CookieOptions.CookieExpire, requestCookie)
|
||||
ticketString, err := store.storeValue(ctx, value, store.CookieOptions.Expire, requestCookie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -131,7 +131,7 @@ func (store *SessionStore) Save(rw http.ResponseWriter, req *http.Request, s *se
|
||||
ticketCookie := store.makeCookie(
|
||||
req,
|
||||
ticketString,
|
||||
store.CookieOptions.CookieExpire,
|
||||
store.CookieOptions.Expire,
|
||||
s.CreatedAt,
|
||||
)
|
||||
|
||||
@ -142,12 +142,12 @@ func (store *SessionStore) Save(rw http.ResponseWriter, req *http.Request, s *se
|
||||
// Load reads sessions.SessionState information from a ticket
|
||||
// cookie within the HTTP request object
|
||||
func (store *SessionStore) Load(req *http.Request) (*sessions.SessionState, error) {
|
||||
requestCookie, err := req.Cookie(store.CookieOptions.CookieName)
|
||||
requestCookie, err := req.Cookie(store.CookieOptions.Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading session: %s", err)
|
||||
}
|
||||
|
||||
val, _, ok := encryption.Validate(requestCookie, store.CookieOptions.CookieSecret, store.CookieOptions.CookieExpire)
|
||||
val, _, ok := encryption.Validate(requestCookie, store.CookieOptions.Secret, store.CookieOptions.Expire)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cookie signature not valid")
|
||||
}
|
||||
@ -161,12 +161,12 @@ func (store *SessionStore) Load(req *http.Request) (*sessions.SessionState, erro
|
||||
|
||||
// loadSessionFromString loads the session based on the ticket value
|
||||
func (store *SessionStore) loadSessionFromString(ctx context.Context, value string) (*sessions.SessionState, error) {
|
||||
ticket, err := decodeTicket(store.CookieOptions.CookieName, value)
|
||||
ticket, err := decodeTicket(store.CookieOptions.Name, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultBytes, err := store.Client.Get(ctx, ticket.asHandle(store.CookieOptions.CookieName))
|
||||
resultBytes, err := store.Client.Get(ctx, ticket.asHandle(store.CookieOptions.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -199,7 +199,7 @@ func (store *SessionStore) Clear(rw http.ResponseWriter, req *http.Request) erro
|
||||
http.SetCookie(rw, clearCookie)
|
||||
|
||||
// If there was an existing cookie we should clear the session in redis
|
||||
requestCookie, err := req.Cookie(store.CookieOptions.CookieName)
|
||||
requestCookie, err := req.Cookie(store.CookieOptions.Name)
|
||||
if err != nil && err == http.ErrNoCookie {
|
||||
// No existing cookie so can't clear redis
|
||||
return nil
|
||||
@ -207,17 +207,17 @@ func (store *SessionStore) Clear(rw http.ResponseWriter, req *http.Request) erro
|
||||
return fmt.Errorf("error retrieving cookie: %v", err)
|
||||
}
|
||||
|
||||
val, _, ok := encryption.Validate(requestCookie, store.CookieOptions.CookieSecret, store.CookieOptions.CookieExpire)
|
||||
val, _, ok := encryption.Validate(requestCookie, store.CookieOptions.Secret, store.CookieOptions.Expire)
|
||||
if !ok {
|
||||
return fmt.Errorf("cookie signature not valid")
|
||||
}
|
||||
|
||||
// We only return an error if we had an issue with redis
|
||||
// If there's an issue decoding the ticket, ignore it
|
||||
ticket, _ := decodeTicket(store.CookieOptions.CookieName, val)
|
||||
ticket, _ := decodeTicket(store.CookieOptions.Name, val)
|
||||
if ticket != nil {
|
||||
ctx := req.Context()
|
||||
err := store.Client.Del(ctx, ticket.asHandle(store.CookieOptions.CookieName))
|
||||
err := store.Client.Del(ctx, ticket.asHandle(store.CookieOptions.Name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error clearing cookie from redis: %s", err)
|
||||
}
|
||||
@ -228,11 +228,11 @@ func (store *SessionStore) Clear(rw http.ResponseWriter, req *http.Request) erro
|
||||
// makeCookie makes a cookie, signing the value if present
|
||||
func (store *SessionStore) makeCookie(req *http.Request, value string, expires time.Duration, now time.Time) *http.Cookie {
|
||||
if value != "" {
|
||||
value = encryption.SignedValue(store.CookieOptions.CookieSecret, store.CookieOptions.CookieName, value, now)
|
||||
value = encryption.SignedValue(store.CookieOptions.Secret, store.CookieOptions.Name, value, now)
|
||||
}
|
||||
return cookies.MakeCookieFromOptions(
|
||||
req,
|
||||
store.CookieOptions.CookieName,
|
||||
store.CookieOptions.Name,
|
||||
value,
|
||||
store.CookieOptions,
|
||||
expires,
|
||||
@ -256,12 +256,12 @@ func (store *SessionStore) storeValue(ctx context.Context, value string, expirat
|
||||
stream := cipher.NewCFBEncrypter(block, ticket.Secret)
|
||||
stream.XORKeyStream(ciphertext, []byte(value))
|
||||
|
||||
handle := ticket.asHandle(store.CookieOptions.CookieName)
|
||||
handle := ticket.asHandle(store.CookieOptions.Name)
|
||||
err = store.Client.Set(ctx, handle, ciphertext, expiration)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ticket.encodeTicket(store.CookieOptions.CookieName), nil
|
||||
return ticket.encodeTicket(store.CookieOptions.Name), nil
|
||||
}
|
||||
|
||||
// getTicket retrieves an existing ticket from the cookie if present,
|
||||
@ -272,14 +272,14 @@ func (store *SessionStore) getTicket(requestCookie *http.Cookie) (*TicketData, e
|
||||
}
|
||||
|
||||
// An existing cookie exists, try to retrieve the ticket
|
||||
val, _, ok := encryption.Validate(requestCookie, store.CookieOptions.CookieSecret, store.CookieOptions.CookieExpire)
|
||||
val, _, ok := encryption.Validate(requestCookie, store.CookieOptions.Secret, store.CookieOptions.Expire)
|
||||
if !ok {
|
||||
// Cookie is invalid, create a new ticket
|
||||
return newTicket()
|
||||
}
|
||||
|
||||
// Valid cookie, decode the ticket
|
||||
ticket, err := decodeTicket(store.CookieOptions.CookieName, val)
|
||||
ticket, err := decodeTicket(store.CookieOptions.Name, val)
|
||||
if err != nil {
|
||||
// If we can't decode the ticket we have to create a new one
|
||||
return newTicket()
|
||||
|
@ -47,25 +47,25 @@ var _ = Describe("NewSessionStore", func() {
|
||||
|
||||
It("have the correct name set", func() {
|
||||
if len(cookies) == 1 {
|
||||
Expect(cookies[0].Name).To(Equal(cookieOpts.CookieName))
|
||||
Expect(cookies[0].Name).To(Equal(cookieOpts.Name))
|
||||
} else {
|
||||
for _, cookie := range cookies {
|
||||
Expect(cookie.Name).To(ContainSubstring(cookieOpts.CookieName))
|
||||
Expect(cookie.Name).To(ContainSubstring(cookieOpts.Name))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
It("have the correct path set", func() {
|
||||
for _, cookie := range cookies {
|
||||
Expect(cookie.Path).To(Equal(cookieOpts.CookiePath))
|
||||
Expect(cookie.Path).To(Equal(cookieOpts.Path))
|
||||
}
|
||||
})
|
||||
|
||||
It("have the correct domain set", func() {
|
||||
for _, cookie := range cookies {
|
||||
specifiedDomain := ""
|
||||
if len(cookieOpts.CookieDomains) > 0 {
|
||||
specifiedDomain = cookieOpts.CookieDomains[0]
|
||||
if len(cookieOpts.Domains) > 0 {
|
||||
specifiedDomain = cookieOpts.Domains[0]
|
||||
}
|
||||
Expect(cookie.Domain).To(Equal(specifiedDomain))
|
||||
}
|
||||
@ -73,19 +73,19 @@ var _ = Describe("NewSessionStore", func() {
|
||||
|
||||
It("have the correct HTTPOnly set", func() {
|
||||
for _, cookie := range cookies {
|
||||
Expect(cookie.HttpOnly).To(Equal(cookieOpts.CookieHTTPOnly))
|
||||
Expect(cookie.HttpOnly).To(Equal(cookieOpts.HTTPOnly))
|
||||
}
|
||||
})
|
||||
|
||||
It("have the correct secure set", func() {
|
||||
for _, cookie := range cookies {
|
||||
Expect(cookie.Secure).To(Equal(cookieOpts.CookieSecure))
|
||||
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.CookieSameSite)))
|
||||
Expect(cookie.SameSite).To(Equal(cookiesapi.ParseSameSite(cookieOpts.SameSite)))
|
||||
}
|
||||
})
|
||||
|
||||
@ -168,8 +168,8 @@ var _ = Describe("NewSessionStore", func() {
|
||||
BeforeEach(func() {
|
||||
By("Using a valid cookie with a different providers session encoding")
|
||||
broken := "BrokenSessionFromADifferentSessionImplementation"
|
||||
value := encryption.SignedValue(cookieOpts.CookieSecret, cookieOpts.CookieName, broken, time.Now())
|
||||
cookie := cookiesapi.MakeCookieFromOptions(request, cookieOpts.CookieName, value, cookieOpts, cookieOpts.CookieExpire, time.Now())
|
||||
value := encryption.SignedValue(cookieOpts.Secret, cookieOpts.Name, broken, time.Now())
|
||||
cookie := cookiesapi.MakeCookieFromOptions(request, cookieOpts.Name, value, cookieOpts, cookieOpts.Expire, time.Now())
|
||||
request.AddCookie(cookie)
|
||||
|
||||
err := ss.Save(response, request, session)
|
||||
@ -245,7 +245,7 @@ var _ = Describe("NewSessionStore", func() {
|
||||
})
|
||||
|
||||
It("loads a session equal to the original session", func() {
|
||||
if cookieOpts.CookieSecret == "" {
|
||||
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))
|
||||
@ -290,7 +290,7 @@ var _ = Describe("NewSessionStore", func() {
|
||||
BeforeEach(func() {
|
||||
switch ss.(type) {
|
||||
case *redis.SessionStore:
|
||||
mr.FastForward(cookieOpts.CookieRefresh + time.Minute)
|
||||
mr.FastForward(cookieOpts.Refresh + time.Minute)
|
||||
}
|
||||
})
|
||||
|
||||
@ -304,7 +304,7 @@ var _ = Describe("NewSessionStore", func() {
|
||||
BeforeEach(func() {
|
||||
switch ss.(type) {
|
||||
case *redis.SessionStore:
|
||||
mr.FastForward(cookieOpts.CookieExpire + time.Minute)
|
||||
mr.FastForward(cookieOpts.Expire + time.Minute)
|
||||
}
|
||||
|
||||
loadedSession, err = ss.Load(request)
|
||||
@ -341,14 +341,14 @@ var _ = Describe("NewSessionStore", func() {
|
||||
Context("with non-default options", func() {
|
||||
BeforeEach(func() {
|
||||
cookieOpts = &options.CookieOptions{
|
||||
CookieName: "_cookie_name",
|
||||
CookiePath: "/path",
|
||||
CookieExpire: time.Duration(72) * time.Hour,
|
||||
CookieRefresh: time.Duration(2) * time.Hour,
|
||||
CookieSecure: false,
|
||||
CookieHTTPOnly: false,
|
||||
CookieDomains: []string{"example.com"},
|
||||
CookieSameSite: "strict",
|
||||
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",
|
||||
}
|
||||
|
||||
var err error
|
||||
@ -364,8 +364,8 @@ var _ = Describe("NewSessionStore", func() {
|
||||
secret := make([]byte, 32)
|
||||
_, err := rand.Read(secret)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cookieOpts.CookieSecret = base64.URLEncoding.EncodeToString(secret)
|
||||
cipher, err := encryption.NewCipher(utils.SecretBytes(cookieOpts.CookieSecret))
|
||||
cookieOpts.Secret = base64.URLEncoding.EncodeToString(secret)
|
||||
cipher, err := encryption.NewCipher(utils.SecretBytes(cookieOpts.Secret))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cipher).ToNot(BeNil())
|
||||
opts.Cipher = cipher
|
||||
@ -384,13 +384,13 @@ var _ = Describe("NewSessionStore", func() {
|
||||
|
||||
// Set default options in CookieOptions
|
||||
cookieOpts = &options.CookieOptions{
|
||||
CookieName: "_oauth2_proxy",
|
||||
CookiePath: "/",
|
||||
CookieExpire: time.Duration(168) * time.Hour,
|
||||
CookieRefresh: time.Duration(1) * time.Hour,
|
||||
CookieSecure: true,
|
||||
CookieHTTPOnly: true,
|
||||
CookieSameSite: "",
|
||||
Name: "_oauth2_proxy",
|
||||
Path: "/",
|
||||
Expire: time.Duration(168) * time.Hour,
|
||||
Refresh: time.Duration(1) * time.Hour,
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "",
|
||||
}
|
||||
|
||||
session = &sessionsapi.SessionState{
|
||||
@ -428,7 +428,7 @@ var _ = Describe("NewSessionStore", func() {
|
||||
mr, err = miniredis.Run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
opts.Type = options.RedisSessionStoreType
|
||||
opts.RedisConnectionURL = "redis://" + mr.Addr()
|
||||
opts.Redis.ConnectionURL = "redis://" + mr.Addr()
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
|
Loading…
Reference in New Issue
Block a user