@@ -5,7 +5,6 @@ import (
55 "encoding/json"
66 "errors"
77 "fmt"
8− "html/template"
98 "net"
109 "net/http"
1110 "net/url"
@@ -18,7 +17,7 @@ import (
1817 middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
1918 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
2019 sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
21− "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/app"
20+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/app/pagewriter"
2221 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic"
2322 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
2423 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
@@ -76,39 +75,33 @@ type OAuthProxy struct {
7675 AuthOnlyPath string
7776 UserInfoPath string
7877
79− allowedRoutes []allowedRoute
80− redirectURL *url.URL // the url to receive requests at
81− whitelistDomains []string
82− provider providers.Provider
83− providerNameOverride string
84− sessionStore sessionsapi.SessionStore
85− ProxyPrefix string
86− SignInMessage string
87− basicAuthValidator basic.Validator
88− displayHtpasswdForm bool
89− serveMux http.Handler
90− SetXAuthRequest bool
91− PassBasicAuth bool
92− SetBasicAuth bool
93− SkipProviderButton bool
94− PassUserHeaders bool
95− BasicAuthPassword string
96− PassAccessToken bool
97− SetAuthorization bool
98− PassAuthorization bool
99− PreferEmailToUser bool
100− skipAuthPreflight bool
101− skipJwtBearerTokens bool
102− templates *template.Template
103− realClientIPParser ipapi.RealClientIPParser
104− trustedIPs *ip.NetSet
105− Banner string
106− Footer string
78+ allowedRoutes []allowedRoute
79+ redirectURL *url.URL // the url to receive requests at
80+ whitelistDomains []string
81+ provider providers.Provider
82+ sessionStore sessionsapi.SessionStore
83+ ProxyPrefix string
84+ basicAuthValidator basic.Validator
85+ serveMux http.Handler
86+ SetXAuthRequest bool
87+ PassBasicAuth bool
88+ SetBasicAuth bool
89+ SkipProviderButton bool
90+ PassUserHeaders bool
91+ BasicAuthPassword string
92+ PassAccessToken bool
93+ SetAuthorization bool
94+ PassAuthorization bool
95+ PreferEmailToUser bool
96+ skipAuthPreflight bool
97+ skipJwtBearerTokens bool
98+ realClientIPParser ipapi.RealClientIPParser
99+ trustedIPs *ip.NetSet
107100
108101 sessionChain alice.Chain
109102 headersChain alice.Chain
110103 preAuthChain alice.Chain
111− errorPage *app.ErrorPage
104+ pageWriter pagewriter.Writer
112105}
113106
114107// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
@@ -118,20 +111,31 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
118111 return nil, fmt.Errorf("error initialising session store: %v", err)
119112 }
120113
121− templates, err := app.LoadTemplates(opts.Templates.Path)
122− if err != nil {
123− return nil, fmt.Errorf("error loading templates: %v", err)
114+ var basicAuthValidator basic.Validator
115+ if opts.HtpasswdFile != "" {
116+ logger.Printf("using htpasswd file: %s", opts.HtpasswdFile)
117+ var err error
118+ basicAuthValidator, err = basic.NewHTPasswdValidator(opts.HtpasswdFile)
119+ if err != nil {
120+ return nil, fmt.Errorf("could not load htpasswdfile: %v", err)
121+ }
124122 }
125123
126− errorPage := &app.ErrorPage{
127− Template: templates.Lookup("error.html"),
128− ProxyPrefix: opts.ProxyPrefix,
129− Footer: opts.Templates.Footer,
130− Version: VERSION,
131− Debug: opts.Templates.Debug,
124+ pageWriter, err := pagewriter.NewWriter(pagewriter.Opts{
125+ TemplatesPath: opts.Templates.Path,
126+ ProxyPrefix: opts.ProxyPrefix,
127+ Footer: opts.Templates.Footer,
128+ Version: VERSION,
129+ Debug: opts.Templates.Debug,
130+ ProviderName: buildProviderName(opts.GetProvider(), opts.ProviderName),
131+ SignInMessage: buildSignInMessage(opts),
132+ DisplayLoginForm: basicAuthValidator != nil && opts.Templates.DisplayLoginForm,
133+ })
134+ if err != nil {
135+ return nil, fmt.Errorf("error initialising page writer: %v", err)
132136 }
133137
134− upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), errorPage.ProxyErrorHandler)
138+ upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), pageWriter.ProxyErrorHandler)
135139 if err != nil {
136140 return nil, fmt.Errorf("error initialising upstream proxy: %v", err)
137141 }
@@ -164,16 +168,6 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
164168 }
165169 }
166170
167− var basicAuthValidator basic.Validator
168− if opts.HtpasswdFile != "" {
169− logger.Printf("using htpasswd file: %s", opts.HtpasswdFile)
170− var err error
171− basicAuthValidator, err = basic.NewHTPasswdValidator(opts.HtpasswdFile)
172− if err != nil {
173− return nil, fmt.Errorf("could not load htpasswdfile: %v", err)
174− }
175− }
176−
177171 allowedRoutes, err := buildRoutesAllowlist(opts)
178172 if err != nil {
179173 return nil, err
@@ -210,30 +204,24 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
210204 AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
211205 UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix),
212206
213− ProxyPrefix: opts.ProxyPrefix,
214− provider: opts.GetProvider(),
215− providerNameOverride: opts.ProviderName,
216− sessionStore: sessionStore,
217− serveMux: upstreamProxy,
218− redirectURL: redirectURL,
219− allowedRoutes: allowedRoutes,
220− whitelistDomains: opts.WhitelistDomains,
221− skipAuthPreflight: opts.SkipAuthPreflight,
222− skipJwtBearerTokens: opts.SkipJwtBearerTokens,
223− realClientIPParser: opts.GetRealClientIPParser(),
224− SkipProviderButton: opts.SkipProviderButton,
225− templates: templates,
226− trustedIPs: trustedIPs,
227− Banner: opts.Templates.Banner,
228− Footer: opts.Templates.Footer,
229− SignInMessage: buildSignInMessage(opts),
230−
231− basicAuthValidator: basicAuthValidator,
232− displayHtpasswdForm: basicAuthValidator != nil && opts.Templates.DisplayLoginForm,
233− sessionChain: sessionChain,
234− headersChain: headersChain,
235− preAuthChain: preAuthChain,
236− errorPage: errorPage,
207+ ProxyPrefix: opts.ProxyPrefix,
208+ provider: opts.GetProvider(),
209+ sessionStore: sessionStore,
210+ serveMux: upstreamProxy,
211+ redirectURL: redirectURL,
212+ allowedRoutes: allowedRoutes,
213+ whitelistDomains: opts.WhitelistDomains,
214+ skipAuthPreflight: opts.SkipAuthPreflight,
215+ skipJwtBearerTokens: opts.SkipJwtBearerTokens,
216+ realClientIPParser: opts.GetRealClientIPParser(),
217+ SkipProviderButton: opts.SkipProviderButton,
218+ trustedIPs: trustedIPs,
219+
220+ basicAuthValidator: basicAuthValidator,
221+ sessionChain: sessionChain,
222+ headersChain: headersChain,
223+ preAuthChain: preAuthChain,
224+ pageWriter: pageWriter,
237225 }, nil
238226}
239227
@@ -331,6 +319,13 @@ func buildSignInMessage(opts *options.Options) string {
331319 return msg
332320}
333321
322+func buildProviderName(p providers.Provider, override string) string {
323+ if override != "" {
324+ return override
325+ }
326+ return p.Data().ProviderName
327+}
328+
334329// buildRoutesAllowlist builds an []allowedRoute list from either the legacy
335330// SkipAuthRegex option (paths only support) or newer SkipAuthRoutes option
336331// (method=path support)
@@ -533,7 +528,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code i
533528 redirectURL = "/"
534529 }
535530
536− p.errorPage.Render(rw, code, redirectURL, appError, messages...)
531+ p.pageWriter.WriteErrorPage(rw, code, redirectURL, appError, messages...)
537532}
538533
539534// IsAllowedRequest is used to check if auth should be skipped for this request
@@ -594,33 +589,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
594589 redirectURL = "/"
595590 }
596591
597− // We allow unescaped template.HTML since it is user configured options
598− /* #nosec G203 */
599− t := struct {
600− ProviderName string
601− SignInMessage template.HTML
602− CustomLogin bool
603− Redirect string
604− Version string
605− ProxyPrefix string
606− Footer template.HTML
607− }{
608− ProviderName: p.provider.Data().ProviderName,
609− SignInMessage: template.HTML(p.SignInMessage),
610− CustomLogin: p.displayHtpasswdForm,
611− Redirect: redirectURL,
612− Version: VERSION,
613− ProxyPrefix: p.ProxyPrefix,
614− Footer: template.HTML(p.Footer),
615− }
616− if p.providerNameOverride != "" {
617− t.ProviderName = p.providerNameOverride
618− }
619− err = p.templates.ExecuteTemplate(rw, "sign_in.html", t)
620− if err != nil {
621− logger.Printf("Error rendering sign_in.html template: %v", err)
622− p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
623− }
592+ p.pageWriter.WriteSignInPage(rw, redirectURL)
624593}
625594
626595// ManualSignIn handles basic auth logins to the proxy