diff --git a/csrf/handlers.go b/csrf/handlers.go new file mode 100644 index 0000000..9a5ceb2 --- /dev/null +++ b/csrf/handlers.go @@ -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) +} diff --git a/csrf/templates.templ b/csrf/templates.templ new file mode 100644 index 0000000..18c2a11 --- /dev/null +++ b/csrf/templates.templ @@ -0,0 +1,42 @@ +package csrf + +import ( + "fmt" + + "examples/shared" +) + +templ Display(u user) { +
+
: { u.firstName }
+
: { u.lastName }
+
: { u.email }
+ +
+} + +templ Form(u user, csrfToken string) { +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+} + +templ Index(u user) { + @shared.Layout("Click to Edit") { +

Click to Edit

+ @Display(u) + } +} + diff --git a/csrf/templates_templ.go b/csrf/templates_templ.go new file mode 100644 index 0000000..9c5d4d5 --- /dev/null +++ b/csrf/templates_templ.go @@ -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("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("
") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + 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("
") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("
") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + 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("
") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("
") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + 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("
") + if err != nil { + return err + } + // Element (standard) + _, 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("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + 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("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, 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("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, 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("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + 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("") + if err != nil { + return err + } + // Text + var_20 := `Click to Edit` + _, err = templBuffer.WriteString(var_20) + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + 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 + }) +} + diff --git a/go.mod b/go.mod index c70bbe1..5bef7ea 100644 --- a/go.mod +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum index fb3442e..7fbd5b9 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/main.go b/main.go index cba1c90..017f8bc 100644 --- a/main.go +++ b/main.go @@ -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",