Skip to content

Commit

Permalink
Fix method names in ticket.go
Browse files Browse the repository at this point in the history
  • Loading branch information
Bibob7 committed Feb 23, 2021
1 parent 9861d00 commit 408ba1a
Show file tree
Hide file tree
Showing 16 changed files with 1,262 additions and 6 deletions.
50 changes: 50 additions & 0 deletions pkg/apis/options/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package options

import "github.com/spf13/pflag"

// Templates includes options for configuring the sign in and error pages
// appearance.
type Templates struct {
// Path is the path to a folder containing a sign_in.html and an error.html
// template.
// These files will be used instead of the default templates if present.
// If either file is missing, the default will be used instead.
Path string `flag:"custom-templates-dir" cfg:"custom_templates_dir"`

// Banner overides the default sign_in page banner text. If unspecified,
// the message will give users a list of allowed email domains.
Banner string `flag:"banner" cfg:"banner"`

// Footer overrides the default sign_in page footer text.
Footer string `flag:"footer" cfg:"footer"`

// DisplayLoginForm determines whether the sign_in page should render a
// password form if a static passwords file (htpasswd file) has been
// configured.
DisplayLoginForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"`

// Debug renders detailed errors when an error page is shown.
// It is not advised to use this in production as errors may contain sensitive
// information.
// Use only for diagnosing backend errors.
Debug bool `flag:"show-debug-on-error" cfg:"show-debug-on-error"`
}

func templatesFlagSet() *pflag.FlagSet {
flagSet := pflag.NewFlagSet("templates", pflag.ExitOnError)

flagSet.String("custom-templates-dir", "", "path to custom html templates")
flagSet.String("banner", "", "custom banner string. Use \"-\" to disable default banner.")
flagSet.String("footer", "", "custom footer string. Use \"-\" to disable default footer.")
flagSet.Bool("display-htpasswd-form", true, "display username / password login form if an htpasswd file is provided")
flagSet.Bool("show-debug-on-error", false, "show detailed error information on error pages (WARNING: this may contain sensitive information - do not use in production)")

return flagSet
}

// templatesDefaults creates a Templates and populates it with any default values
func templatesDefaults() Templates {
return Templates{
DisplayLoginForm: true,
}
}
97 changes: 97 additions & 0 deletions pkg/app/pagewriter/error_page.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package pagewriter

import (
"fmt"
"html/template"
"net/http"

"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
)

// errorMessages are default error messages for each of the the different
// http status codes expected to be rendered in the error page.
var errorMessages = map[int]string{
http.StatusInternalServerError: "Oops! Something went wrong. For more information contact your server administrator.",
http.StatusNotFound: "We could not find the resource you were looking for.",
http.StatusForbidden: "You do not have permission to access this resource.",
http.StatusUnauthorized: "You need to be logged in to access this resource.",
}

// errorPageWriter is used to render error pages.
type errorPageWriter struct {
// template is the error page HTML template.
template *template.Template

// proxyPrefix is the prefix under which OAuth2 Proxy pages are served.
proxyPrefix string

// footer is the footer to be displayed at the bottom of the page.
// If not set, a default footer will be used.
footer string

// version is the OAuth2 Proxy version to be used in the default footer.
version string

// debug determines whether errors pages should be rendered with detailed
// errors.
debug bool
}

// WriteErrorPage writes an error page to the given response writer.
// It uses the passed redirectURL to give users the option to go back to where
// they originally came from or try signing in again.
func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, status int, redirectURL string, appError string, messages ...interface{}) {
rw.WriteHeader(status)

// We allow unescaped template.HTML since it is user configured options
/* #nosec G203 */
data := struct {
Title string
Message string
ProxyPrefix string
StatusCode int
Redirect string
Footer template.HTML
Version string
}{
Title: http.StatusText(status),
Message: e.getMessage(status, appError, messages...),
ProxyPrefix: e.proxyPrefix,
StatusCode: status,
Redirect: redirectURL,
Footer: template.HTML(e.footer),
Version: e.version,
}

if err := e.template.Execute(rw, data); err != nil {
logger.Printf("Error rendering error template: %v", err)
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}

// ProxyErrorHandler is used by the upstream ReverseProxy to render error pages
// when there are issues with upstream servers.
// It is expected to always render a bad gateway error.
func (e *errorPageWriter) ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error) {
logger.Errorf("Error proxying to upstream server: %v", proxyErr)
e.WriteErrorPage(rw, http.StatusBadGateway, "", proxyErr.Error(), "There was a problem connecting to the upstream server.")
}

// getMessage creates the message for the template parameters.
// If the errorPagewriter.Debug is enabled, the application error takes precedence.
// Otherwise, any messages will be used.
// The first message is expected to be a format string.
// If no messages are supplied, a default error message will be used.
func (e *errorPageWriter) getMessage(status int, appError string, messages ...interface{}) string {
if e.debug {
return appError
}
if len(messages) > 0 {
format := fmt.Sprintf("%v", messages[0])
return fmt.Sprintf(format, messages[1:]...)
}
if msg, ok := errorMessages[status]; ok {
return msg
}
return "Unknown error"
}
101 changes: 101 additions & 0 deletions pkg/app/pagewriter/error_page_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package pagewriter

import (
"errors"
"html/template"
"io/ioutil"
"net/http/httptest"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Error Page Writer", func() {
var errorPage *errorPageWriter

BeforeEach(func() {
tmpl, err := template.New("").Parse("{{.Title}} {{.Message}} {{.ProxyPrefix}} {{.StatusCode}} {{.Redirect}} {{.Footer}} {{.Version}}")
Expect(err).ToNot(HaveOccurred())

errorPage = &errorPageWriter{
template: tmpl,
proxyPrefix: "/prefix/",
footer: "Custom Footer Text",
version: "v0.0.0-test",
}
})

Context("WriteErrorPage", func() {
It("Writes the template to the response writer", func() {
recorder := httptest.NewRecorder()
errorPage.WriteErrorPage(recorder, 403, "/redirect", "Access Denied")

body, err := ioutil.ReadAll(recorder.Result().Body)
Expect(err).ToNot(HaveOccurred())
Expect(string(body)).To(Equal("Forbidden You do not have permission to access this resource. /prefix/ 403 /redirect Custom Footer Text v0.0.0-test"))
})

It("With a different code, uses the stock message for the correct code", func() {
recorder := httptest.NewRecorder()
errorPage.WriteErrorPage(recorder, 500, "/redirect", "Access Denied")

body, err := ioutil.ReadAll(recorder.Result().Body)
Expect(err).ToNot(HaveOccurred())
Expect(string(body)).To(Equal("Internal Server Error Oops! Something went wrong. For more information contact your server administrator. /prefix/ 500 /redirect Custom Footer Text v0.0.0-test"))
})

It("With a message override, uses the message", func() {
recorder := httptest.NewRecorder()
errorPage.WriteErrorPage(recorder, 403, "/redirect", "Access Denied", "An extra message: %s", "with more context.")

body, err := ioutil.ReadAll(recorder.Result().Body)
Expect(err).ToNot(HaveOccurred())
Expect(string(body)).To(Equal("Forbidden An extra message: with more context. /prefix/ 403 /redirect Custom Footer Text v0.0.0-test"))
})
})

Context("ProxyErrorHandler", func() {
It("Writes a bad gateway error the response writer", func() {
req := httptest.NewRequest("", "/bad-gateway", nil)
recorder := httptest.NewRecorder()
errorPage.ProxyErrorHandler(recorder, req, errors.New("some upstream error"))

body, err := ioutil.ReadAll(recorder.Result().Body)
Expect(err).ToNot(HaveOccurred())
Expect(string(body)).To(Equal("Bad Gateway There was a problem connecting to the upstream server. /prefix/ 502 Custom Footer Text v0.0.0-test"))
})
})

Context("With Debug enabled", func() {
BeforeEach(func() {
tmpl, err := template.New("").Parse("{{.Message}}")
Expect(err).ToNot(HaveOccurred())

errorPage.template = tmpl
errorPage.debug = true
})

Context("WriteErrorPage", func() {
It("Writes the detailed error in place of the message", func() {
recorder := httptest.NewRecorder()
errorPage.WriteErrorPage(recorder, 403, "/redirect", "Debug error")

body, err := ioutil.ReadAll(recorder.Result().Body)
Expect(err).ToNot(HaveOccurred())
Expect(string(body)).To(Equal("Debug error"))
})
})

Context("ProxyErrorHandler", func() {
It("Writes a bad gateway error the response writer", func() {
req := httptest.NewRequest("", "/bad-gateway", nil)
recorder := httptest.NewRecorder()
errorPage.ProxyErrorHandler(recorder, req, errors.New("some upstream error"))

body, err := ioutil.ReadAll(recorder.Result().Body)
Expect(err).ToNot(HaveOccurred())
Expect(string(body)).To(Equal("some upstream error"))
})
})
})
})
85 changes: 85 additions & 0 deletions pkg/app/pagewriter/pagewriter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package pagewriter

import (
"fmt"
"net/http"
)

// Writer is an interface for rendering html templates for both sign-in and
// error pages.
// It can also be used to write errors for the http.ReverseProxy used in the
// upstream package.
type Writer interface {
WriteSignInPage(rw http.ResponseWriter, redirectURL string)
WriteErrorPage(rw http.ResponseWriter, status int, redirectURL string, appError string, messages ...interface{})
ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error)
}

// pageWriter implements the Writer interface
type pageWriter struct {
*errorPageWriter
*signInPageWriter
}

// Opts contains all options required to configure the template
// rendering within OAuth2 Proxy.
type Opts struct {
// TemplatesPath is the path from which to load custom templates for the sign-in and error pages.
TemplatesPath string

// ProxyPrefix is the prefix under which OAuth2 Proxy pages are served.
ProxyPrefix string

// Footer is the footer to be displayed at the bottom of the page.
// If not set, a default footer will be used.
Footer string

// Version is the OAuth2 Proxy version to be used in the default footer.
Version string

// Debug determines whether errors pages should be rendered with detailed
// errors.
Debug bool

// DisplayLoginForm determines whether or not the basic auth password form is displayed on the sign-in page.
DisplayLoginForm bool

// ProviderName is the name of the provider that should be displayed on the login button.
ProviderName string

// SignInMessage is the messge displayed above the login button.
SignInMessage string
}

// NewWriter constructs a Writer from the options given to allow
// rendering of sign-in and error pages.
func NewWriter(opts Opts) (Writer, error) {
templates, err := loadTemplates(opts.TemplatesPath)
if err != nil {
return nil, fmt.Errorf("error loading templates: %v", err)
}

errorPage := &errorPageWriter{
template: templates.Lookup("error.html"),
proxyPrefix: opts.ProxyPrefix,
footer: opts.Footer,
version: opts.Version,
debug: opts.Debug,
}

signInPage := &signInPageWriter{
template: templates.Lookup("sign_in.html"),
errorPageWriter: errorPage,
proxyPrefix: opts.ProxyPrefix,
providerName: opts.ProviderName,
signInMessage: opts.SignInMessage,
footer: opts.Footer,
version: opts.Version,
displayLoginForm: opts.DisplayLoginForm,
}

return &pageWriter{
errorPageWriter: errorPage,
signInPageWriter: signInPage,
}, nil
}
17 changes: 17 additions & 0 deletions pkg/app/pagewriter/pagewriter_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package pagewriter

import (
"testing"

"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestOptionsSuite(t *testing.T) {
logger.SetOutput(GinkgoWriter)
logger.SetErrOutput(GinkgoWriter)

RegisterFailHandler(Fail)
RunSpecs(t, "App Suite")
}
Loading

0 comments on commit 408ba1a

Please sign in to comment.