// Copyright (C) 2018 Marius Schellenberger package server import ( "git.giftfish.de/ston1th/gowiki/pkg/core" "git.giftfish.de/ston1th/gowiki/pkg/log" "git.giftfish.de/ston1th/gowiki/pkg/render" "git.giftfish.de/ston1th/jwt/v3" "net/http" "time" ) type notFoundHandler struct { s *HTTPServer } func (nf *notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { newContext(w, r, nf.s).NotFound() } func adminAuthHandler(h ctxHandler) ctxHandler { return func(ctx *Context) { if ctx.Admin() { h(ctx) return } ctx.NotFound() } } func userAuthHandler(h ctxHandler) ctxHandler { return func(ctx *Context) { if ctx.Admin() { h(ctx) return } if ctx.User() == ctx.Var("user") { h(ctx) return } ctx.NotFound() } } func authHandler(h ctxHandler) ctxHandler { return func(ctx *Context) { if ctx.LoggedOn() { h(ctx) return } ctx.NotFound() } } func staticHandler(ctx *Context) { var err error ctx.SetHeader("Content-Type", "text/css; charset=utf-8") ctx.SetHeader("Expires", time.Now().Add(time.Hour*12).Format(http.TimeFormat)) switch ctx.Path() { case "/bootstrap.css": err = ctx.Write(ctx.Srv.res["bootstrap.css"]) case "/custom.css": err = ctx.Write(ctx.Srv.res["custom.css"]) } if err != nil { log.Println("static:", err) } } func indexHandler(ctx *Context) { ctx.Redirect(core.IndexURI, 302) } func loginHandler(ctx *Context) { if ctx.LoggedOn() { ctx.Redirect(core.IndexURI, 302) return } ctx.Template("loginHandler") ctx.Data = webData{ Title: "Login", BodyTitle: "Login", } switch ctx.Method() { case "GET": ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } user := ctx.Form("user") password := ctx.Form("password") if user == "" || password == "" { ctx.Error("wrong inputs") return } u, err := ctx.Srv.DB.Login(user, password) if err != nil { ctx.Error(err) return } ctx.SetCookie(jwt.Claims{userClaim: u.Username, createdClaim: u.Created, adminClaim: u.Admin}) ctx.Redirect(core.IndexURI, 302) } } func searchHandler(ctx *Context) { ctx.Template("searchHandler") ctx.Data = webData{ Title: "Search", BodyTitle: "Search", } switch ctx.Method() { case "GET": ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } search := ctx.Form("search") res, err := ctx.Srv.DB.Index.Search(search) if err != nil { ctx.Error(err) return } ctx.Data.Data = res ctx.Data.Search = search ctx.Exec() } } func allHandler(ctx *Context) { ctx.Template("allHandler") ctx.Data = webData{ Title: "All Pages", BodyTitle: "All Pages", Admin: ctx.Admin(), } ctx.Data.Data = ctx.Srv.DB.GetAllPages(ctx.User()) ctx.Exec() } func pageNewHandler(ctx *Context) { ctx.Template("pageNewHandler") ctx.Data = webData{Title: "New Page"} switch ctx.Method() { case "GET": ctx.Data.Data = core.Page{Perm: core.Public} ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } section := ctx.Form("section") title := ctx.Form("title") markdown := ctx.Form("markdown") perm := core.ParsePermString(ctx.Form("perm")) p := core.Page{ Title: title, Markdown: markdown, Perm: perm, } ctx.Data.Data = p if section != core.WikiSection { section = ctx.User() } page, err := ctx.Srv.DB.CreatePage(title, section, markdown, ctx.User(), perm) if err != nil { ctx.Error(err) return } ctx.Redirect(page.StoreTitle, 302) } } func pageHandler(ctx *Context) { ctx.Data = webData{ Admin: ctx.Admin(), } section := ctx.Var("section") title := ctx.Var("title") user := ctx.User() if key := ctx.Var("key"); key != "" { t, err := jwt.DecodeToken(key) if err != nil { log.Println("DecodeToken:", err) ctx.NotFound() return } if err = ctx.Srv.JWT.Verify(t); err != nil { log.Println("VerifyToken:", err) ctx.NotFound() return } user = t.Claims.GetString(sharedClaim) if ctx.Srv.DB.LockedOut(user, t.Claims.GetString(createdClaim)) { if err = ctx.Srv.JWT.Invalidate(t); err != nil { log.Println("Invalidate:", err) } ctx.NotFound() return } section = t.Claims.GetString(sectionClaim) title = t.Claims.GetString(titleClaim) ctx.Data.Key = key if ctx.T == nil { ctx.Template("pageViewHandler") } } else { if ctx.T == nil { ctx.Template("pageHandler") } } page, err := ctx.Srv.DB.GetPage(section, title, user) if err != nil { log.Println(err) ctx.NotFound() return } ctx.Data.Data = page ctx.Data.Title = page.Title ctx.Data.BodyTitle = page.Title ctx.Exec() } func pageMDHandler(f ctxHandler) ctxHandler { return func(ctx *Context) { ctx.Template("pageMDHandler") f(ctx) } } func pageShareHandler(ctx *Context) { ctx.Template("pageShareHandler") ctx.Data = webData{} section := ctx.Var("section") title := ctx.Var("title") user := ctx.User() page, err := ctx.Srv.DB.GetPage(section, title, user) if err != nil { ctx.NotFound() return } if page.Perm != core.Private { ctx.Error("only private pages can be shared") return } ctx.Data.Data = page.StoreTitle ctx.Data.Title = page.Title ctx.Data.BodyTitle = page.Title switch ctx.Method() { case "GET": ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } d, err := time.ParseDuration(ctx.Form("duration")) if err != nil { ctx.Error(err) return } t := jwt.NewToken(map[string]interface{}{ sharedClaim: user, createdClaim: ctx.Token.Claims.GetString(createdClaim), sectionClaim: section, titleClaim: title, jwt.ExpClaim: jwt.NewExp(d), }, nil) if err = ctx.Srv.JWT.Sign(t); err != nil { log.Println("SignToken:", err) ctx.NotFound() return } ctx.Data.Key = t.String() ctx.Exec() } } func pageBlacklistHandler(ctx *Context) { ctx.Template("pageBlacklistHandler") ctx.Data = webData{ Title: "Blacklist", BodyTitle: "Blacklist", } switch ctx.Method() { case "GET": ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } t, err := jwt.DecodeToken(ctx.Form("share")) if err != nil { ctx.Error(err) return } err = ctx.Srv.JWT.Verify(t) if err != nil { ctx.Error(err) return } user := t.Claims.GetString(sharedClaim) if user == "" { ctx.Error("blacklisting denied: no share token") return } if ctx.User() != user { ctx.Error("blacklisting denied: token was issued by " + user) return } err = ctx.Srv.JWT.Invalidate(t) if err != nil { ctx.Error(err) return } ctx.Exec() } } func pageEditHandler(ctx *Context) { ctx.Template("pageEditHandler") ctx.Data = webData{} section := ctx.Var("section") title := ctx.Var("title") page, err := ctx.Srv.DB.GetPage(section, title, ctx.User()) if err != nil { ctx.NotFound() return } ctx.Data.Title = page.Title ctx.Data.BodyTitle = page.Title switch ctx.Method() { case "GET": ctx.Data.Data = page ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } page.Markdown = ctx.Form("markdown") page.Perm = core.ParsePermString(ctx.Form("perm")) ctx.Data.Data = page err = ctx.Srv.DB.UpdatePage(title, section, page.Markdown, ctx.User(), page.Perm) if err != nil { ctx.Error(err) return } ctx.Redirect(page.StoreTitle, 302) } } func pageDelHandler(ctx *Context) { section := ctx.Var("section") title := ctx.Var("title") ctx.Template("pageDelHandler") ctx.Data.Data = render.StoreTitle(section, title) ctx.Data.Title = title ctx.Data.BodyTitle = title switch ctx.Method() { case "GET": ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } if err := ctx.Srv.DB.DeletePage(title, section, ctx.User()); err != nil { ctx.Error(err) return } ctx.Redirect(core.IndexURI, 302) } } func userHandler(ctx *Context) { if !ctx.LoggedOn() { ctx.NotFound() return } ctx.Template("userHandler") ctx.Data = webData{ Title: "Users", BodyTitle: "Users", User: ctx.User(), } us, err := ctx.Srv.DB.GetUsers() if err != nil { ctx.Data.Msg = err.Error() } ctx.Data.Data = us ctx.Exec() } func userNewHandler(ctx *Context) { ctx.Template("userNewHandler") ctx.Data = webData{ Title: "New User", BodyTitle: "New User", } switch ctx.Method() { case "GET": ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } user := ctx.Form("user") password := ctx.Form("password") repeat := ctx.Form("repeat") admin := ctx.Form("admin") adm := false if admin == "0" { adm = true } if user == "" || password == "" || password != repeat { ctx.Error("wrong inputs") return } if err := ctx.Srv.DB.CreateUser(user, password, adm); err != nil { ctx.Error(err) return } ctx.Redirect("/user", 302) } } func userEditHandler(ctx *Context) { ctx.Template("userEditHandler") ctx.Data = webData{ Title: "Edit User", BodyTitle: "Edit User", } switch ctx.Method() { case "GET": u, err := ctx.Srv.DB.GetUserWithoutPassword(ctx.Var("user")) if err != nil { ctx.Error(err) return } ctx.Data.Data = u ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } user := ctx.Var("user") password := ctx.Form("password") repeat := ctx.Form("repeat") admin := ctx.Form("admin") adm := false if admin == "0" { adm = true } if user == "" || password != repeat { ctx.Error("wrong inputs") return } if ctx.Admin() { if err := ctx.Srv.DB.AdminUpdateUser(user, password, adm); err != nil { ctx.Error(err) return } } else { if user == ctx.User() { if err := ctx.Srv.DB.UpdateUserPassword(user, password); err != nil { ctx.Error(err) return } ctx.Redirect(core.IndexURI, 302) return } } ctx.Redirect("/user", 302) } } func userUnlockHandler(ctx *Context) { user := ctx.Var("user") if err := ctx.Srv.DB.UnlockUser(user); err != nil { log.Println("unlock:", err) } ctx.Redirect("/user", 302) } func userDelHandler(ctx *Context) { ctx.Template("userDelHandler") ctx.Data = webData{ Title: "Delete User", BodyTitle: "Delete User", } switch ctx.Method() { case "GET": user, err := ctx.Srv.DB.GetUserWithoutPassword(ctx.Var("user")) if err != nil { ctx.Error(err) return } ctx.Data.Data = user ctx.Exec() case "POST": if !ctx.CheckXsrf() { return } if err := ctx.Srv.DB.DeleteUser(ctx.Var("user")); err != nil { log.Println("delete:", err) } ctx.Redirect("/user", 302) } } func logoutHandler(ctx *Context) { ctx.Srv.JWT.Invalidate(&ctx.Token) ctx.SetCookie(nil) ctx.Redirect(core.IndexURI, 302) }