oauth2-proxy/oauth2-proxy

View on GitHub
pkg/app/pagewriter/pagewriter.go

Summary

Maintainability
A
1 hr
Test Coverage
B
86%
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, req *http.Request, redirectURL string, statusCode int)
    WriteErrorPage(rw http.ResponseWriter, opts ErrorPageOpts)
    ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error)
    WriteRobotsTxt(rw http.ResponseWriter, req *http.Request)
}

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

// 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

    // CustomLogo is the path or URL to a logo to be displayed on the sign in page.
    // The logo can be either PNG, JPG/JPEG or SVG.
    // If a URL is used, image support depends on the browser.
    CustomLogo 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)
    }

    logoData, err := loadCustomLogo(opts.CustomLogo)
    if err != nil {
        return nil, fmt.Errorf("error loading logo: %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,
        logoData:         logoData,
    }

    staticPages, err := newStaticPageWriter(opts.TemplatesPath, errorPage)
    if err != nil {
        return nil, fmt.Errorf("error loading static page writer: %v", err)
    }

    return &pageWriter{
        errorPageWriter:  errorPage,
        signInPageWriter: signInPage,
        staticPageWriter: staticPages,
    }, nil
}

// WriterFuncs is an implementation of the PageWriter interface based
// on override functions.
// If any of the funcs are not provided, a default implementation will be used.
// This is primarily for us in testing.
type WriterFuncs struct {
    SignInPageFunc func(rw http.ResponseWriter, req *http.Request, redirectURL string, statusCode int)
    ErrorPageFunc  func(rw http.ResponseWriter, opts ErrorPageOpts)
    ProxyErrorFunc func(rw http.ResponseWriter, req *http.Request, proxyErr error)
    RobotsTxtfunc  func(rw http.ResponseWriter, req *http.Request)
}

// WriteSignInPage implements the Writer interface.
// If the SignInPageFunc is provided, this will be used, else a default
// implementation will be used.
func (w *WriterFuncs) WriteSignInPage(rw http.ResponseWriter, req *http.Request, redirectURL string, statusCode int) {
    if w.SignInPageFunc != nil {
        w.SignInPageFunc(rw, req, redirectURL, statusCode)
        return
    }

    if _, err := rw.Write([]byte("Sign In")); err != nil {
        rw.WriteHeader(http.StatusInternalServerError)
    }
}

// WriteErrorPage implements the Writer interface.
// If the ErrorPageFunc is provided, this will be used, else a default
// implementation will be used.
func (w *WriterFuncs) WriteErrorPage(rw http.ResponseWriter, opts ErrorPageOpts) {
    if w.ErrorPageFunc != nil {
        w.ErrorPageFunc(rw, opts)
        return
    }

    rw.WriteHeader(opts.Status)
    errMsg := fmt.Sprintf("%d - %v", opts.Status, opts.AppError)
    if _, err := rw.Write([]byte(errMsg)); err != nil {
        rw.WriteHeader(http.StatusInternalServerError)
    }
}

// ProxyErrorHandler implements the Writer interface.
// If the ProxyErrorFunc is provided, this will be used, else a default
// implementation will be used.
func (w *WriterFuncs) ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error) {
    if w.ProxyErrorFunc != nil {
        w.ProxyErrorFunc(rw, req, proxyErr)
        return
    }

    w.WriteErrorPage(rw, ErrorPageOpts{
        Status:   http.StatusBadGateway,
        AppError: proxyErr.Error(),
    })
}

// WriteRobotsTxt implements the Writer interface.
// If the RobotsTxtfunc is provided, this will be used, else a default
// implementation will be used.
func (w *WriterFuncs) WriteRobotsTxt(rw http.ResponseWriter, req *http.Request) {
    if w.RobotsTxtfunc != nil {
        w.RobotsTxtfunc(rw, req)
        return
    }

    if _, err := rw.Write([]byte("Allow: *")); err != nil {
        rw.WriteHeader(http.StatusInternalServerError)
    }
}