git.iko.soy/typeable/oauth2-proxy
on master help sign in
Wrap templates and page rendering in PageWriter interface
e8e2af7Joel Speed
oauthproxy.go+25−37
@@ -101,8 +101,7 @@ type OAuthProxy struct {
101 sessionChain alice.Chain
101 sessionChain alice.Chain
102 headersChain alice.Chain
102 headersChain alice.Chain
103 preAuthChain alice.Chain
103 preAuthChain alice.Chain
104− errorPage *app.ErrorPage
104+ pageWriter app.PageWriter
105− signInPage *app.SignInPage
106}
105}
107
106
108// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
107// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
@@ -112,20 +111,31 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
112 return nil, fmt.Errorf("error initialising session store: %v", err)
111 return nil, fmt.Errorf("error initialising session store: %v", err)
113 }
112 }
114
113
115− templates, err := app.LoadTemplates(opts.Templates.Path)
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)
116− if err != nil {
119+ if err != nil {
117− return nil, fmt.Errorf("error loading templates: %v", err)
120+ return nil, fmt.Errorf("could not load htpasswdfile: %v", err)
121+ }
118 }
122 }
119
123
120− errorPage := &app.ErrorPage{
124+ pageWriter, err := app.NewPageWriter(app.PageWriterOpts{
121− Template: templates.Lookup("error.html"),
125+ TemplatesPath: opts.Templates.Path,
122− ProxyPrefix: opts.ProxyPrefix,
126+ ProxyPrefix: opts.ProxyPrefix,
123− Footer: opts.Templates.Footer,
127+ Footer: opts.Templates.Footer,
124− Version: VERSION,
128+ Version: VERSION,
125− Debug: opts.Templates.Debug,
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)
126 }
136 }
127
137
128− upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), errorPage.ProxyErrorHandler)
138+ upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), pageWriter.ProxyErrorHandler)
129 if err != nil {
139 if err != nil {
130 return nil, fmt.Errorf("error initialising upstream proxy: %v", err)
140 return nil, fmt.Errorf("error initialising upstream proxy: %v", err)
131 }
141 }
@@ -158,27 +168,6 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
158 }
168 }
159 }
169 }
160
170
161− var basicAuthValidator basic.Validator
162− if opts.HtpasswdFile != "" {
163− logger.Printf("using htpasswd file: %s", opts.HtpasswdFile)
164− var err error
165− basicAuthValidator, err = basic.NewHTPasswdValidator(opts.HtpasswdFile)
166− if err != nil {
167− return nil, fmt.Errorf("could not load htpasswdfile: %v", err)
168− }
169− }
170−
171− signInPage := &app.SignInPage{
172− Template: templates.Lookup("sign_in.html"),
173− ErrorPage: errorPage,
174− ProxyPrefix: opts.ProxyPrefix,
175− ProviderName: buildProviderName(opts.GetProvider(), opts.ProviderName),
176− SignInMessage: buildSignInMessage(opts),
177− Footer: opts.Templates.Footer,
178− Version: VERSION,
179− DisplayLoginForm: basicAuthValidator != nil && opts.Templates.DisplayLoginForm,
180− }
181−
182 allowedRoutes, err := buildRoutesAllowlist(opts)
171 allowedRoutes, err := buildRoutesAllowlist(opts)
183 if err != nil {
172 if err != nil {
184 return nil, err
173 return nil, err
@@ -232,8 +221,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
232 sessionChain: sessionChain,
221 sessionChain: sessionChain,
233 headersChain: headersChain,
222 headersChain: headersChain,
234 preAuthChain: preAuthChain,
223 preAuthChain: preAuthChain,
235− errorPage: errorPage,
224+ pageWriter: pageWriter,
236− signInPage: signInPage,
237 }, nil
225 }, nil
238}
226}
239
227
@@ -540,7 +528,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code i
540 redirectURL = "/"
528 redirectURL = "/"
541 }
529 }
542
530
543− p.errorPage.Render(rw, code, redirectURL, appError, messages...)
531+ p.pageWriter.WriteErrorPage(rw, code, redirectURL, appError, messages...)
544}
532}
545
533
546// IsAllowedRequest is used to check if auth should be skipped for this request
534// IsAllowedRequest is used to check if auth should be skipped for this request
@@ -601,7 +589,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
601 redirectURL = "/"
589 redirectURL = "/"
602 }
590 }
603
591
604− p.signInPage.Render(rw, redirectURL)
592+ p.pageWriter.WriteSignInPage(rw, redirectURL)
605}
593}
606
594
607// ManualSignIn handles basic auth logins to the proxy
595// ManualSignIn handles basic auth logins to the proxy
pkg/app/error_page.go+23−23
@@ -17,30 +17,30 @@ var errorMessages = map[int]string{
17 http.StatusUnauthorized: "You need to be logged in to access this resource.",
17 http.StatusUnauthorized: "You need to be logged in to access this resource.",
18}
18}
19
19
20−// ErrorPage is used to render error pages.
20+// errorPageWriter is used to render error pages.
21−type ErrorPage struct {
21+type errorPageWriter struct {
22− // Template is the error page HTML template.
22+ // template is the error page HTML template.
23− Template *template.Template
23+ template *template.Template
24
24
25− // ProxyPrefix is the prefix under which OAuth2 Proxy pages are served.
25+ // proxyPrefix is the prefix under which OAuth2 Proxy pages are served.
26− ProxyPrefix string
26+ proxyPrefix string
27
27
28− // Footer is the footer to be displayed at the bottom of the page.
28+ // footer is the footer to be displayed at the bottom of the page.
29 // If not set, a default footer will be used.
29 // If not set, a default footer will be used.
30− Footer string
30+ footer string
31
31
32− // Version is the OAuth2 Proxy version to be used in the default footer.
32+ // version is the OAuth2 Proxy version to be used in the default footer.
33− Version string
33+ version string
34
34
35− // Debug determines whether errors pages should be rendered with detailed
35+ // debug determines whether errors pages should be rendered with detailed
36 // errors.
36 // errors.
37− Debug bool
37+ debug bool
38}
38}
39
39
40−// Render writes an error page to the given response writer.
40+// WriteErrorPage writes an error page to the given response writer.
41// It uses the passed redirectURL to give users the option to go back to where
41// It uses the passed redirectURL to give users the option to go back to where
42// they originally came from or try signing in again.
42// they originally came from or try signing in again.
43−func (e *ErrorPage) Render(rw http.ResponseWriter, status int, redirectURL string, appError string, messages ...interface{}) {
43+func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, status int, redirectURL string, appError string, messages ...interface{}) {
44 rw.WriteHeader(status)
44 rw.WriteHeader(status)
45
45
46 // We allow unescaped template.HTML since it is user configured options
46 // We allow unescaped template.HTML since it is user configured options
@@ -56,14 +56,14 @@ func (e *ErrorPage) Render(rw http.ResponseWriter, status int, redirectURL strin
56 }{
56 }{
57 Title: http.StatusText(status),
57 Title: http.StatusText(status),
58 Message: e.getMessage(status, appError, messages...),
58 Message: e.getMessage(status, appError, messages...),
59− ProxyPrefix: e.ProxyPrefix,
59+ ProxyPrefix: e.proxyPrefix,
60 StatusCode: status,
60 StatusCode: status,
61 Redirect: redirectURL,
61 Redirect: redirectURL,
62− Footer: template.HTML(e.Footer),
62+ Footer: template.HTML(e.footer),
63− Version: e.Version,
63+ Version: e.version,
64 }
64 }
65
65
66− if err := e.Template.Execute(rw, data); err != nil {
66+ if err := e.template.Execute(rw, data); err != nil {
67 logger.Printf("Error rendering error template: %v", err)
67 logger.Printf("Error rendering error template: %v", err)
68 http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
68 http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
69 }
69 }
@@ -72,18 +72,18 @@ func (e *ErrorPage) Render(rw http.ResponseWriter, status int, redirectURL strin
72// ProxyErrorHandler is used by the upstream ReverseProxy to render error pages
72// ProxyErrorHandler is used by the upstream ReverseProxy to render error pages
73// when there are issues with upstream servers.
73// when there are issues with upstream servers.
74// It is expected to always render a bad gateway error.
74// It is expected to always render a bad gateway error.
75−func (e *ErrorPage) ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error) {
75+func (e *errorPageWriter) ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error) {
76 logger.Errorf("Error proxying to upstream server: %v", proxyErr)
76 logger.Errorf("Error proxying to upstream server: %v", proxyErr)
77− e.Render(rw, http.StatusBadGateway, "", proxyErr.Error(), "There was a problem connecting to the upstream server.")
77+ e.WriteErrorPage(rw, http.StatusBadGateway, "", proxyErr.Error(), "There was a problem connecting to the upstream server.")
78}
78}
79
79
80// getMessage creates the message for the template parameters.
80// getMessage creates the message for the template parameters.
81−// If the ErrorPage.Debug is enabled, the application error takes precedence.
81+// If the errorPagewriter.Debug is enabled, the application error takes precedence.
82// Otherwise, any messages will be used.
82// Otherwise, any messages will be used.
83// The first message is expected to be a format string.
83// The first message is expected to be a format string.
84// If no messages are supplied, a default error message will be used.
84// If no messages are supplied, a default error message will be used.
85−func (e *ErrorPage) getMessage(status int, appError string, messages ...interface{}) string {
85+func (e *errorPageWriter) getMessage(status int, appError string, messages ...interface{}) string {
86− if e.Debug {
86+ if e.debug {
87 return appError
87 return appError
88 }
88 }
89 if len(messages) > 0 {
89 if len(messages) > 0 {
pkg/app/error_page_test.go+15−15
@@ -10,25 +10,25 @@ import (
10 . "github.com/onsi/gomega"
10 . "github.com/onsi/gomega"
11)
11)
12
12
13−var _ = Describe("Error Page", func() {
13+var _ = Describe("Error Page Writer", func() {
14− var errorPage *ErrorPage
14+ var errorPage *errorPageWriter
15
15
16 BeforeEach(func() {
16 BeforeEach(func() {
17 tmpl, err := template.New("").Parse("{{.Title}} {{.Message}} {{.ProxyPrefix}} {{.StatusCode}} {{.Redirect}} {{.Footer}} {{.Version}}")
17 tmpl, err := template.New("").Parse("{{.Title}} {{.Message}} {{.ProxyPrefix}} {{.StatusCode}} {{.Redirect}} {{.Footer}} {{.Version}}")
18 Expect(err).ToNot(HaveOccurred())
18 Expect(err).ToNot(HaveOccurred())
19
19
20− errorPage = &ErrorPage{
20+ errorPage = &errorPageWriter{
21− Template: tmpl,
21+ template: tmpl,
22− ProxyPrefix: "/prefix/",
22+ proxyPrefix: "/prefix/",
23− Footer: "Custom Footer Text",
23+ footer: "Custom Footer Text",
24− Version: "v0.0.0-test",
24+ version: "v0.0.0-test",
25 }
25 }
26 })
26 })
27
27
28− Context("Render", func() {
28+ Context("WriteErrorPage", func() {
29 It("Writes the template to the response writer", func() {
29 It("Writes the template to the response writer", func() {
30 recorder := httptest.NewRecorder()
30 recorder := httptest.NewRecorder()
31− errorPage.Render(recorder, 403, "/redirect", "Access Denied")
31+ errorPage.WriteErrorPage(recorder, 403, "/redirect", "Access Denied")
32
32
33 body, err := ioutil.ReadAll(recorder.Result().Body)
33 body, err := ioutil.ReadAll(recorder.Result().Body)
34 Expect(err).ToNot(HaveOccurred())
34 Expect(err).ToNot(HaveOccurred())
@@ -37,7 +37,7 @@ var _ = Describe("Error Page", func() {
37
37
38 It("With a different code, uses the stock message for the correct code", func() {
38 It("With a different code, uses the stock message for the correct code", func() {
39 recorder := httptest.NewRecorder()
39 recorder := httptest.NewRecorder()
40− errorPage.Render(recorder, 500, "/redirect", "Access Denied")
40+ errorPage.WriteErrorPage(recorder, 500, "/redirect", "Access Denied")
41
41
42 body, err := ioutil.ReadAll(recorder.Result().Body)
42 body, err := ioutil.ReadAll(recorder.Result().Body)
43 Expect(err).ToNot(HaveOccurred())
43 Expect(err).ToNot(HaveOccurred())
@@ -46,7 +46,7 @@ var _ = Describe("Error Page", func() {
46
46
47 It("With a message override, uses the message", func() {
47 It("With a message override, uses the message", func() {
48 recorder := httptest.NewRecorder()
48 recorder := httptest.NewRecorder()
49− errorPage.Render(recorder, 403, "/redirect", "Access Denied", "An extra message: %s", "with more context.")
49+ errorPage.WriteErrorPage(recorder, 403, "/redirect", "Access Denied", "An extra message: %s", "with more context.")
50
50
51 body, err := ioutil.ReadAll(recorder.Result().Body)
51 body, err := ioutil.ReadAll(recorder.Result().Body)
52 Expect(err).ToNot(HaveOccurred())
52 Expect(err).ToNot(HaveOccurred())
@@ -71,14 +71,14 @@ var _ = Describe("Error Page", func() {
71 tmpl, err := template.New("").Parse("{{.Message}}")
71 tmpl, err := template.New("").Parse("{{.Message}}")
72 Expect(err).ToNot(HaveOccurred())
72 Expect(err).ToNot(HaveOccurred())
73
73
74− errorPage.Template = tmpl
74+ errorPage.template = tmpl
75− errorPage.Debug = true
75+ errorPage.debug = true
76 })
76 })
77
77
78− Context("Render", func() {
78+ Context("WriteErrorPage", func() {
79 It("Writes the detailed error in place of the message", func() {
79 It("Writes the detailed error in place of the message", func() {
80 recorder := httptest.NewRecorder()
80 recorder := httptest.NewRecorder()
81− errorPage.Render(recorder, 403, "/redirect", "Debug error")
81+ errorPage.WriteErrorPage(recorder, 403, "/redirect", "Debug error")
82
82
83 body, err := ioutil.ReadAll(recorder.Result().Body)
83 body, err := ioutil.ReadAll(recorder.Result().Body)
84 Expect(err).ToNot(HaveOccurred())
84 Expect(err).ToNot(HaveOccurred())
pkg/app/pagewriter.gonew file+85
@@ -0,0 +1,85 @@
1+package app
2+
3+import (
4+ "fmt"
5+ "net/http"
6+)
7+
8+// PageWriter is an interface for rendering html templates for both sign-in and
9+// error pages.
10+// It can also be used to write errors for the http.ReverseProxy used in the
11+// upstream package.
12+type PageWriter interface {
13+ WriteSignInPage(rw http.ResponseWriter, redirectURL string)
14+ WriteErrorPage(rw http.ResponseWriter, status int, redirectURL string, appError string, messages ...interface{})
15+ ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error)
16+}
17+
18+// pageWriter implements the PageWriter interface
19+type pageWriter struct {
20+ *errorPageWriter
21+ *signInPageWriter
22+}
23+
24+// PageWriterOpts contains all options required to configure the template
25+// rendering within OAuth2 Proxy.
26+type PageWriterOpts struct {
27+ // TemplatesPath is the path from which to load custom templates for the sign-in and error pages.
28+ TemplatesPath string
29+
30+ // ProxyPrefix is the prefix under which OAuth2 Proxy pages are served.
31+ ProxyPrefix string
32+
33+ // Footer is the footer to be displayed at the bottom of the page.
34+ // If not set, a default footer will be used.
35+ Footer string
36+
37+ // Version is the OAuth2 Proxy version to be used in the default footer.
38+ Version string
39+
40+ // Debug determines whether errors pages should be rendered with detailed
41+ // errors.
42+ Debug bool
43+
44+ // DisplayLoginForm determines whether or not the basic auth password form is displayed on the sign-in page.
45+ DisplayLoginForm bool
46+
47+ // ProviderName is the name of the provider that should be displayed on the login button.
48+ ProviderName string
49+
50+ // SignInMessage is the messge displayed above the login button.
51+ SignInMessage string
52+}
53+
54+// NewPageWriter constructs a PageWriter from the options given to allow
55+// rendering of sign-in and error pages.
56+func NewPageWriter(opts PageWriterOpts) (PageWriter, error) {
57+ templates, err := loadTemplates(opts.TemplatesPath)
58+ if err != nil {
59+ return nil, fmt.Errorf("error loading templates: %v", err)
60+ }
61+
62+ errorPage := &errorPageWriter{
63+ template: templates.Lookup("error.html"),
64+ proxyPrefix: opts.ProxyPrefix,
65+ footer: opts.Footer,
66+ version: opts.Version,
67+ debug: opts.Debug,
68+ }
69+
70+ signInPage := &signInPageWriter{
71+ template: templates.Lookup("sign_in.html"),
72+ errorPageWriter: errorPage,
73+ proxyPrefix: opts.ProxyPrefix,
74+ providerName: opts.ProviderName,
75+ signInMessage: opts.SignInMessage,
76+ footer: opts.Footer,
77+ version: opts.Version,
78+ displayLoginForm: opts.DisplayLoginForm,
79+ }
80+
81+ return &pageWriter{
82+ errorPageWriter: errorPage,
83+ signInPageWriter: signInPage,
84+ }, nil
85+}
pkg/app/pagewriter_test.gonew file+126
@@ -0,0 +1,126 @@
1+package app
2+
3+import (
4+ "io/ioutil"
5+ "net/http/httptest"
6+ "os"
7+ "path/filepath"
8+
9+ . "github.com/onsi/ginkgo"
10+ . "github.com/onsi/gomega"
11+)
12+
13+var _ = Describe("PageWriter", func() {
14+ Context("NewPageWriter", func() {
15+ var writer PageWriter
16+ var opts PageWriterOpts
17+
18+ BeforeEach(func() {
19+ opts = PageWriterOpts{
20+ TemplatesPath: "",
21+ ProxyPrefix: "/prefix",
22+ Footer: "<Footer>",
23+ Version: "<Version>",
24+ Debug: false,
25+ DisplayLoginForm: false,
26+ ProviderName: "<ProviderName>",
27+ SignInMessage: "<SignInMessage>",
28+ }
29+ })
30+
31+ Context("With no custom templates", func() {
32+ BeforeEach(func() {
33+ var err error
34+ writer, err = NewPageWriter(opts)
35+ Expect(err).ToNot(HaveOccurred())
36+ })
37+
38+ It("Writes the default error template", func() {
39+ recorder := httptest.NewRecorder()
40+ writer.WriteErrorPage(recorder, 500, "/redirect", "Some debug error")
41+
42+ body, err := ioutil.ReadAll(recorder.Result().Body)
43+ Expect(err).ToNot(HaveOccurred())
44+ Expect(string(body)).To(HavePrefix("\n<!DOCTYPE html>"))
45+ })
46+
47+ It("Writes the default sign in template", func() {
48+ recorder := httptest.NewRecorder()
49+ writer.WriteSignInPage(recorder, "/redirect")
50+
51+ body, err := ioutil.ReadAll(recorder.Result().Body)
52+ Expect(err).ToNot(HaveOccurred())
53+ Expect(string(body)).To(HavePrefix("\n<!DOCTYPE html>"))
54+ })
55+ })
56+
57+ Context("With custom templates", func() {
58+ var customDir string
59+
60+ BeforeEach(func() {
61+ var err error
62+ customDir, err = ioutil.TempDir("", "oauth2-proxy-pagewriter-test")
63+ Expect(err).ToNot(HaveOccurred())
64+
65+ templateHTML := `Custom Template`
66+ signInFile := filepath.Join(customDir, signInTemplateName)
67+ Expect(ioutil.WriteFile(signInFile, []byte(templateHTML), 0600)).To(Succeed())
68+ errorFile := filepath.Join(customDir, errorTemplateName)
69+ Expect(ioutil.WriteFile(errorFile, []byte(templateHTML), 0600)).To(Succeed())
70+
71+ opts.TemplatesPath = customDir
72+
73+ writer, err = NewPageWriter(opts)
74+ Expect(err).ToNot(HaveOccurred())
75+ })
76+
77+ AfterEach(func() {
78+ Expect(os.RemoveAll(customDir)).To(Succeed())
79+ })
80+
81+ It("Writes the custom error template", func() {
82+ recorder := httptest.NewRecorder()
83+ writer.WriteErrorPage(recorder, 500, "/redirect", "Some debug error")
84+
85+ body, err := ioutil.ReadAll(recorder.Result().Body)
86+ Expect(err).ToNot(HaveOccurred())
87+ Expect(string(body)).To(Equal("Custom Template"))
88+ })
89+
90+ It("Writes the custom sign in template", func() {
91+ recorder := httptest.NewRecorder()
92+ writer.WriteSignInPage(recorder, "/redirect")
93+
94+ body, err := ioutil.ReadAll(recorder.Result().Body)
95+ Expect(err).ToNot(HaveOccurred())
96+ Expect(string(body)).To(Equal("Custom Template"))
97+ })
98+ })
99+
100+ Context("With an invalid custom template", func() {
101+ var customDir string
102+
103+ BeforeEach(func() {
104+ var err error
105+ customDir, err = ioutil.TempDir("", "oauth2-proxy-pagewriter-test")
106+ Expect(err).ToNot(HaveOccurred())
107+
108+ templateHTML := `{{ Custom Broken Template`
109+ signInFile := filepath.Join(customDir, signInTemplateName)
110+ Expect(ioutil.WriteFile(signInFile, []byte(templateHTML), 0600)).To(Succeed())
111+
112+ opts.TemplatesPath = customDir
113+ })
114+
115+ AfterEach(func() {
116+ Expect(os.RemoveAll(customDir)).To(Succeed())
117+ })
118+
119+ It("Should return an error", func() {
120+ writer, err := NewPageWriter(opts)
121+ Expect(err).To(MatchError(ContainSubstring("template: sign_in.html:1: function \"Custom\" not defined")))
122+ Expect(writer).To(BeNil())
123+ })
124+ })
125+ })
126+})
pkg/app/sign_in_page.go+21−21
@@ -7,37 +7,37 @@ import (
7 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
7 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
8)
8)
9
9
10−// SignInPage is used to render sign-in pages.
10+// signInPageWriter is used to render sign-in pages.
11−type SignInPage struct {
11+type signInPageWriter struct {
12 // Template is the sign-in page HTML template.
12 // Template is the sign-in page HTML template.
13− Template *template.Template
13+ template *template.Template
14
14
15− // ErrorPage is used to render an error if there are problems with rendering the sign-in page.
15+ // errorPageWriter is used to render an error if there are problems with rendering the sign-in page.
16− ErrorPage *ErrorPage
16+ errorPageWriter *errorPageWriter
17
17
18 // ProxyPrefix is the prefix under which OAuth2 Proxy pages are served.
18 // ProxyPrefix is the prefix under which OAuth2 Proxy pages are served.
19− ProxyPrefix string
19+ proxyPrefix string
20
20
21 // ProviderName is the name of the provider that should be displayed on the login button.
21 // ProviderName is the name of the provider that should be displayed on the login button.
22− ProviderName string
22+ providerName string
23
23
24 // SignInMessage is the messge displayed above the login button.
24 // SignInMessage is the messge displayed above the login button.
25− SignInMessage string
25+ signInMessage string
26
26
27 // Footer is the footer to be displayed at the bottom of the page.
27 // Footer is the footer to be displayed at the bottom of the page.
28 // If not set, a default footer will be used.
28 // If not set, a default footer will be used.
29− Footer string
29+ footer string
30
30
31 // Version is the OAuth2 Proxy version to be used in the default footer.
31 // Version is the OAuth2 Proxy version to be used in the default footer.
32− Version string
32+ version string
33
33
34 // DisplayLoginForm determines whether or not the basic auth password form is displayed on the sign-in page.
34 // DisplayLoginForm determines whether or not the basic auth password form is displayed on the sign-in page.
35− DisplayLoginForm bool
35+ displayLoginForm bool
36}
36}
37
37
38−// Render writes the sign-in page to the given response writer.
38+// WriteSignInPage writes the sign-in page to the given response writer.
39// It uses the redirectURL to be able to set the final destination for the user post login.
39// It uses the redirectURL to be able to set the final destination for the user post login.
40−func (s *SignInPage) Render(rw http.ResponseWriter, redirectURL string) {
40+func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL string) {
41 // We allow unescaped template.HTML since it is user configured options
41 // We allow unescaped template.HTML since it is user configured options
42 /* #nosec G203 */
42 /* #nosec G203 */
43 t := struct {
43 t := struct {
@@ -49,18 +49,18 @@ func (s *SignInPage) Render(rw http.ResponseWriter, redirectURL string) {
49 ProxyPrefix string
49 ProxyPrefix string
50 Footer template.HTML
50 Footer template.HTML
51 }{
51 }{
52− ProviderName: s.ProviderName,
52+ ProviderName: s.providerName,
53− SignInMessage: template.HTML(s.SignInMessage),
53+ SignInMessage: template.HTML(s.signInMessage),
54− CustomLogin: s.DisplayLoginForm,
54+ CustomLogin: s.displayLoginForm,
55 Redirect: redirectURL,
55 Redirect: redirectURL,
56− Version: s.Version,
56+ Version: s.version,
57− ProxyPrefix: s.ProxyPrefix,
57+ ProxyPrefix: s.proxyPrefix,
58− Footer: template.HTML(s.Footer),
58+ Footer: template.HTML(s.footer),
59 }
59 }
60
60
61− err := s.Template.Execute(rw, t)
61+ err := s.template.Execute(rw, t)
62 if err != nil {
62 if err != nil {
63 logger.Printf("Error rendering sign-in template: %v", err)
63 logger.Printf("Error rendering sign-in template: %v", err)
64− s.ErrorPage.Render(rw, http.StatusInternalServerError, redirectURL, err.Error())
64+ s.errorPageWriter.WriteErrorPage(rw, http.StatusInternalServerError, redirectURL, err.Error())
65 }
65 }
66}
66}
pkg/app/sign_in_page_test.go+17−17
@@ -9,35 +9,35 @@ import (
9 . "github.com/onsi/gomega"
9 . "github.com/onsi/gomega"
10)
10)
11
11
12−var _ = Describe("SignIn Page", func() {
12+var _ = Describe("SignIn Page Writer", func() {
13− var signInPage *SignInPage
13+ var signInPage *signInPageWriter
14
14
15 BeforeEach(func() {
15 BeforeEach(func() {
16 errorTmpl, err := template.New("").Parse("{{.Title}}")
16 errorTmpl, err := template.New("").Parse("{{.Title}}")
17 Expect(err).ToNot(HaveOccurred())
17 Expect(err).ToNot(HaveOccurred())
18− errorPage := &ErrorPage{
18+ errorPage := &errorPageWriter{
19− Template: errorTmpl,
19+ template: errorTmpl,
20 }
20 }
21
21
22 tmpl, err := template.New("").Parse("{{.ProxyPrefix}} {{.ProviderName}} {{.SignInMessage}} {{.Footer}} {{.Version}} {{.Redirect}} {{.CustomLogin}}")
22 tmpl, err := template.New("").Parse("{{.ProxyPrefix}} {{.ProviderName}} {{.SignInMessage}} {{.Footer}} {{.Version}} {{.Redirect}} {{.CustomLogin}}")
23 Expect(err).ToNot(HaveOccurred())
23 Expect(err).ToNot(HaveOccurred())
24
24
25− signInPage = &SignInPage{
25+ signInPage = &signInPageWriter{
26− Template: tmpl,
26+ template: tmpl,
27− ErrorPage: errorPage,
27+ errorPageWriter: errorPage,
28− ProxyPrefix: "/prefix/",
28+ proxyPrefix: "/prefix/",
29− ProviderName: "My Provider",
29+ providerName: "My Provider",
30− SignInMessage: "Sign In Here",
30+ signInMessage: "Sign In Here",
31− Footer: "Custom Footer Text",
31+ footer: "Custom Footer Text",
32− Version: "v0.0.0-test",
32+ version: "v0.0.0-test",
33− DisplayLoginForm: true,
33+ displayLoginForm: true,
34 }
34 }
35 })
35 })
36
36
37− Context("Render", func() {
37+ Context("WriteSignInPage", func() {
38 It("Writes the template to the response writer", func() {
38 It("Writes the template to the response writer", func() {
39 recorder := httptest.NewRecorder()
39 recorder := httptest.NewRecorder()
40− signInPage.Render(recorder, "/redirect")
40+ signInPage.WriteSignInPage(recorder, "/redirect")
41
41
42 body, err := ioutil.ReadAll(recorder.Result().Body)
42 body, err := ioutil.ReadAll(recorder.Result().Body)
43 Expect(err).ToNot(HaveOccurred())
43 Expect(err).ToNot(HaveOccurred())
@@ -48,10 +48,10 @@ var _ = Describe("SignIn Page", func() {
48 // Overwrite the template with something bad
48 // Overwrite the template with something bad
49 tmpl, err := template.New("").Parse("{{.Unknown}}")
49 tmpl, err := template.New("").Parse("{{.Unknown}}")
50 Expect(err).ToNot(HaveOccurred())
50 Expect(err).ToNot(HaveOccurred())
51− signInPage.Template = tmpl
51+ signInPage.template = tmpl
52
52
53 recorder := httptest.NewRecorder()
53 recorder := httptest.NewRecorder()
54− signInPage.Render(recorder, "/redirect")
54+ signInPage.WriteSignInPage(recorder, "/redirect")
55
55
56 body, err := ioutil.ReadAll(recorder.Result().Body)
56 body, err := ioutil.ReadAll(recorder.Result().Body)
57 Expect(err).ToNot(HaveOccurred())
57 Expect(err).ToNot(HaveOccurred())
pkg/app/templates.go+2−2
@@ -206,10 +206,10 @@ const (
206{{end}}`
206{{end}}`
207)
207)
208
208
209−// LoadTemplates adds the Sign In and Error templates from the custom template
209+// loadTemplates adds the Sign In and Error templates from the custom template
210// directory, or uses the defaults if they do not exist or the custom directory
210// directory, or uses the defaults if they do not exist or the custom directory
211// is not provided.
211// is not provided.
212−func LoadTemplates(customDir string) (*template.Template, error) {
212+func loadTemplates(customDir string) (*template.Template, error) {
213 t := template.New("").Funcs(template.FuncMap{
213 t := template.New("").Funcs(template.FuncMap{
214 "ToUpper": strings.ToUpper,
214 "ToUpper": strings.ToUpper,
215 "ToLower": strings.ToLower,
215 "ToLower": strings.ToLower,
pkg/app/templates_test.go+11−11
@@ -21,16 +21,16 @@ var _ = Describe("Templates", func() {
21
21
22 templateHTML := `{{.TestString}} {{.TestString | ToLower}} {{.TestString | ToUpper}}`
22 templateHTML := `{{.TestString}} {{.TestString | ToLower}} {{.TestString | ToUpper}}`
23 signInFile := filepath.Join(customDir, signInTemplateName)
23 signInFile := filepath.Join(customDir, signInTemplateName)
24− Expect(ioutil.WriteFile(signInFile, []byte(templateHTML), 0666)).To(Succeed())
24+ Expect(ioutil.WriteFile(signInFile, []byte(templateHTML), 0600)).To(Succeed())
25 errorFile := filepath.Join(customDir, errorTemplateName)
25 errorFile := filepath.Join(customDir, errorTemplateName)
26− Expect(ioutil.WriteFile(errorFile, []byte(templateHTML), 0666)).To(Succeed())
26+ Expect(ioutil.WriteFile(errorFile, []byte(templateHTML), 0600)).To(Succeed())
27 })
27 })
28
28
29 AfterEach(func() {
29 AfterEach(func() {
30 Expect(os.RemoveAll(customDir)).To(Succeed())
30 Expect(os.RemoveAll(customDir)).To(Succeed())
31 })
31 })
32
32
33− Context("LoadTemplates", func() {
33+ Context("loadTemplates", func() {
34 var data interface{}
34 var data interface{}
35 var t *template.Template
35 var t *template.Template
36
36
@@ -73,7 +73,7 @@ var _ = Describe("Templates", func() {
73 Context("With no custom directory", func() {
73 Context("With no custom directory", func() {
74 BeforeEach(func() {
74 BeforeEach(func() {
75 var err error
75 var err error
76− t, err = LoadTemplates("")
76+ t, err = loadTemplates("")
77 Expect(err).ToNot(HaveOccurred())
77 Expect(err).ToNot(HaveOccurred())
78 })
78 })
79
79
@@ -94,7 +94,7 @@ var _ = Describe("Templates", func() {
94 Context("With both templates", func() {
94 Context("With both templates", func() {
95 BeforeEach(func() {
95 BeforeEach(func() {
96 var err error
96 var err error
97− t, err = LoadTemplates(customDir)
97+ t, err = loadTemplates(customDir)
98 Expect(err).ToNot(HaveOccurred())
98 Expect(err).ToNot(HaveOccurred())
99 })
99 })
100
100
@@ -116,7 +116,7 @@ var _ = Describe("Templates", func() {
116 Expect(os.Remove(filepath.Join(customDir, errorTemplateName))).To(Succeed())
116 Expect(os.Remove(filepath.Join(customDir, errorTemplateName))).To(Succeed())
117
117
118 var err error
118 var err error
119− t, err = LoadTemplates(customDir)
119+ t, err = loadTemplates(customDir)
120 Expect(err).ToNot(HaveOccurred())
120 Expect(err).ToNot(HaveOccurred())
121 })
121 })
122
122
@@ -138,7 +138,7 @@ var _ = Describe("Templates", func() {
138 Expect(os.Remove(filepath.Join(customDir, signInTemplateName))).To(Succeed())
138 Expect(os.Remove(filepath.Join(customDir, signInTemplateName))).To(Succeed())
139
139
140 var err error
140 var err error
141− t, err = LoadTemplates(customDir)
141+ t, err = loadTemplates(customDir)
142 Expect(err).ToNot(HaveOccurred())
142 Expect(err).ToNot(HaveOccurred())
143 })
143 })
144
144
@@ -158,11 +158,11 @@ var _ = Describe("Templates", func() {
158 Context("With an invalid sign_in template", func() {
158 Context("With an invalid sign_in template", func() {
159 BeforeEach(func() {
159 BeforeEach(func() {
160 signInFile := filepath.Join(customDir, signInTemplateName)
160 signInFile := filepath.Join(customDir, signInTemplateName)
161− Expect(ioutil.WriteFile(signInFile, []byte("{{"), 0666))
161+ Expect(ioutil.WriteFile(signInFile, []byte("{{"), 0600))
162 })
162 })
163
163
164 It("Should return an error when loading templates", func() {
164 It("Should return an error when loading templates", func() {
165− t, err := LoadTemplates(customDir)
165+ t, err := loadTemplates(customDir)
166 Expect(err).To(MatchError(HavePrefix("could not add Sign In template:")))
166 Expect(err).To(MatchError(HavePrefix("could not add Sign In template:")))
167 Expect(t).To(BeNil())
167 Expect(t).To(BeNil())
168 })
168 })
@@ -171,11 +171,11 @@ var _ = Describe("Templates", func() {
171 Context("With an invalid error template", func() {
171 Context("With an invalid error template", func() {
172 BeforeEach(func() {
172 BeforeEach(func() {
173 errorFile := filepath.Join(customDir, errorTemplateName)
173 errorFile := filepath.Join(customDir, errorTemplateName)
174− Expect(ioutil.WriteFile(errorFile, []byte("{{"), 0666))
174+ Expect(ioutil.WriteFile(errorFile, []byte("{{"), 0600))
175 })
175 })
176
176
177 It("Should return an error when loading templates", func() {
177 It("Should return an error when loading templates", func() {
178− t, err := LoadTemplates(customDir)
178+ t, err := loadTemplates(customDir)
179 Expect(err).To(MatchError(HavePrefix("could not add Error template:")))
179 Expect(err).To(MatchError(HavePrefix("could not add Error template:")))
180 Expect(t).To(BeNil())
180 Expect(t).To(BeNil())
181 })
181 })