mirror of
https://github.com/nais/wonderwall.git
synced 2026-05-07 17:06:57 +00:00
128 lines
3.7 KiB
Go
128 lines
3.7 KiB
Go
package router
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/rs/zerolog"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/nais/wonderwall/pkg/config"
|
|
"github.com/nais/wonderwall/pkg/openid"
|
|
logentry "github.com/nais/wonderwall/pkg/router/middleware"
|
|
"github.com/nais/wonderwall/pkg/router/paths"
|
|
"github.com/nais/wonderwall/pkg/router/request"
|
|
)
|
|
|
|
type ErrorPage struct {
|
|
CorrelationID string
|
|
RetryURI string
|
|
}
|
|
|
|
//go:embed templates/error.gohtml
|
|
var errorGoHtml string
|
|
var errorTemplate *template.Template
|
|
|
|
func init() {
|
|
var err error
|
|
|
|
errorTemplate = template.New("error")
|
|
errorTemplate, err = errorTemplate.Parse(errorGoHtml)
|
|
if err != nil {
|
|
log.Fatalf("parsing error template: %+v", err)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) respondError(w http.ResponseWriter, r *http.Request, statusCode int, cause error, level zerolog.Level) {
|
|
logger := logentry.LogEntry(r.Context())
|
|
logger.WithLevel(level).Stack().Err(cause).Msgf("error in route: %+v", cause)
|
|
|
|
if len(h.Cfg.Wonderwall().ErrorRedirectURI) > 0 {
|
|
err := h.customErrorRedirect(w, r, statusCode)
|
|
if err == nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
h.defaultErrorResponse(w, r, statusCode)
|
|
}
|
|
|
|
func (h *Handler) defaultErrorResponse(w http.ResponseWriter, r *http.Request, statusCode int) {
|
|
w.WriteHeader(statusCode)
|
|
|
|
loginCookie, err := h.getLoginCookie(r)
|
|
if err != nil {
|
|
loginCookie = nil
|
|
}
|
|
|
|
errorPage := ErrorPage{
|
|
CorrelationID: middleware.GetReqID(r.Context()),
|
|
RetryURI: RetryURI(r, h.Cfg.Wonderwall().Ingress, loginCookie),
|
|
}
|
|
err = errorTemplate.Execute(w, errorPage)
|
|
if err != nil {
|
|
log.Errorf("executing error template: %+v", err)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) customErrorRedirect(w http.ResponseWriter, r *http.Request, statusCode int) error {
|
|
override, err := url.Parse(h.Cfg.Wonderwall().ErrorRedirectURI)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// strip scheme and host to avoid cross-domain redirects
|
|
override.Scheme = ""
|
|
override.Host = ""
|
|
|
|
query := override.Query()
|
|
query.Add("correlation_id", middleware.GetReqID(r.Context()))
|
|
query.Add("status_code", strconv.Itoa(statusCode))
|
|
|
|
override.RawQuery = query.Encode()
|
|
|
|
errorRedirectURI := override.String()
|
|
http.Redirect(w, r, errorRedirectURI, http.StatusFound)
|
|
return nil
|
|
}
|
|
|
|
func (h *Handler) InternalError(w http.ResponseWriter, r *http.Request, cause error) {
|
|
h.respondError(w, r, http.StatusInternalServerError, cause, zerolog.ErrorLevel)
|
|
}
|
|
|
|
func (h *Handler) BadRequest(w http.ResponseWriter, r *http.Request, cause error) {
|
|
h.respondError(w, r, http.StatusBadRequest, cause, zerolog.ErrorLevel)
|
|
}
|
|
|
|
func (h *Handler) Unauthorized(w http.ResponseWriter, r *http.Request, cause error) {
|
|
h.respondError(w, r, http.StatusUnauthorized, cause, zerolog.WarnLevel)
|
|
}
|
|
|
|
// RetryURI returns a URI that should retry the desired route that failed.
|
|
// It only handles the routes exposed by Wonderwall, i.e. `/oauth2/*`. As these routes
|
|
// are related to the authentication flow, we default to redirecting back to the handled
|
|
// `/oauth2/login` endpoint unless the original request attempted to reach the logout-flow.
|
|
func RetryURI(r *http.Request, ingress string, loginCookie *openid.LoginCookie) string {
|
|
retryURI := r.URL.Path
|
|
prefix := config.ParseIngress(ingress)
|
|
|
|
if strings.HasSuffix(retryURI, paths.OAuth2+paths.Logout) || strings.HasSuffix(retryURI, paths.OAuth2+paths.FrontChannelLogout) {
|
|
return prefix + retryURI
|
|
}
|
|
|
|
redirect := request.CanonicalRedirectURL(r, ingress)
|
|
|
|
if loginCookie != nil && len(loginCookie.Referer) > 0 {
|
|
redirect = loginCookie.Referer
|
|
}
|
|
|
|
retryURI = fmt.Sprintf(prefix + paths.OAuth2 + paths.Login)
|
|
retryURI = retryURI + fmt.Sprintf("?%s=%s", request.RedirectURLParameter, redirect)
|
|
return retryURI
|
|
}
|