inteface.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package main
  2. import (
  3. "crypto/rand"
  4. "encoding/hex"
  5. "fmt"
  6. "github.com/gorilla/sessions"
  7. "github.com/labstack/echo"
  8. "github.com/labstack/echo-contrib/session"
  9. "github.com/labstack/echo/middleware"
  10. "net/http"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "time"
  15. )
  16. type userConfig struct {
  17. Admins []string `json:"admins"`
  18. Boards []configBoard `json:"boards"`
  19. }
  20. func genConfig() *userConfig {
  21. u := &userConfig{
  22. make([]string, 0, len(config.Admins)),
  23. make([]configBoard, 0, len(config.Boards)),
  24. }
  25. for k := range config.Admins { u.Admins = append(u.Admins, k) }
  26. for b := range config.Boards { u.Boards = append(u.Boards, config.Boards[b]) }
  27. return u
  28. }
  29. func serveIndex(c echo.Context) error {
  30. return c.File("server/templates/head.html")
  31. }
  32. func serveLogin(c echo.Context) error {
  33. return c.File("server/templates/login.html")
  34. }
  35. func serveInterface(address string) {
  36. e := echo.New()
  37. e.Use(middleware.GzipWithConfig(middleware.GzipConfig{Level: 5}))
  38. e.Use(session.Middleware(sessions.NewCookieStore([]byte(config.Secret))))
  39. e.GET("/", func(c echo.Context) error {
  40. sess, _ := session.Get("session", c)
  41. if auth, ok := sess.Values["authorised"]; ok && auth.(bool) {
  42. return c.Redirect(http.StatusTemporaryRedirect, "/admin")
  43. }
  44. return c.Redirect(http.StatusTemporaryRedirect, "/login")
  45. })
  46. e.GET("/login", serveLogin)
  47. g := e.Group("/admin")
  48. g.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
  49. return func(c echo.Context) error {
  50. sess, _ := session.Get("session", c)
  51. auth, ok := sess.Values["authorised"]
  52. if !ok || !auth.(bool) {
  53. fmt.Println("Not authorised")
  54. return c.Redirect(http.StatusTemporaryRedirect, "/login")
  55. }
  56. fmt.Println("Authorised")
  57. return next(c)
  58. }
  59. })
  60. g.GET("", serveIndex)
  61. g.POST("", serveIndex)
  62. g.POST("/user", func(c echo.Context) error {
  63. sess, _ := session.Get("session", c)
  64. name := strings.ToLower(c.FormValue("username"))
  65. matched, _ := regexp.MatchString(`\w`, name)
  66. if !matched {
  67. return c.String(http.StatusNotAcceptable, "Invalid username. Allowed symbols: a-z 0-9 _")
  68. }
  69. pass := c.FormValue("password")
  70. if len(name) == 0 {
  71. return c.String(http.StatusNotAcceptable, "No username")
  72. }
  73. admin, exist := config.Admins[name]
  74. if exist {
  75. if len(pass) > 0 {
  76. if sess.Values["username"] != name {
  77. return c.String(http.StatusNotAcceptable, "Can't change other user passwords")
  78. }
  79. err := admin.newPassword(pass)
  80. if err != nil {
  81. fmt.Printf("Failed hash password %s\n", err)
  82. return c.String(http.StatusNotAcceptable, "Failed to hash")
  83. }
  84. }
  85. } else {
  86. if len(pass) == 0 {
  87. return c.String(http.StatusNotAcceptable, "No password")
  88. }
  89. admin := configAdmin{name, nil}
  90. err := admin.newPassword(pass)
  91. if err != nil {
  92. fmt.Printf("Failed hash password %s\n", err)
  93. return c.String(http.StatusNotAcceptable, "Failed to hash")
  94. }
  95. config.Admins[name] = admin
  96. }
  97. saveConfig()
  98. return c.JSON(http.StatusOK, genConfig())
  99. })
  100. g.POST("/board", func(c echo.Context) error {
  101. id64, err := strconv.ParseUint(c.FormValue("id"), 10, 16)
  102. if err != nil {
  103. return c.String(http.StatusNotAcceptable, "Invalid ID")
  104. }
  105. id := uint16(id64)
  106. keysize, err := strconv.ParseUint(c.FormValue("keysize"), 10, 16)
  107. if err != nil || (keysize != 128 && keysize != 192 && keysize != 256) {
  108. return c.String(http.StatusNotAcceptable, "Invalid key size")
  109. }
  110. key, err := hex.DecodeString(c.FormValue("key"))
  111. if err != nil {
  112. return c.String(http.StatusNotAcceptable, "Key is not base 16")
  113. }
  114. if len(key) != int(keysize/8) && len(key) != 0 {
  115. return c.String(http.StatusNotAcceptable, "Key does not match key size")
  116. }
  117. if board, exist := config.Boards[id]; exist {
  118. // Update settings
  119. if len(key) > 0 {
  120. board.KEY = key
  121. board.KEY16 = hex.EncodeToString(key)
  122. }
  123. } else {
  124. // New board
  125. if len(key) == 0 {
  126. key = make([]byte, keysize/8)
  127. _, err = rand.Read(key)
  128. errCheck(err, "Failed generate random key")
  129. }
  130. config.Boards[id] = configBoard{
  131. id,
  132. key,
  133. hex.EncodeToString(key),
  134. time.Unix(0, 0),
  135. }
  136. }
  137. saveConfig()
  138. return c.JSON(http.StatusOK, genConfig())
  139. })
  140. g.DELETE("/user/:name", func(c echo.Context) error {
  141. sess, _ := session.Get("session", c)
  142. if sess.Values["username"] == c.Param("name") {
  143. return c.String(http.StatusNotAcceptable, "Can't delete yourself")
  144. }
  145. _, ok := config.Admins[c.Param("name")]
  146. if !ok {
  147. return c.String(http.StatusNotFound, "Not found")
  148. }
  149. delete(config.Admins, c.Param("name"))
  150. saveConfig()
  151. return c.JSON(http.StatusOK, genConfig())
  152. })
  153. g.DELETE("/board/:id", func(c echo.Context) error {
  154. id64, err := strconv.ParseUint(c.Param("id"), 10, 16)
  155. if err != nil {
  156. return c.String(http.StatusNotAcceptable, "Invalid ID")
  157. }
  158. id := uint16(id64)
  159. _, ok := config.Boards[id]
  160. if !ok {
  161. return c.String(http.StatusNotFound, "Not found")
  162. }
  163. delete(config.Boards, id)
  164. saveConfig()
  165. return c.JSON(http.StatusOK, genConfig())
  166. })
  167. e.POST("/login", func(c echo.Context) error {
  168. username := c.FormValue("username")
  169. password := c.FormValue("password")
  170. user, ok := config.Admins[username]
  171. if !ok || !user.checkPassword(password) {
  172. return c.File("server/templates/login.html")
  173. }
  174. sess, _ := session.Get("session", c)
  175. sess.Options = &sessions.Options{
  176. Path: "/",
  177. MaxAge: 86400 * 7,
  178. HttpOnly: true,
  179. }
  180. sess.Values["username"] = user.Username
  181. sess.Values["authorised"] = true
  182. err := sess.Save(c.Request(), c.Response())
  183. errCheck(err, "Failed to save session")
  184. return c.Redirect(http.StatusTemporaryRedirect, "/admin")
  185. })
  186. g.GET("/logout", func(c echo.Context) error {
  187. sess, _ := session.Get("session", c)
  188. sess.Values["authorised"] = false
  189. err := sess.Save(c.Request(), c.Response())
  190. errCheck(err, "Failed to save session")
  191. return c.Redirect(http.StatusTemporaryRedirect, "/login")
  192. })
  193. //g.Use(middleware.BasicAuth()
  194. g.GET("/config", func(c echo.Context) error {
  195. return c.JSON(http.StatusOK, genConfig())
  196. })
  197. e.Logger.Fatal(e.Start(address))
  198. }