vouch-proxy/pkg/responses/responses.go

138 lines
4.1 KiB
Go

/*
Copyright 2020 The Vouch Proxy Authors.
Use of this source code is governed by The MIT License (MIT) that
can be found in the LICENSE file. Software distributed under The
MIT License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
OR CONDITIONS OF ANY KIND, either express or implied.
*/
package responses
import (
"errors"
"html/template"
"net/http"
"github.com/vouch/vouch-proxy/pkg/cfg"
"github.com/vouch/vouch-proxy/pkg/cookie"
"go.uber.org/zap"
"golang.org/x/net/context"
)
// Index variables passed to index.tmpl
type Index struct {
Msg string
TestURLs []string
Testing bool
DocumentRoot string
}
var (
indexTemplate *template.Template
errorTemplate *template.Template
log *zap.SugaredLogger
fastlog *zap.Logger
errNotAuthorized = errors.New("not authorized")
)
// Configure see main.go configure()
func Configure() {
log = cfg.Logging.Logger
fastlog = cfg.Logging.FastLogger
log.Debugf("responses.Configure() attempting to parse embedded templates")
indexTemplate = template.Must(template.ParseFS(cfg.Templates, "templates/index.tmpl"))
}
// RenderIndex render the response as an HTML page, mostly used in testing
func RenderIndex(w http.ResponseWriter, msg string) {
if err := indexTemplate.Execute(w, &Index{Msg: msg, TestURLs: cfg.Cfg.TestURLs, Testing: cfg.Cfg.Testing, DocumentRoot: cfg.Cfg.DocumentRoot}); err != nil {
log.Error(err)
}
}
// renderError html error page
// something terse for the end user
func renderError(w http.ResponseWriter, msg string, status int) {
log.Debugf("rendering error for user: %s", msg)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(status)
if err := indexTemplate.Execute(w, &Index{Msg: msg, DocumentRoot: cfg.Cfg.DocumentRoot}); err != nil {
log.Error(err)
}
}
// OK200 returns "200 OK"
func OK200(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("200 OK\n"))
if err != nil {
log.Error(err)
}
}
// Redirect302 redirect to the specified rURL
func Redirect302(w http.ResponseWriter, r *http.Request, rURL string) {
if cfg.Cfg.Testing {
cfg.Cfg.TestURLs = append(cfg.Cfg.TestURLs, rURL)
RenderIndex(w, "302 redirect to: "+rURL)
return
}
http.Redirect(w, r, rURL, http.StatusFound)
}
// Error400 Bad Request
func Error400(w http.ResponseWriter, r *http.Request, e error) {
cancelClearSetError(w, r, e)
renderError(w, "400 Bad Request", http.StatusBadRequest)
}
// Error401 Unauthorized, the standard error returned when failing /validate
// this is captured by nginx, which converts the 401 into 302 to the login page
func Error401(w http.ResponseWriter, r *http.Request, e error) {
cancelClearSetError(w, r, e)
http.Error(w, e.Error(), http.StatusUnauthorized)
// renderError(w, "401 Unauthorized")
}
// Error401HTTP
func Error401HTTP(w http.ResponseWriter, r *http.Request, e error) {
cancelClearSetError(w, r, e)
renderError(w, e.Error(), http.StatusUnauthorized)
}
// Error403 Forbidden
// if there's an error during /auth or if they don't pass validation in /auth
func Error403(w http.ResponseWriter, r *http.Request, e error) {
cancelClearSetError(w, r, e)
renderError(w, "403 Forbidden", http.StatusForbidden)
}
// Error500 Internal Error
// something is not right, hopefully this never happens
func Error500(w http.ResponseWriter, r *http.Request, e error) {
cancelClearSetError(w, r, e)
log.Infof("If this error persists it may be worthy of a bug report but please check your setup first. See the README at %s", cfg.Branding.URL)
renderError(w, "500 - Internal Server Error", http.StatusInternalServerError)
}
// cancelClearSetError convenience method to keep it DRY
func cancelClearSetError(w http.ResponseWriter, r *http.Request, e error) {
log.Warn(e)
cookie.ClearCookie(w, r)
w.Header().Set(cfg.Cfg.Headers.Error, e.Error())
addErrandCancelRequest(r)
}
// cfg.ErrCtx is tested by `jwtmanager.JWTCacheHandler`
func addErrandCancelRequest(r *http.Request) {
ctx, cancel := context.WithCancel(r.Context())
ctx = context.WithValue(ctx, cfg.ErrCtxKey, true)
*r = *r.Clone(ctx)
cancel() // we're done
return
}