inteface.go 6.3 KB

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