gowiki/pkg/server/context.go
2018-09-12 00:37:13 +02:00

209 lines
4.2 KiB
Go

// Copyright (C) 2017 Marius Schellenberger
package server
import (
"errors"
"git.giftfish.de/ston1th/gowiki/pkg/log"
"git.giftfish.de/ston1th/jwt"
"github.com/gorilla/mux"
"html/template"
"net/http"
"time"
)
func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) (ctx *Context) {
h := w.Header()
h.Set("X-Frame-Options", "DENY")
h.Set("X-Content-Type-Options", "nosniff")
h.Set("X-XSS-Protection", "1; mode=block")
h.Set("Content-Security-Policy", "default-src 'none';style-src 'self' 'unsafe-inline';frame-ancestors 'none'")
ctx = &Context{
Request: r,
Response: w,
Srv: s,
Time: time.Now(),
}
if c, err := r.Cookie(cookieName); err == nil {
t, err := jwt.DecodeToken(c.Value)
if err != nil {
log.Println("DecodeToken:", err)
} else {
if err = s.JWT.Verify(t); err != nil {
log.Println("VerifyToken:", err)
} else {
ctx.Token = *t
return
}
}
}
ctx.SetCookie(nil)
return
}
type Context struct {
Request *http.Request
Response http.ResponseWriter
Srv *HTTPServer
T *template.Template
Status int
Err error
Time time.Time
Token jwt.Token
Data webData
}
type webData struct {
Version string
Time int64
Title string
BodyTitle string
Admin bool
Login bool
User string
Token string
Msg string
Search string
Data interface{}
}
func (c *Context) Exec() {
defer c.log()
if c.T == nil {
c.Status = http.StatusInternalServerError
c.Err = errors.New("template is nil")
return
}
c.Data.Token = newXsrf(c.Token.RawSig()[:keySize])
c.Data.Version = c.Srv.Version
if c.Data.BodyTitle == "" {
c.Data.BodyTitle = c.Data.Title
}
c.Data.Admin = c.GetAdmin()
c.Data.User = c.GetUser()
c.Data.Login = c.LoggedOn()
c.Data.Time = time.Since(c.Time).Nanoseconds() / 1e6
c.Err = c.T.Execute(c.Response, c.Data)
if c.Err != nil {
c.Status = http.StatusInternalServerError
}
if c.Status == 0 {
c.Status = http.StatusOK
}
}
func (c *Context) log() {
if c.Err != nil {
log.Printf("%s %s %s %d error: %s\n", c.Request.RemoteAddr, c.Request.Method, c.Request.URL, c.Status, c.Err)
return
}
log.Printf("%s %s %s %d\n", c.Request.RemoteAddr, c.Request.Method, c.Request.URL, c.Status)
}
func (c *Context) NotFound() {
c.Template("notFoundHandler")
c.Data = webData{Title: "404"}
c.Status = http.StatusNotFound
c.Response.WriteHeader(http.StatusNotFound)
c.Exec()
}
func (c *Context) Template(name string) {
c.T = c.Srv.templ[name]
}
func (c *Context) Write(buf []byte) (err error) {
_, err = c.Response.Write(buf)
return
}
func (c *Context) SetHeader(name, value string) {
c.Response.Header().Set(name, value)
}
func (c *Context) Redirect(uri string, code int) {
log.Printf("%s %s %s %d %s\n", c.Request.RemoteAddr, c.Request.Method, c.Request.URL, code, uri)
http.Redirect(c.Response, c.Request, uri, code)
}
func (c *Context) Method() string {
return c.Request.Method
}
func (c *Context) Path() string {
return c.Request.URL.Path
}
func (c *Context) FormSlice(name string) []string {
// does nothing if called twice
c.Request.ParseForm()
return c.Request.PostForm[name]
}
func (c *Context) Form(name string) (ret string) {
ret = c.Request.PostFormValue(name)
return
}
func (c *Context) Var(name string) (ret string) {
ret, _ = mux.Vars(c.Request)[name]
return
}
// CheckToken validates the xsrf token
func (c *Context) CheckXsrf(f func()) (ok bool) {
ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize])
if !ok {
if f != nil {
f()
return
}
c.Data.Msg = "wrong csrf token"
c.Exec()
}
return
}
func (c *Context) LoggedOn() (ok bool) {
_, ok = c.Token.Claims["user"]
return
}
func (c *Context) SetCookie(claims map[string]interface{}) {
t := jwt.NewToken(claims, nil)
if err := c.Srv.JWT.Sign(t); err != nil {
log.Println(err)
return
}
c.Token = *t
http.SetCookie(c.Response, &http.Cookie{
Name: cookieName,
Value: t.Sig(),
MaxAge: 3600 * 12,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteStrictMode,
})
}
func (c *Context) GetUser() string {
if ret, ok := c.Token.Claims["user"].(string); ok {
return ret
}
return ""
}
func (c *Context) GetAdmin() bool {
if ret, ok := c.Token.Claims["admin"].(bool); ok {
return ret
}
return false
}
type ctxHandler func(*Context)