feat: csrf

This commit is contained in:
joerdav 2023-05-02 14:52:43 +01:00
parent e6b99f5420
commit fe6fe1f9b3
No known key found for this signature in database
GPG Key ID: 7E93835EA5290C52
6 changed files with 812 additions and 1 deletions

56
csrf/handlers.go Normal file
View File

@ -0,0 +1,56 @@
package csrf
import (
"log"
"net/http"
"github.com/justinas/nosurf"
)
type user struct {
firstName, lastName, email string
}
var demoUser user = user{
firstName: "Joe",
lastName: "Blow",
email: "joe@blow.com",
}
func Handlers(prefix string, mux *http.ServeMux) {
mux.HandleFunc(prefix+"/", index)
mux.HandleFunc(prefix, index)
mux.Handle(prefix+"/contact/1", nosurf.New(http.HandlerFunc(putUser)))
mux.Handle(prefix+"/contact/1/edit", nosurf.New(http.HandlerFunc(editForm)))
}
func index(w http.ResponseWriter, r *http.Request) {
// Load user
user := demoUser
if r.Header.Get("HX-Request") == "true" {
Display(user).Render(r.Context(), w)
return
}
Index(user).Render(r.Context(), w)
}
func editForm(w http.ResponseWriter, r *http.Request) {
// Load user
user := demoUser
Form(user, nosurf.Token(r)).Render(r.Context(), w)
}
func putUser(w http.ResponseWriter, r *http.Request) {
// Load user
if err := r.ParseForm(); err != nil {
log.Println(err)
w.WriteHeader(500)
return
}
demoUser = user{
firstName: r.FormValue("firstName"),
lastName: r.FormValue("lastName"),
email: r.FormValue("email"),
}
Display(demoUser).Render(r.Context(), w)
}

42
csrf/templates.templ Normal file
View File

@ -0,0 +1,42 @@
package csrf
import (
"fmt"
"examples/shared"
)
templ Display(u user) {
<div hx-target="this" hx-swap="outerHTML">
<div><label>First Name</label>: { u.firstName }</div>
<div><label>Last Name</label>: { u.lastName }</div>
<div><label>Email</label>: { u.email }</div>
<button hx-get="/csrf/contact/1/edit" class="button is-black">Click To Edit</button>
</div>
}
templ Form(u user, csrfToken string) {
<form hx-put="/csrf/contact/1" hx-headers={ fmt.Sprintf(`{"X-CSRF-Token": %q}`, csrfToken) } hx-target="this" hx-swap="outerHTML">
<div class="field">
<div class="control"><label>First Name</label><input class="input" type="text" name="firstName" value={ u.firstName }/></div>
</div>
<div class="field">
<div class="control"><label>Last Name</label><input class="input" type="text" name="lastName" value={ u.lastName }/></div>
</div>
<div class="field">
<div class="control"><label>Email Address</label><input class="input" type="email" name="email" value={ u.email }/></div>
</div>
<div class="field is-grouped">
<div class="control"><button class="button is-black">Submit</button></div>
<div class="control"><button class="button" hx-get="/csrf">Cancel</button></div>
</div>
</form>
}
templ Index(u user) {
@shared.Layout("Click to Edit") {
<h2 class="title">Click to Edit</h2>
@Display(u)
}
}

701
csrf/templates_templ.go Normal file
View File

@ -0,0 +1,701 @@
// Code generated by templ@(devel) DO NOT EDIT.
package csrf
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
// GoExpression
import (
"fmt"
"examples/shared"
)
func Display(u user) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
templBuffer, templIsBuffer := w.(*bytes.Buffer)
if !templIsBuffer {
templBuffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templBuffer)
}
ctx = templ.InitializeContext(ctx)
var_1 := templ.GetChildren(ctx)
if var_1 == nil {
var_1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" hx-target=\"this\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" hx-swap=\"outerHTML\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<label>")
if err != nil {
return err
}
// Text
var_2 := `First Name`
_, err = templBuffer.WriteString(var_2)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</label>")
if err != nil {
return err
}
// Text
var_3 := `: `
_, err = templBuffer.WriteString(var_3)
if err != nil {
return err
}
// StringExpression
var var_4 string = u.firstName
_, err = templBuffer.WriteString(templ.EscapeString(var_4))
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<label>")
if err != nil {
return err
}
// Text
var_5 := `Last Name`
_, err = templBuffer.WriteString(var_5)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</label>")
if err != nil {
return err
}
// Text
var_6 := `: `
_, err = templBuffer.WriteString(var_6)
if err != nil {
return err
}
// StringExpression
var var_7 string = u.lastName
_, err = templBuffer.WriteString(templ.EscapeString(var_7))
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<label>")
if err != nil {
return err
}
// Text
var_8 := `Email`
_, err = templBuffer.WriteString(var_8)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</label>")
if err != nil {
return err
}
// Text
var_9 := `: `
_, err = templBuffer.WriteString(var_9)
if err != nil {
return err
}
// StringExpression
var var_10 string = u.email
_, err = templBuffer.WriteString(templ.EscapeString(var_10))
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<button")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" hx-get=\"/csrf/contact/1/edit\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" class=\"button is-black\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Text
var_11 := `Click To Edit`
_, err = templBuffer.WriteString(var_11)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</button>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
if !templIsBuffer {
_, err = io.Copy(w, templBuffer)
}
return err
})
}
func Form(u user, csrfToken string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
templBuffer, templIsBuffer := w.(*bytes.Buffer)
if !templIsBuffer {
templBuffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templBuffer)
}
ctx = templ.InitializeContext(ctx)
var_12 := templ.GetChildren(ctx)
if var_12 == nil {
var_12 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
// Element (standard)
_, err = templBuffer.WriteString("<form")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" hx-put=\"/csrf/contact/1\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" hx-headers=")
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(templ.EscapeString(fmt.Sprintf(`{"X-CSRF-Token": %q}`, csrfToken)))
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" hx-target=\"this\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" hx-swap=\"outerHTML\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"field\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"control\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<label>")
if err != nil {
return err
}
// Text
var_13 := `First Name`
_, err = templBuffer.WriteString(var_13)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</label>")
if err != nil {
return err
}
// Element (void)
_, err = templBuffer.WriteString("<input")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"input\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" type=\"text\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" name=\"firstName\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" value=")
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(templ.EscapeString(u.firstName))
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"field\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"control\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<label>")
if err != nil {
return err
}
// Text
var_14 := `Last Name`
_, err = templBuffer.WriteString(var_14)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</label>")
if err != nil {
return err
}
// Element (void)
_, err = templBuffer.WriteString("<input")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"input\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" type=\"text\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" name=\"lastName\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" value=")
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(templ.EscapeString(u.lastName))
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"field\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"control\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<label>")
if err != nil {
return err
}
// Text
var_15 := `Email Address`
_, err = templBuffer.WriteString(var_15)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</label>")
if err != nil {
return err
}
// Element (void)
_, err = templBuffer.WriteString("<input")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"input\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" type=\"email\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" name=\"email\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" value=")
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(templ.EscapeString(u.email))
if err != nil {
return err
}
_, err = templBuffer.WriteString("\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"field is-grouped\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"control\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<button")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"button is-black\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Text
var_16 := `Submit`
_, err = templBuffer.WriteString(var_16)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</button>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<div")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"control\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Element (standard)
_, err = templBuffer.WriteString("<button")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"button\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(" hx-get=\"/csrf\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Text
var_17 := `Cancel`
_, err = templBuffer.WriteString(var_17)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</button>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</div>")
if err != nil {
return err
}
_, err = templBuffer.WriteString("</form>")
if err != nil {
return err
}
if !templIsBuffer {
_, err = io.Copy(w, templBuffer)
}
return err
})
}
func Index(u user) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
templBuffer, templIsBuffer := w.(*bytes.Buffer)
if !templIsBuffer {
templBuffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templBuffer)
}
ctx = templ.InitializeContext(ctx)
var_18 := templ.GetChildren(ctx)
if var_18 == nil {
var_18 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
// TemplElement
var_19 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
templBuffer, templIsBuffer := w.(*bytes.Buffer)
if !templIsBuffer {
templBuffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templBuffer)
}
// Element (standard)
_, err = templBuffer.WriteString("<h2")
if err != nil {
return err
}
// Element Attributes
_, err = templBuffer.WriteString(" class=\"title\"")
if err != nil {
return err
}
_, err = templBuffer.WriteString(">")
if err != nil {
return err
}
// Text
var_20 := `Click to Edit`
_, err = templBuffer.WriteString(var_20)
if err != nil {
return err
}
_, err = templBuffer.WriteString("</h2>")
if err != nil {
return err
}
// Whitespace (normalised)
_, err = templBuffer.WriteString(` `)
if err != nil {
return err
}
// TemplElement
err = Display(u).Render(ctx, templBuffer)
if err != nil {
return err
}
if !templIsBuffer {
_, err = io.Copy(w, templBuffer)
}
return err
})
err = shared.Layout("Click to Edit").Render(templ.WithChildren(ctx, var_19), templBuffer)
if err != nil {
return err
}
if !templIsBuffer {
_, err = io.Copy(w, templBuffer)
}
return err
})
}

5
go.mod
View File

@ -9,4 +9,7 @@ require (
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4
)
require golang.org/x/net v0.9.0 // indirect
require (
github.com/justinas/nosurf v1.1.1 // indirect
golang.org/x/net v0.9.0 // indirect
)

2
go.sum
View File

@ -4,6 +4,8 @@ github.com/a-h/templ v0.2.233 h1:EnZqZmtV0YICqWG6MtLNmTcWuFkl2ImyQ63SIpWaM2Y=
github.com/a-h/templ v0.2.233/go.mod h1:h1DdzFMWVApvTcZBNmM6+mD6EPq6uYkncMNF7zdLj9I=
github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5 h1:NeF/iw7KU9W7CYYJimd5x7ooOXCLrfo8FcHdFPUU+2w=
github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5/go.mod h1:nqma2qb9ViAJOP4MBucyH+SPbOyNDZaRQyusfpK4PjY=
github.com/justinas/nosurf v1.1.1 h1:92Aw44hjSK4MxJeMSyDa7jwuI9GR2J/JCQiaKvXXSlk=
github.com/justinas/nosurf v1.1.1/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 h1:0sw0nJM544SpsihWx1bkXdYLQDlzRflMgFJQ4Yih9ts=

View File

@ -4,6 +4,7 @@ import (
"examples/bulkupdate"
"examples/clicktoedit"
"examples/clicktoload"
"examples/csrf"
"examples/deleterow"
"examples/editrow"
"examples/inlinevalidation"
@ -37,6 +38,12 @@ type Example struct {
}
var examples = []Example{
{
Name: "CSRF Protection",
Desc: "Demonstrates how to do CSRF Protection",
Slug: "csrf",
Handlers: csrf.Handlers,
},
{
Name: "Click To Edit",
Desc: "Demonstrates inline editing of a data object",