A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 

623 righe
18 KiB

  1. package writefreely
  2. import (
  3. "fmt"
  4. "html/template"
  5. "net/http"
  6. "net/url"
  7. "runtime/debug"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/gorilla/sessions"
  12. "github.com/writeas/impart"
  13. "github.com/writeas/web-core/log"
  14. "github.com/writeas/writefreely/page"
  15. )
  16. type UserLevel int
  17. const (
  18. UserLevelNone UserLevel = iota // user or not -- ignored
  19. UserLevelOptional // user or not -- object fetched if user
  20. UserLevelNoneRequired // non-user (required)
  21. UserLevelUser // user (required)
  22. )
  23. type (
  24. handlerFunc func(app *app, w http.ResponseWriter, r *http.Request) error
  25. userHandlerFunc func(app *app, u *User, w http.ResponseWriter, r *http.Request) error
  26. dataHandlerFunc func(app *app, w http.ResponseWriter, r *http.Request) ([]byte, string, error)
  27. authFunc func(app *app, r *http.Request) (*User, error)
  28. )
  29. type Handler struct {
  30. errors *ErrorPages
  31. sessionStore *sessions.CookieStore
  32. app *app
  33. }
  34. // ErrorPages hold template HTML error pages for displaying errors to the user.
  35. // In each, there should be a defined template named "base".
  36. type ErrorPages struct {
  37. NotFound *template.Template
  38. Gone *template.Template
  39. InternalServerError *template.Template
  40. Blank *template.Template
  41. }
  42. // NewHandler returns a new Handler instance, using the given StaticPage data,
  43. // and saving alias to the application's CookieStore.
  44. func NewHandler(app *app) *Handler {
  45. h := &Handler{
  46. errors: &ErrorPages{
  47. NotFound: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>404</title></head><body><p>Not found.</p></body></html>{{end}}")),
  48. Gone: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>410</title></head><body><p>Gone.</p></body></html>{{end}}")),
  49. InternalServerError: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>500</title></head><body><p>Internal server error.</p></body></html>{{end}}")),
  50. Blank: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>{{.Title}}</title></head><body><p>{{.Content}}</p></body></html>{{end}}")),
  51. },
  52. sessionStore: app.sessionStore,
  53. app: app,
  54. }
  55. return h
  56. }
  57. // SetErrorPages sets the given set of ErrorPages as templates for any errors
  58. // that come up.
  59. func (h *Handler) SetErrorPages(e *ErrorPages) {
  60. h.errors = e
  61. }
  62. // User handles requests made in the web application by the authenticated user.
  63. // This provides user-friendly HTML pages and actions that work in the browser.
  64. func (h *Handler) User(f userHandlerFunc) http.HandlerFunc {
  65. return func(w http.ResponseWriter, r *http.Request) {
  66. h.handleHTTPError(w, r, func() error {
  67. var status int
  68. start := time.Now()
  69. defer func() {
  70. if e := recover(); e != nil {
  71. log.Error("%s: %s", e, debug.Stack())
  72. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  73. status = http.StatusInternalServerError
  74. }
  75. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  76. }()
  77. u := getUserSession(h.app, r)
  78. if u == nil {
  79. err := ErrNotLoggedIn
  80. status = err.Status
  81. return err
  82. }
  83. err := f(h.app, u, w, r)
  84. if err == nil {
  85. status = http.StatusOK
  86. } else if err, ok := err.(impart.HTTPError); ok {
  87. status = err.Status
  88. } else {
  89. status = http.StatusInternalServerError
  90. }
  91. return err
  92. }())
  93. }
  94. }
  95. // Admin handles requests on /admin routes
  96. func (h *Handler) Admin(f userHandlerFunc) http.HandlerFunc {
  97. return func(w http.ResponseWriter, r *http.Request) {
  98. h.handleHTTPError(w, r, func() error {
  99. var status int
  100. start := time.Now()
  101. defer func() {
  102. if e := recover(); e != nil {
  103. log.Error("%s: %s", e, debug.Stack())
  104. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  105. status = http.StatusInternalServerError
  106. }
  107. log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()))
  108. }()
  109. u := getUserSession(h.app, r)
  110. if u == nil || !u.IsAdmin() {
  111. err := impart.HTTPError{http.StatusNotFound, ""}
  112. status = err.Status
  113. return err
  114. }
  115. err := f(h.app, u, w, r)
  116. if err == nil {
  117. status = http.StatusOK
  118. } else if err, ok := err.(impart.HTTPError); ok {
  119. status = err.Status
  120. } else {
  121. status = http.StatusInternalServerError
  122. }
  123. return err
  124. }())
  125. }
  126. }
  127. // UserAPI handles requests made in the API by the authenticated user.
  128. // This provides user-friendly HTML pages and actions that work in the browser.
  129. func (h *Handler) UserAPI(f userHandlerFunc) http.HandlerFunc {
  130. return h.UserAll(false, f, func(app *app, r *http.Request) (*User, error) {
  131. // Authorize user from Authorization header
  132. t := r.Header.Get("Authorization")
  133. if t == "" {
  134. return nil, ErrNoAccessToken
  135. }
  136. u := &User{ID: app.db.GetUserID(t)}
  137. if u.ID == -1 {
  138. return nil, ErrBadAccessToken
  139. }
  140. return u, nil
  141. })
  142. }
  143. func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerFunc {
  144. return func(w http.ResponseWriter, r *http.Request) {
  145. handleFunc := func() error {
  146. var status int
  147. start := time.Now()
  148. defer func() {
  149. if e := recover(); e != nil {
  150. log.Error("%s: %s", e, debug.Stack())
  151. impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."})
  152. status = 500
  153. }
  154. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  155. }()
  156. u, err := a(h.app, r)
  157. if err != nil {
  158. if err, ok := err.(impart.HTTPError); ok {
  159. status = err.Status
  160. } else {
  161. status = 500
  162. }
  163. return err
  164. }
  165. err = f(h.app, u, w, r)
  166. if err == nil {
  167. status = 200
  168. } else if err, ok := err.(impart.HTTPError); ok {
  169. status = err.Status
  170. } else {
  171. status = 500
  172. }
  173. return err
  174. }
  175. if web {
  176. h.handleHTTPError(w, r, handleFunc())
  177. } else {
  178. h.handleError(w, r, handleFunc())
  179. }
  180. }
  181. }
  182. func (h *Handler) RedirectOnErr(f handlerFunc, loc string) handlerFunc {
  183. return func(app *app, w http.ResponseWriter, r *http.Request) error {
  184. err := f(app, w, r)
  185. if err != nil {
  186. if ie, ok := err.(impart.HTTPError); ok {
  187. // Override default redirect with returned error's, if it's a
  188. // redirect error.
  189. if ie.Status == http.StatusFound {
  190. return ie
  191. }
  192. }
  193. return impart.HTTPError{http.StatusFound, loc}
  194. }
  195. return nil
  196. }
  197. }
  198. func (h *Handler) Page(n string) http.HandlerFunc {
  199. return h.Web(func(app *app, w http.ResponseWriter, r *http.Request) error {
  200. t, ok := pages[n]
  201. if !ok {
  202. return impart.HTTPError{http.StatusNotFound, "Page not found."}
  203. }
  204. sp := pageForReq(app, r)
  205. err := t.ExecuteTemplate(w, "base", sp)
  206. if err != nil {
  207. log.Error("Unable to render page: %v", err)
  208. }
  209. return err
  210. }, UserLevelOptional)
  211. }
  212. func (h *Handler) WebErrors(f handlerFunc, ul UserLevel) http.HandlerFunc {
  213. return func(w http.ResponseWriter, r *http.Request) {
  214. // TODO: factor out this logic shared with Web()
  215. h.handleHTTPError(w, r, func() error {
  216. var status int
  217. start := time.Now()
  218. defer func() {
  219. if e := recover(); e != nil {
  220. u := getUserSession(h.app, r)
  221. username := "None"
  222. if u != nil {
  223. username = u.Username
  224. }
  225. log.Error("User: %s\n\n%s: %s", username, e, debug.Stack())
  226. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  227. status = 500
  228. }
  229. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  230. }()
  231. var session *sessions.Session
  232. var err error
  233. if ul != UserLevelNone {
  234. session, err = h.sessionStore.Get(r, cookieName)
  235. if err != nil && (ul == UserLevelNoneRequired || ul == UserLevelUser) {
  236. // Cookie is required, but we can ignore this error
  237. log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul, err)
  238. }
  239. _, gotUser := session.Values[cookieUserVal].(*User)
  240. if ul == UserLevelNoneRequired && gotUser {
  241. to := correctPageFromLoginAttempt(r)
  242. log.Info("Handler: Required NO user, but got one. Redirecting to %s", to)
  243. err := impart.HTTPError{http.StatusFound, to}
  244. status = err.Status
  245. return err
  246. } else if ul == UserLevelUser && !gotUser {
  247. log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.")
  248. err := ErrNotLoggedIn
  249. status = err.Status
  250. return err
  251. }
  252. }
  253. // TODO: pass User object to function
  254. err = f(h.app, w, r)
  255. if err == nil {
  256. status = 200
  257. } else if httpErr, ok := err.(impart.HTTPError); ok {
  258. status = httpErr.Status
  259. if status < 300 || status > 399 {
  260. addSessionFlash(h.app, w, r, httpErr.Message, session)
  261. return impart.HTTPError{http.StatusFound, r.Referer()}
  262. }
  263. } else {
  264. e := fmt.Sprintf("[Web handler] 500: %v", err)
  265. if !strings.HasSuffix(e, "write: broken pipe") {
  266. log.Error(e)
  267. } else {
  268. log.Error(e)
  269. }
  270. log.Info("Web handler internal error render")
  271. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  272. status = 500
  273. }
  274. return err
  275. }())
  276. }
  277. }
  278. // Web handles requests made in the web application. This provides user-
  279. // friendly HTML pages and actions that work in the browser.
  280. func (h *Handler) Web(f handlerFunc, ul UserLevel) http.HandlerFunc {
  281. return func(w http.ResponseWriter, r *http.Request) {
  282. h.handleHTTPError(w, r, func() error {
  283. var status int
  284. start := time.Now()
  285. defer func() {
  286. if e := recover(); e != nil {
  287. u := getUserSession(h.app, r)
  288. username := "None"
  289. if u != nil {
  290. username = u.Username
  291. }
  292. log.Error("User: %s\n\n%s: %s", username, e, debug.Stack())
  293. log.Info("Web deferred internal error render")
  294. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  295. status = 500
  296. }
  297. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  298. }()
  299. if ul != UserLevelNone {
  300. session, err := h.sessionStore.Get(r, cookieName)
  301. if err != nil && (ul == UserLevelNoneRequired || ul == UserLevelUser) {
  302. // Cookie is required, but we can ignore this error
  303. log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul, err)
  304. }
  305. _, gotUser := session.Values[cookieUserVal].(*User)
  306. if ul == UserLevelNoneRequired && gotUser {
  307. to := correctPageFromLoginAttempt(r)
  308. log.Info("Handler: Required NO user, but got one. Redirecting to %s", to)
  309. err := impart.HTTPError{http.StatusFound, to}
  310. status = err.Status
  311. return err
  312. } else if ul == UserLevelUser && !gotUser {
  313. log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.")
  314. err := ErrNotLoggedIn
  315. status = err.Status
  316. return err
  317. }
  318. }
  319. // TODO: pass User object to function
  320. err := f(h.app, w, r)
  321. if err == nil {
  322. status = 200
  323. } else if httpErr, ok := err.(impart.HTTPError); ok {
  324. status = httpErr.Status
  325. } else {
  326. e := fmt.Sprintf("[Web handler] 500: %v", err)
  327. log.Error(e)
  328. log.Info("Web internal error render")
  329. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  330. status = 500
  331. }
  332. return err
  333. }())
  334. }
  335. }
  336. func (h *Handler) All(f handlerFunc) http.HandlerFunc {
  337. return func(w http.ResponseWriter, r *http.Request) {
  338. h.handleError(w, r, func() error {
  339. // TODO: return correct "success" status
  340. status := 200
  341. start := time.Now()
  342. defer func() {
  343. if e := recover(); e != nil {
  344. log.Error("%s:\n%s", e, debug.Stack())
  345. impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."})
  346. status = 500
  347. }
  348. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  349. }()
  350. // TODO: do any needed authentication
  351. err := f(h.app, w, r)
  352. if err != nil {
  353. if err, ok := err.(impart.HTTPError); ok {
  354. status = err.Status
  355. } else {
  356. status = 500
  357. }
  358. }
  359. return err
  360. }())
  361. }
  362. }
  363. func (h *Handler) Download(f dataHandlerFunc, ul UserLevel) http.HandlerFunc {
  364. return func(w http.ResponseWriter, r *http.Request) {
  365. h.handleHTTPError(w, r, func() error {
  366. var status int
  367. start := time.Now()
  368. defer func() {
  369. if e := recover(); e != nil {
  370. log.Error("%s: %s", e, debug.Stack())
  371. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  372. status = 500
  373. }
  374. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  375. }()
  376. data, filename, err := f(h.app, w, r)
  377. if err != nil {
  378. if err, ok := err.(impart.HTTPError); ok {
  379. status = err.Status
  380. } else {
  381. status = 500
  382. }
  383. return err
  384. }
  385. ext := ".json"
  386. ct := "application/json"
  387. if strings.HasSuffix(r.URL.Path, ".csv") {
  388. ext = ".csv"
  389. ct = "text/csv"
  390. } else if strings.HasSuffix(r.URL.Path, ".zip") {
  391. ext = ".zip"
  392. ct = "application/zip"
  393. }
  394. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s%s", filename, ext))
  395. w.Header().Set("Content-Type", ct)
  396. w.Header().Set("Content-Length", strconv.Itoa(len(data)))
  397. fmt.Fprint(w, string(data))
  398. status = 200
  399. return nil
  400. }())
  401. }
  402. }
  403. func (h *Handler) Redirect(url string, ul UserLevel) http.HandlerFunc {
  404. return func(w http.ResponseWriter, r *http.Request) {
  405. h.handleHTTPError(w, r, func() error {
  406. start := time.Now()
  407. var status int
  408. if ul != UserLevelNone {
  409. session, err := h.sessionStore.Get(r, cookieName)
  410. if err != nil && (ul == UserLevelNoneRequired || ul == UserLevelUser) {
  411. // Cookie is required, but we can ignore this error
  412. log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul, err)
  413. }
  414. _, gotUser := session.Values[cookieUserVal].(*User)
  415. if ul == UserLevelNoneRequired && gotUser {
  416. to := correctPageFromLoginAttempt(r)
  417. log.Info("Handler: Required NO user, but got one. Redirecting to %s", to)
  418. err := impart.HTTPError{http.StatusFound, to}
  419. status = err.Status
  420. return err
  421. } else if ul == UserLevelUser && !gotUser {
  422. log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.")
  423. err := ErrNotLoggedIn
  424. status = err.Status
  425. return err
  426. }
  427. }
  428. status = sendRedirect(w, http.StatusFound, url)
  429. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  430. return nil
  431. }())
  432. }
  433. }
  434. func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err error) {
  435. if err == nil {
  436. return
  437. }
  438. if err, ok := err.(impart.HTTPError); ok {
  439. if err.Status >= 300 && err.Status < 400 {
  440. sendRedirect(w, err.Status, err.Message)
  441. return
  442. } else if err.Status == http.StatusUnauthorized {
  443. q := ""
  444. if r.URL.RawQuery != "" {
  445. q = url.QueryEscape("?" + r.URL.RawQuery)
  446. }
  447. sendRedirect(w, http.StatusFound, "/login?to="+r.URL.Path+q)
  448. return
  449. } else if err.Status == http.StatusGone {
  450. p := &struct {
  451. page.StaticPage
  452. Content *template.HTML
  453. }{
  454. StaticPage: pageForReq(h.app, r),
  455. }
  456. if err.Message != "" {
  457. co := template.HTML(err.Message)
  458. p.Content = &co
  459. }
  460. h.errors.Gone.ExecuteTemplate(w, "base", p)
  461. return
  462. } else if err.Status == http.StatusNotFound {
  463. h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  464. return
  465. } else if err.Status == http.StatusInternalServerError {
  466. log.Info("handleHTTPErorr internal error render")
  467. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  468. return
  469. } else if err.Status == http.StatusAccepted {
  470. impart.WriteSuccess(w, "", err.Status)
  471. return
  472. } else {
  473. p := &struct {
  474. page.StaticPage
  475. Title string
  476. Content template.HTML
  477. }{
  478. pageForReq(h.app, r),
  479. fmt.Sprintf("Uh oh (%d)", err.Status),
  480. template.HTML(fmt.Sprintf("<p style=\"text-align: center\" class=\"introduction\">%s</p>", err.Message)),
  481. }
  482. h.errors.Blank.ExecuteTemplate(w, "base", p)
  483. return
  484. }
  485. impart.WriteError(w, err)
  486. return
  487. }
  488. impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."})
  489. }
  490. func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) {
  491. if err == nil {
  492. return
  493. }
  494. if err, ok := err.(impart.HTTPError); ok {
  495. if err.Status >= 300 && err.Status < 400 {
  496. sendRedirect(w, err.Status, err.Message)
  497. return
  498. }
  499. // if strings.Contains(r.Header.Get("Accept"), "text/html") {
  500. impart.WriteError(w, err)
  501. // }
  502. return
  503. }
  504. if IsJSON(r.Header.Get("Content-Type")) {
  505. impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."})
  506. return
  507. }
  508. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  509. }
  510. func correctPageFromLoginAttempt(r *http.Request) string {
  511. to := r.FormValue("to")
  512. if to == "" {
  513. to = "/"
  514. } else if !strings.HasPrefix(to, "/") {
  515. to = "/" + to
  516. }
  517. return to
  518. }
  519. func (h *Handler) LogHandlerFunc(f http.HandlerFunc) http.HandlerFunc {
  520. return func(w http.ResponseWriter, r *http.Request) {
  521. h.handleHTTPError(w, r, func() error {
  522. status := 200
  523. start := time.Now()
  524. defer func() {
  525. if e := recover(); e != nil {
  526. log.Error("Handler.LogHandlerFunc\n\n%s: %s", e, debug.Stack())
  527. h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r))
  528. status = 500
  529. }
  530. // TODO: log actual status code returned
  531. log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
  532. }()
  533. f(w, r)
  534. return nil
  535. }())
  536. }
  537. }
  538. func sendRedirect(w http.ResponseWriter, code int, location string) int {
  539. w.Header().Set("Location", location)
  540. w.WriteHeader(code)
  541. return code
  542. }