git.iko.soy/typeable/oauth2-proxy
on master help sign in
feat: add ensure defaults to all migrated structs

Signed-off-by: Jan Larwig <jan@larwig.com>

commit527c72f23f5aeceeb9cc4b3e7dd10fdaab8d67c2 authorJan Larwig <jan@larwig.com> dateThu 30 Oct 2025, 09:26 (1 year ago) parent51b1fd0
14 files changed+189−33
main.go+4−2 main_test.go+2−7 pkg/apis/middleware/session.go+2−1 pkg/apis/options/alpha_options.go+3−3 pkg/apis/options/header.go+27 pkg/apis/options/legacy_options.go−4 pkg/apis/options/legacy_options_test.go+2−1 pkg/apis/options/options.go+20 pkg/apis/options/providers.go+70 pkg/apis/options/secret_source.go+5 pkg/apis/options/upstreams.go+40−1 pkg/upstream/http.go+6−5 pkg/validation/providers.go+3−4 pkg/validation/upstreams.go+5−5
main.go+4−2
@@ -20,7 +20,7 @@ func main() {
2020
2121 // Because we parse early to determine alpha vs legacy config, we have to
2222 // ignore any unknown flags for now
23− configFlagSet.ParseErrorsWhitelist.UnknownFlags = true
23+ configFlagSet.ParseErrorsAllowlist.UnknownFlags = true
2424
2525 config := configFlagSet.String("config", "", "path to config file")
2626 alphaConfig := configFlagSet.String("alpha-config", "", "path to alpha config file (use at your own risk - the structure in this config file may change between minor releases)")
@@ -78,6 +78,8 @@ func loadConfiguration(config, yamlConfig string, extraFlags *pflag.FlagSet, arg
7878 return loadYamlOptions(yamlConfig, config, extraFlags, args)
7979 }
8080
81+ opts.EnsureDefaults()
82+
8183 return opts, nil
8284}
8385
@@ -117,7 +119,7 @@ func loadYamlOptions(yamlConfig, config string, extraFlags *pflag.FlagSet, args
117119 return nil, fmt.Errorf("failed to load alpha options: %v", err)
118120 }
119121
120− alphaOpts.MergeInto(opts)
122+ alphaOpts.MergeOptionsWithDefaults(opts)
121123 return opts, nil
122124}
123125
main_test.go+2−7
@@ -3,7 +3,6 @@ package main
33import (
44 "errors"
55 "os"
6− "time"
76
87 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
98 . "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options/testutil"
@@ -114,10 +113,6 @@ cookie_secure="false"
114113redirect_url="http://localhost:4180/oauth2/callback"
115114`
116115
117− durationPtr := func(d time.Duration) *time.Duration {
118− return &d
119− }
120−
121116 testExpectedOptions := func() *options.Options {
122117 opts, err := options.NewLegacyOptions().ToOptions()
123118 Expect(err).ToNot(HaveOccurred())
@@ -133,10 +128,10 @@ redirect_url="http://localhost:4180/oauth2/callback"
133128 ID: "/",
134129 Path: "/",
135130 URI: "http://httpbin",
136− FlushInterval: durationPtr(options.DefaultUpstreamFlushInterval),
131+ FlushInterval: ptr.Ptr(options.DefaultUpstreamFlushInterval),
137132 PassHostHeader: ptr.Ptr(true),
138133 ProxyWebSockets: ptr.Ptr(true),
139− Timeout: durationPtr(options.DefaultUpstreamTimeout),
134+ Timeout: ptr.Ptr(options.DefaultUpstreamTimeout),
140135 InsecureSkipTLSVerify: ptr.Ptr(false),
141136 DisableKeepAlives: ptr.Ptr(false),
142137 },
pkg/apis/middleware/session.go+2−1
@@ -6,6 +6,7 @@ import (
66
77 "github.com/coreos/go-oidc/v3/oidc"
88 sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
9+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
910)
1011
1112// TokenToSessionFunc takes a raw ID Token and converts it into a SessionState.
@@ -40,7 +41,7 @@ func CreateTokenToSessionFunc(verify VerifyFunc) TokenToSessionFunc {
4041 claims.Email = claims.Subject
4142 }
4243
43− if claims.Verified != nil && !*claims.Verified {
44+ if !ptr.Deref(claims.Verified, false) {
4445 return nil, fmt.Errorf("email in id_token (%s) isn't verified", claims.Email)
4546 }
4647
pkg/apis/options/alpha_options.go+3−3
@@ -65,9 +65,9 @@ func (a *AlphaOptions) ExtractFrom(opts *Options) {
6565 a.Providers = opts.Providers
6666}
6767
68−// MergeInto replaces alpha options in the Options struct with the values
69−// from the AlphaOptions
70−func (a *AlphaOptions) MergeInto(opts *Options) {
68+// MergeOptionsWithDefaults replaces alpha options in the Options struct
69+// with the values from the AlphaOptions and ensures the defaults
70+func (a *AlphaOptions) MergeOptionsWithDefaults(opts *Options) {
7171 opts.UpstreamServers = a.UpstreamConfig
7272 opts.InjectRequestHeaders = a.InjectRequestHeaders
7373 opts.InjectResponseHeaders = a.InjectResponseHeaders
pkg/apis/options/header.go+27
@@ -1,5 +1,7 @@
11package options
22
3+import "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
4+
35// Header represents an individual header that will be added to a request or
46// response header.
57type Header struct {
@@ -53,3 +55,28 @@ type ClaimSource struct {
5355 // basicAuthPassword will be used as the password value.
5456 BasicAuthPassword *SecretSource `yaml:"basicAuthPassword,omitempty"`
5557}
58+
59+// EnsureDefaults sets any default values for Header fields.
60+func (h *Header) EnsureDefaults() {
61+ if h.PreserveRequestValue == nil {
62+ h.PreserveRequestValue = ptr.Ptr(false)
63+ }
64+ for i := range h.Values {
65+ h.Values[i].EnsureDefaults()
66+ }
67+}
68+
69+// EnsureDefaults sets any default values for HeaderValue fields.
70+func (hv *HeaderValue) EnsureDefaults() {
71+ if hv.ClaimSource != nil {
72+ hv.ClaimSource.EnsureDefaults()
73+ }
74+ if hv.SecretSource != nil {
75+ hv.SecretSource.EnsureDefaults()
76+ }
77+}
78+
79+// EnsureDefaults sets any default values for ClaimSource fields.
80+func (hc *ClaimSource) EnsureDefaults() {
81+ // No defaults to set currently
82+}
pkg/apis/options/legacy_options.go−4
@@ -177,10 +177,6 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
177177 // Force defaults compatible with static responses
178178 upstream.URI = ""
179179 upstream.InsecureSkipTLSVerify = ptr.Ptr(false)
180− upstream.PassHostHeader = nil
181− upstream.ProxyWebSockets = nil
182− upstream.FlushInterval = nil
183− upstream.Timeout = nil
184180 upstream.DisableKeepAlives = ptr.Ptr(false)
185181 case "unix":
186182 upstream.Path = "/"
pkg/apis/options/legacy_options_test.go+2−1
@@ -3,6 +3,7 @@ package options
33import (
44 "time"
55
6+ . "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options/testutil"
67 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
78 . "github.com/onsi/ginkgo/v2"
89 . "github.com/onsi/gomega"
@@ -132,7 +133,7 @@ var _ = Describe("Legacy Options", func() {
132133
133134 converted, err := legacyOpts.ToOptions()
134135 Expect(err).ToNot(HaveOccurred())
135− Expect(converted).To(Equal(opts))
136+ Expect(converted).To(EqualOpts(opts))
136137 })
137138 })
138139
pkg/apis/options/options.go+20
@@ -168,3 +168,23 @@ func NewFlagSet() *pflag.FlagSet {
168168
169169 return flagSet
170170}
171+
172+// EnsureDefaults configures the defaults for all options
173+// to ensure no unexpected empty strings for enum types or nils for booleans
174+func (o *Options) EnsureDefaults() {
175+ o.Providers.EnsureDefaults()
176+ o.UpstreamServers.EnsureDefaults()
177+
178+ for i := range o.InjectRequestHeaders {
179+ o.InjectRequestHeaders[i].EnsureDefaults()
180+ }
181+ for i := range o.InjectResponseHeaders {
182+ o.InjectResponseHeaders[i].EnsureDefaults()
183+ }
184+
185+ // TBD: Uncomment as we add EnsureDefaults methods
186+ // o.Cookie.EnsureDefaults()
187+ // o.Session.EnsureDefaults()
188+ // o.Templates.EnsureDefaults()
189+ // o.Logging.EnsureDefaults()
190+}
pkg/apis/options/providers.go+70
@@ -289,6 +289,7 @@ type LoginGovOptions struct {
289289 PubJWKURL string `yaml:"pubjwkURL,omitempty"`
290290}
291291
292+// Legacy default providers configuration
292293func providerDefaults() Providers {
293294 providers := Providers{
294295 {
@@ -310,3 +311,72 @@ func providerDefaults() Providers {
310311 }
311312 return providers
312313}
314+
315+// EnsureDefaults sets any default values for Providers fields.
316+func (p Providers) EnsureDefaults() {
317+ for i := range p {
318+ p[i].EnsureDefaults()
319+ }
320+}
321+
322+// EnsureDefaults sets any default values for Provider fields.
323+func (p *Provider) EnsureDefaults() {
324+ if p.SkipClaimsFromProfileURL == nil {
325+ p.SkipClaimsFromProfileURL = ptr.Ptr(false)
326+ }
327+ if p.UseSystemTrustStore == nil {
328+ p.UseSystemTrustStore = ptr.Ptr(true)
329+ }
330+
331+ p.OIDCConfig.EnsureDefaults()
332+ p.MicrosoftEntraIDConfig.EnsureDefaults()
333+ p.ADFSConfig.EnsureDefaults()
334+ p.GoogleConfig.EnsureDefaults()
335+}
336+
337+// EnsureDefaults sets any default values for OIDCOptions fields.
338+func (o *OIDCOptions) EnsureDefaults() {
339+ // Ensure OIDC defaults
340+ if o.InsecureAllowUnverifiedEmail == nil {
341+ o.InsecureAllowUnverifiedEmail = ptr.Ptr(false)
342+ }
343+ if o.InsecureSkipNonce == nil {
344+ o.InsecureSkipNonce = ptr.Ptr(true)
345+ }
346+ if o.SkipDiscovery == nil {
347+ o.SkipDiscovery = ptr.Ptr(false)
348+ }
349+ if o.UserIDClaim == "" {
350+ o.UserIDClaim = OIDCEmailClaim
351+ }
352+ if o.EmailClaim == "" {
353+ o.EmailClaim = OIDCEmailClaim
354+ }
355+ if o.GroupsClaim == "" {
356+ o.GroupsClaim = OIDCGroupsClaim
357+ }
358+ if len(o.AudienceClaims) == 0 {
359+ o.AudienceClaims = OIDCAudienceClaims
360+ }
361+}
362+
363+// EnsureDefaults sets any default values for MicrosoftEntraIDOptions fields.
364+func (me *MicrosoftEntraIDOptions) EnsureDefaults() {
365+ if me.FederatedTokenAuth == nil {
366+ me.FederatedTokenAuth = ptr.Ptr(false)
367+ }
368+}
369+
370+// EnsureDefaults sets any default values for ADFSOptions fields.
371+func (a *ADFSOptions) EnsureDefaults() {
372+ if a.SkipScope == nil {
373+ a.SkipScope = ptr.Ptr(false)
374+ }
375+}
376+
377+// EnsureDefaults sets any default values for GoogleOptions fields.
378+func (g *GoogleOptions) EnsureDefaults() {
379+ if g.UseApplicationDefaultCredentials == nil {
380+ g.UseApplicationDefaultCredentials = ptr.Ptr(false)
381+ }
382+}
pkg/apis/options/secret_source.go+5
@@ -12,3 +12,8 @@ type SecretSource struct {
1212 // FromFile expects a path to a file containing the secret value.
1313 FromFile string `yaml:"fromFile,omitempty"`
1414}
15+
16+// EnsureDefaults sets any default values for SecretSource fields.
17+func (ss *SecretSource) EnsureDefaults() {
18+ // No defaults to set currently
19+}
pkg/apis/options/upstreams.go+40−1
@@ -1,6 +1,10 @@
11package options
22
3−import "time"
3+import (
4+ "time"
5+
6+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
7+)
48
59const (
610 // DefaultUpstreamFlushInterval is the default value for the Upstream FlushInterval.
@@ -98,3 +102,38 @@ type Upstream struct {
98102 // Defaults to false.
99103 DisableKeepAlives *bool `yaml:"disableKeepAlives,omitempty"`
100104}
105+
106+// EnsureDefaults sets any default values for UpstreamConfig fields.
107+func (uc *UpstreamConfig) EnsureDefaults() {
108+ if uc.ProxyRawPath == nil {
109+ uc.ProxyRawPath = ptr.Ptr(false)
110+ }
111+ for i := range uc.Upstreams {
112+ uc.Upstreams[i].EnsureDefaults()
113+ }
114+}
115+
116+// EnsureDefaults sets any default values for Upstream fields.
117+func (u *Upstream) EnsureDefaults() {
118+ if u.InsecureSkipTLSVerify == nil {
119+ u.InsecureSkipTLSVerify = ptr.Ptr(false)
120+ }
121+ if u.Static == nil {
122+ u.Static = ptr.Ptr(false)
123+ }
124+ if u.FlushInterval == nil {
125+ u.FlushInterval = ptr.Ptr(DefaultUpstreamFlushInterval)
126+ }
127+ if u.PassHostHeader == nil {
128+ u.PassHostHeader = ptr.Ptr(true)
129+ }
130+ if u.ProxyWebSockets == nil {
131+ u.ProxyWebSockets = ptr.Ptr(true)
132+ }
133+ if u.Timeout == nil {
134+ u.Timeout = ptr.Ptr(DefaultUpstreamTimeout)
135+ }
136+ if u.DisableKeepAlives == nil {
137+ u.DisableKeepAlives = ptr.Ptr(false)
138+ }
139+}
pkg/upstream/http.go+6−5
@@ -11,6 +11,7 @@ import (
1111 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
1212 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1313 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/hmacauth"
14+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
1415)
1516
1617const (
@@ -53,8 +54,8 @@ func newHTTPUpstreamProxy(upstream options.Upstream, u *url.URL, sigData *option
5354
5455 // Set up a WebSocket proxy if required
5556 var wsProxy http.Handler
56− if upstream.ProxyWebSockets == nil || *upstream.ProxyWebSockets {
57− wsProxy = newWebSocketReverseProxy(u, *upstream.InsecureSkipTLSVerify)
57+ if *upstream.ProxyWebSockets {
58+ wsProxy = newWebSocketReverseProxy(u, upstream.InsecureSkipTLSVerify)
5859 }
5960
6061 var auth hmacauth.HmacAuth
@@ -156,7 +157,7 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
156157 // Ensure we always pass the original request path
157158 setProxyDirector(proxy)
158159
159− if upstream.PassHostHeader != nil && !*upstream.PassHostHeader {
160+ if upstream.PassHostHeader != nil && !(*upstream.PassHostHeader) {
160161 setProxyUpstreamHostHeader(proxy, target)
161162 }
162163
@@ -200,14 +201,14 @@ func setProxyDirector(proxy *httputil.ReverseProxy) {
200201}
201202
202203// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
203−func newWebSocketReverseProxy(u *url.URL, skipTLSVerify bool) http.Handler {
204+func newWebSocketReverseProxy(u *url.URL, skipTLSVerify *bool) http.Handler {
204205 wsProxy := httputil.NewSingleHostReverseProxy(u)
205206
206207 // Inherit default transport options from Go's stdlib
207208 transport := http.DefaultTransport.(*http.Transport).Clone()
208209
209210 /* #nosec G402 */
210− if skipTLSVerify {
211+ if ptr.Deref(skipTLSVerify, false) {
211212 transport.TLSClientConfig.InsecureSkipVerify = true
212213 }
213214
pkg/validation/providers.go+3−4
@@ -5,7 +5,6 @@ import (
55 "os"
66
77 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
8− "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
98)
109
1110// validateProviders is the initial validation migration for multiple providrers
@@ -97,9 +96,9 @@ func validateGoogleConfig(provider options.Provider) []string {
9796
9897 hasAdminEmail := provider.GoogleConfig.AdminEmail != ""
9998 hasSAJSON := provider.GoogleConfig.ServiceAccountJSON != ""
100− useADC := ptr.Deref(provider.GoogleConfig.UseApplicationDefaultCredentials, false)
99+ useADC := provider.GoogleConfig.UseApplicationDefaultCredentials
101100
102− if !hasAdminEmail && !hasSAJSON && !useADC {
101+ if !hasAdminEmail && !hasSAJSON && !(*useADC) {
103102 return msgs
104103 }
105104
@@ -108,7 +107,7 @@ func validateGoogleConfig(provider options.Provider) []string {
108107 }
109108
110109 _, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON)
111− if !useADC {
110+ if !(*useADC) {
112111 if !hasSAJSON {
113112 msgs = append(msgs, "missing setting: google-service-account-json or google-use-application-default-credentials")
114113 } else if err != nil {
pkg/validation/upstreams.go+5−5
@@ -54,12 +54,12 @@ func validateUpstream(upstream options.Upstream, ids, paths map[string]struct{})
5454func validateStaticUpstream(upstream options.Upstream) []string {
5555 msgs := []string{}
5656
57− if !*upstream.Static && upstream.StaticCode != nil {
57+ if !(*upstream.Static) && upstream.StaticCode != nil {
5858 msgs = append(msgs, fmt.Sprintf("upstream %q has staticCode (%d), but is not a static upstream, set 'static' for a static response", upstream.ID, *upstream.StaticCode))
5959 }
6060
6161 // Checks after this only make sense when the upstream is static
62− if !*upstream.Static {
62+ if !(*upstream.Static) {
6363 return msgs
6464 }
6565
@@ -72,10 +72,10 @@ func validateStaticUpstream(upstream options.Upstream) []string {
7272 if upstream.FlushInterval != nil && *upstream.FlushInterval != options.DefaultUpstreamFlushInterval {
7373 msgs = append(msgs, fmt.Sprintf("upstream %q has flushInterval, but is a static upstream, this will have no effect.", upstream.ID))
7474 }
75− if upstream.PassHostHeader != nil {
75+ if *upstream.PassHostHeader {
7676 msgs = append(msgs, fmt.Sprintf("upstream %q has passHostHeader, but is a static upstream, this will have no effect.", upstream.ID))
7777 }
78− if upstream.ProxyWebSockets != nil {
78+ if *upstream.ProxyWebSockets {
7979 msgs = append(msgs, fmt.Sprintf("upstream %q has proxyWebSockets, but is a static upstream, this will have no effect.", upstream.ID))
8080 }
8181
@@ -85,7 +85,7 @@ func validateStaticUpstream(upstream options.Upstream) []string {
8585func validateUpstreamURI(upstream options.Upstream) []string {
8686 msgs := []string{}
8787
88− if !*upstream.Static && upstream.URI == "" {
88+ if !(*upstream.Static) && upstream.URI == "" {
8989 msgs = append(msgs, fmt.Sprintf("upstream %q has empty uri: uris are required for all non-static upstreams", upstream.ID))
9090 return msgs
9191 }