146 lines
3.8 KiB
Go
146 lines
3.8 KiB
Go
package lib
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ed25519"
|
|
"git.gammaspectra.live/git/go-away/lib/challenge"
|
|
"git.gammaspectra.live/git/go-away/lib/policy"
|
|
"git.gammaspectra.live/git/go-away/utils"
|
|
"github.com/google/cel-go/cel"
|
|
"log/slog"
|
|
"maps"
|
|
"net/http"
|
|
)
|
|
|
|
// Defines challenge.StateInterface
|
|
|
|
var _ challenge.StateInterface
|
|
|
|
func (state *State) ProgramEnv() *cel.Env {
|
|
return state.programEnv
|
|
}
|
|
|
|
func (state *State) Client() *http.Client {
|
|
return state.client
|
|
}
|
|
|
|
func (state *State) PrivateKey() ed25519.PrivateKey {
|
|
return state.privateKey
|
|
}
|
|
|
|
func (state *State) PublicKey() ed25519.PublicKey {
|
|
return state.publicKey
|
|
}
|
|
|
|
func (state *State) UrlPath() string {
|
|
return state.urlPath
|
|
}
|
|
|
|
func (state *State) ChallengeFailed(r *http.Request, reg *challenge.Registration, err error, redirect string, logger *slog.Logger) {
|
|
if logger == nil {
|
|
logger = state.Logger(r)
|
|
}
|
|
logger.Warn("challenge failed", "challenge", reg.Name, "err", err, "redirect", redirect)
|
|
|
|
//TODO: metrics
|
|
}
|
|
|
|
func (state *State) ChallengePassed(r *http.Request, reg *challenge.Registration, redirect string, logger *slog.Logger) {
|
|
if logger == nil {
|
|
logger = state.Logger(r)
|
|
}
|
|
logger.Warn("challenge passed", "challenge", reg.Name, "redirect", redirect)
|
|
|
|
//TODO: metrics
|
|
}
|
|
|
|
func (state *State) ChallengeIssued(r *http.Request, reg *challenge.Registration, redirect string, logger *slog.Logger) {
|
|
if logger == nil {
|
|
logger = state.Logger(r)
|
|
}
|
|
logger.Info("challenge issued", "challenge", reg.Name, "redirect", redirect)
|
|
|
|
//TODO: metrics
|
|
}
|
|
|
|
func (state *State) Logger(r *http.Request) *slog.Logger {
|
|
return GetLoggerForRequest(r)
|
|
}
|
|
|
|
func (state *State) ChallengePage(w http.ResponseWriter, r *http.Request, status int, reg *challenge.Registration, params map[string]any) {
|
|
data := challenge.RequestDataFromContext(r.Context())
|
|
input := make(map[string]any)
|
|
input["Id"] = data.Id.String()
|
|
input["Random"] = utils.CacheBust()
|
|
if reg != nil {
|
|
input["Challenge"] = reg.Name
|
|
input["Path"] = state.UrlPath()
|
|
}
|
|
input["Theme"] = state.Settings().ChallengeTemplateTheme
|
|
|
|
maps.Copy(input, params)
|
|
|
|
if _, ok := input["Title"]; !ok {
|
|
input["Title"] = "Checking you are not a bot"
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
|
buf := bytes.NewBuffer(make([]byte, 0, 8192))
|
|
|
|
err := templates["challenge-"+state.Settings().ChallengeTemplate+".gohtml"].Execute(buf, input)
|
|
if err != nil {
|
|
state.ErrorPage(w, r, http.StatusInternalServerError, err, "")
|
|
} else {
|
|
w.WriteHeader(status)
|
|
_, _ = w.Write(buf.Bytes())
|
|
}
|
|
}
|
|
|
|
func (state *State) ErrorPage(w http.ResponseWriter, r *http.Request, status int, err error, redirect string) {
|
|
data := challenge.RequestDataFromContext(r.Context())
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
|
buf := bytes.NewBuffer(make([]byte, 0, 8192))
|
|
|
|
err2 := templates["challenge-"+state.Settings().ChallengeTemplate+".gohtml"].Execute(buf, map[string]any{
|
|
"Id": data.Id.String(),
|
|
"Random": utils.CacheBust(),
|
|
"Error": err.Error(),
|
|
"Path": state.UrlPath(),
|
|
"Theme": state.Settings().ChallengeTemplateTheme,
|
|
"Title": "Oh no! " + http.StatusText(status),
|
|
"HideSpinner": true,
|
|
"Challenge": "",
|
|
"Redirect": redirect,
|
|
})
|
|
if err2 != nil {
|
|
// nested errors!
|
|
panic(err2)
|
|
} else {
|
|
w.WriteHeader(status)
|
|
_, _ = w.Write(buf.Bytes())
|
|
}
|
|
}
|
|
|
|
func (state *State) GetChallenge(id challenge.Id) (*challenge.Registration, bool) {
|
|
reg, ok := state.challenges.Get(id)
|
|
return reg, ok
|
|
}
|
|
|
|
func (state *State) GetChallenges() challenge.Register {
|
|
return state.challenges
|
|
}
|
|
|
|
func (state *State) GetChallengeByName(name string) (*challenge.Registration, bool) {
|
|
reg, _, ok := state.challenges.GetByName(name)
|
|
return reg, ok
|
|
}
|
|
func (state *State) Settings() policy.Settings {
|
|
return state.settings
|
|
}
|
|
|
|
func (state *State) GetBackend(host string) http.Handler {
|
|
return utils.SelectHTTPHandler(state.Settings().Backends, host)
|
|
}
|