1/* 2 * Copyright © 2018-2021 A Bunch Tell LLC. 3 * 4 * This file is part of WriteFreely. 5 * 6 * WriteFreely is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Affero General Public License, included 8 * in the LICENSE file in this source code package. 9 */ 10 11package writefreely 12 13import ( 14 "fmt" 15 "html/template" 16 "net/http" 17 "net/url" 18 "runtime/debug" 19 "strconv" 20 "strings" 21 "time" 22 23 "github.com/gorilla/sessions" 24 "git.mills.io/prologic/go-gopher" 25 "github.com/writeas/impart" 26 "github.com/writeas/web-core/log" 27 "github.com/writefreely/writefreely/config" 28 "github.com/writefreely/writefreely/page" 29) 30 31// UserLevel represents the required user level for accessing an endpoint 32type UserLevel int 33 34const ( 35 UserLevelNoneType UserLevel = iota // user or not -- ignored 36 UserLevelOptionalType // user or not -- object fetched if user 37 UserLevelNoneRequiredType // non-user (required) 38 UserLevelUserType // user (required) 39) 40 41func UserLevelNone(cfg *config.Config) UserLevel { 42 return UserLevelNoneType 43} 44 45func UserLevelOptional(cfg *config.Config) UserLevel { 46 return UserLevelOptionalType 47} 48 49func UserLevelNoneRequired(cfg *config.Config) UserLevel { 50 return UserLevelNoneRequiredType 51} 52 53func UserLevelUser(cfg *config.Config) UserLevel { 54 return UserLevelUserType 55} 56 57// UserLevelReader returns the permission level required for any route where 58// users can read published content. 59func UserLevelReader(cfg *config.Config) UserLevel { 60 if cfg.App.Private { 61 return UserLevelUserType 62 } 63 return UserLevelOptionalType 64} 65 66type ( 67 handlerFunc func(app *App, w http.ResponseWriter, r *http.Request) error 68 gopherFunc func(app *App, w gopher.ResponseWriter, r *gopher.Request) error 69 userHandlerFunc func(app *App, u *User, w http.ResponseWriter, r *http.Request) error 70 userApperHandlerFunc func(apper Apper, u *User, w http.ResponseWriter, r *http.Request) error 71 dataHandlerFunc func(app *App, w http.ResponseWriter, r *http.Request) ([]byte, string, error) 72 authFunc func(app *App, r *http.Request) (*User, error) 73 UserLevelFunc func(cfg *config.Config) UserLevel 74) 75 76type Handler struct { 77 errors *ErrorPages 78 sessionStore sessions.Store 79 app Apper 80} 81 82// ErrorPages hold template HTML error pages for displaying errors to the user. 83// In each, there should be a defined template named "base". 84type ErrorPages struct { 85 NotFound *template.Template 86 Gone *template.Template 87 InternalServerError *template.Template 88 UnavailableError *template.Template 89 Blank *template.Template 90} 91 92// NewHandler returns a new Handler instance, using the given StaticPage data, 93// and saving alias to the application's CookieStore. 94func NewHandler(apper Apper) *Handler { 95 h := &Handler{ 96 errors: &ErrorPages{ 97 NotFound: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>404</title></head><body><p>Not found.</p></body></html>{{end}}")), 98 Gone: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>410</title></head><body><p>Gone.</p></body></html>{{end}}")), 99 InternalServerError: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>500</title></head><body><p>Internal server error.</p></body></html>{{end}}")), 100 UnavailableError: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>503</title></head><body><p>Service is temporarily unavailable.</p></body></html>{{end}}")), 101 Blank: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>{{.Title}}</title></head><body><p>{{.Content}}</p></body></html>{{end}}")), 102 }, 103 sessionStore: apper.App().SessionStore(), 104 app: apper, 105 } 106 107 return h 108} 109 110// NewWFHandler returns a new Handler instance, using WriteFreely template files. 111// You MUST call writefreely.InitTemplates() before this. 112func NewWFHandler(apper Apper) *Handler { 113 h := NewHandler(apper) 114 h.SetErrorPages(&ErrorPages{ 115 NotFound: pages["404-general.tmpl"], 116 Gone: pages["410.tmpl"], 117 InternalServerError: pages["500.tmpl"], 118 UnavailableError: pages["503.tmpl"], 119 Blank: pages["blank.tmpl"], 120 }) 121 return h 122} 123 124// SetErrorPages sets the given set of ErrorPages as templates for any errors 125// that come up. 126func (h *Handler) SetErrorPages(e *ErrorPages) { 127 h.errors = e 128} 129 130// User handles requests made in the web application by the authenticated user. 131// This provides user-friendly HTML pages and actions that work in the browser. 132func (h *Handler) User(f userHandlerFunc) http.HandlerFunc { 133 return func(w http.ResponseWriter, r *http.Request) { 134 h.handleHTTPError(w, r, func() error { 135 var status int 136 start := time.Now() 137 138 defer func() { 139 if e := recover(); e != nil { 140 log.Error("%s: %s", e, debug.Stack()) 141 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 142 status = http.StatusInternalServerError 143 } 144 145 log.Info(h.app.ReqLog(r, status, time.Since(start))) 146 }() 147 148 u := getUserSession(h.app.App(), r) 149 if u == nil { 150 err := ErrNotLoggedIn 151 status = err.Status 152 return err 153 } 154 155 err := f(h.app.App(), u, w, r) 156 if err == nil { 157 status = http.StatusOK 158 } else if err, ok := err.(impart.HTTPError); ok { 159 status = err.Status 160 } else { 161 status = http.StatusInternalServerError 162 } 163 164 return err 165 }()) 166 } 167} 168 169// Admin handles requests on /admin routes 170func (h *Handler) Admin(f userHandlerFunc) http.HandlerFunc { 171 return func(w http.ResponseWriter, r *http.Request) { 172 h.handleHTTPError(w, r, func() error { 173 var status int 174 start := time.Now() 175 176 defer func() { 177 if e := recover(); e != nil { 178 log.Error("%s: %s", e, debug.Stack()) 179 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 180 status = http.StatusInternalServerError 181 } 182 183 log.Info(h.app.ReqLog(r, status, time.Since(start))) 184 }() 185 186 u := getUserSession(h.app.App(), r) 187 if u == nil || !u.IsAdmin() { 188 err := impart.HTTPError{http.StatusNotFound, ""} 189 status = err.Status 190 return err 191 } 192 193 err := f(h.app.App(), u, w, r) 194 if err == nil { 195 status = http.StatusOK 196 } else if err, ok := err.(impart.HTTPError); ok { 197 status = err.Status 198 } else { 199 status = http.StatusInternalServerError 200 } 201 202 return err 203 }()) 204 } 205} 206 207// AdminApper handles requests on /admin routes that require an Apper. 208func (h *Handler) AdminApper(f userApperHandlerFunc) http.HandlerFunc { 209 return func(w http.ResponseWriter, r *http.Request) { 210 h.handleHTTPError(w, r, func() error { 211 var status int 212 start := time.Now() 213 214 defer func() { 215 if e := recover(); e != nil { 216 log.Error("%s: %s", e, debug.Stack()) 217 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 218 status = http.StatusInternalServerError 219 } 220 221 log.Info(h.app.ReqLog(r, status, time.Since(start))) 222 }() 223 224 u := getUserSession(h.app.App(), r) 225 if u == nil || !u.IsAdmin() { 226 err := impart.HTTPError{http.StatusNotFound, ""} 227 status = err.Status 228 return err 229 } 230 231 err := f(h.app, u, w, r) 232 if err == nil { 233 status = http.StatusOK 234 } else if err, ok := err.(impart.HTTPError); ok { 235 status = err.Status 236 } else { 237 status = http.StatusInternalServerError 238 } 239 240 return err 241 }()) 242 } 243} 244 245func apiAuth(app *App, r *http.Request) (*User, error) { 246 // Authorize user from Authorization header 247 t := r.Header.Get("Authorization") 248 if t == "" { 249 return nil, ErrNoAccessToken 250 } 251 u := &User{ID: app.db.GetUserID(t)} 252 if u.ID == -1 { 253 return nil, ErrBadAccessToken 254 } 255 256 return u, nil 257} 258 259// optionaAPIAuth is used for endpoints that accept authenticated requests via 260// Authorization header or cookie, unlike apiAuth. It returns a different err 261// in the case where no Authorization header is present. 262func optionalAPIAuth(app *App, r *http.Request) (*User, error) { 263 // Authorize user from Authorization header 264 t := r.Header.Get("Authorization") 265 if t == "" { 266 return nil, ErrNotLoggedIn 267 } 268 u := &User{ID: app.db.GetUserID(t)} 269 if u.ID == -1 { 270 return nil, ErrBadAccessToken 271 } 272 273 return u, nil 274} 275 276func webAuth(app *App, r *http.Request) (*User, error) { 277 u := getUserSession(app, r) 278 if u == nil { 279 return nil, ErrNotLoggedIn 280 } 281 return u, nil 282} 283 284// UserAPI handles requests made in the API by the authenticated user. 285// This provides user-friendly HTML pages and actions that work in the browser. 286func (h *Handler) UserAPI(f userHandlerFunc) http.HandlerFunc { 287 return h.UserAll(false, f, apiAuth) 288} 289 290// UserWebAPI handles endpoints that accept a user authorized either via the web (cookies) or an Authorization header. 291func (h *Handler) UserWebAPI(f userHandlerFunc) http.HandlerFunc { 292 return h.UserAll(false, f, func(app *App, r *http.Request) (*User, error) { 293 // Authorize user via cookies 294 u := getUserSession(app, r) 295 if u != nil { 296 return u, nil 297 } 298 299 // Fall back to access token, since user isn't logged in via web 300 var err error 301 u, err = apiAuth(app, r) 302 if err != nil { 303 return nil, err 304 } 305 306 return u, nil 307 }) 308} 309 310func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerFunc { 311 return func(w http.ResponseWriter, r *http.Request) { 312 handleFunc := func() error { 313 var status int 314 start := time.Now() 315 316 defer func() { 317 if e := recover(); e != nil { 318 log.Error("%s: %s", e, debug.Stack()) 319 impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."}) 320 status = 500 321 } 322 323 log.Info(h.app.ReqLog(r, status, time.Since(start))) 324 }() 325 326 u, err := a(h.app.App(), r) 327 if err != nil { 328 if err, ok := err.(impart.HTTPError); ok { 329 status = err.Status 330 } else { 331 status = 500 332 } 333 return err 334 } 335 336 err = f(h.app.App(), u, w, r) 337 if err == nil { 338 status = 200 339 } else if err, ok := err.(impart.HTTPError); ok { 340 status = err.Status 341 } else { 342 status = 500 343 } 344 345 return err 346 } 347 348 if web { 349 h.handleHTTPError(w, r, handleFunc()) 350 } else { 351 h.handleError(w, r, handleFunc()) 352 } 353 } 354} 355 356func (h *Handler) RedirectOnErr(f handlerFunc, loc string) handlerFunc { 357 return func(app *App, w http.ResponseWriter, r *http.Request) error { 358 err := f(app, w, r) 359 if err != nil { 360 if ie, ok := err.(impart.HTTPError); ok { 361 // Override default redirect with returned error's, if it's a 362 // redirect error. 363 if ie.Status == http.StatusFound { 364 return ie 365 } 366 } 367 return impart.HTTPError{http.StatusFound, loc} 368 } 369 return nil 370 } 371} 372 373func (h *Handler) Page(n string) http.HandlerFunc { 374 return h.Web(func(app *App, w http.ResponseWriter, r *http.Request) error { 375 t, ok := pages[n] 376 if !ok { 377 return impart.HTTPError{http.StatusNotFound, "Page not found."} 378 } 379 380 sp := pageForReq(app, r) 381 382 err := t.ExecuteTemplate(w, "base", sp) 383 if err != nil { 384 log.Error("Unable to render page: %v", err) 385 } 386 return err 387 }, UserLevelOptional) 388} 389 390func (h *Handler) WebErrors(f handlerFunc, ul UserLevelFunc) http.HandlerFunc { 391 return func(w http.ResponseWriter, r *http.Request) { 392 // TODO: factor out this logic shared with Web() 393 h.handleHTTPError(w, r, func() error { 394 var status int 395 start := time.Now() 396 397 defer func() { 398 if e := recover(); e != nil { 399 u := getUserSession(h.app.App(), r) 400 username := "None" 401 if u != nil { 402 username = u.Username 403 } 404 log.Error("User: %s\n\n%s: %s", username, e, debug.Stack()) 405 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 406 status = 500 407 } 408 409 log.Info(h.app.ReqLog(r, status, time.Since(start))) 410 }() 411 412 var session *sessions.Session 413 var err error 414 if ul(h.app.App().cfg) != UserLevelNoneType { 415 session, err = h.sessionStore.Get(r, cookieName) 416 if err != nil && (ul(h.app.App().cfg) == UserLevelNoneRequiredType || ul(h.app.App().cfg) == UserLevelUserType) { 417 // Cookie is required, but we can ignore this error 418 log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul(h.app.App().cfg), err) 419 } 420 421 _, gotUser := session.Values[cookieUserVal].(*User) 422 if ul(h.app.App().cfg) == UserLevelNoneRequiredType && gotUser { 423 to := correctPageFromLoginAttempt(r) 424 log.Info("Handler: Required NO user, but got one. Redirecting to %s", to) 425 err := impart.HTTPError{http.StatusFound, to} 426 status = err.Status 427 return err 428 } else if ul(h.app.App().cfg) == UserLevelUserType && !gotUser { 429 log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.") 430 err := ErrNotLoggedIn 431 status = err.Status 432 return err 433 } 434 } 435 436 // TODO: pass User object to function 437 err = f(h.app.App(), w, r) 438 if err == nil { 439 status = 200 440 } else if httpErr, ok := err.(impart.HTTPError); ok { 441 status = httpErr.Status 442 if status < 300 || status > 399 { 443 addSessionFlash(h.app.App(), w, r, httpErr.Message, session) 444 return impart.HTTPError{http.StatusFound, r.Referer()} 445 } 446 } else { 447 e := fmt.Sprintf("[Web handler] 500: %v", err) 448 if !strings.HasSuffix(e, "write: broken pipe") { 449 log.Error(e) 450 } else { 451 log.Error(e) 452 } 453 log.Info("Web handler internal error render") 454 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 455 status = 500 456 } 457 458 return err 459 }()) 460 } 461} 462 463func (h *Handler) CollectionPostOrStatic(w http.ResponseWriter, r *http.Request) { 464 if strings.Contains(r.URL.Path, ".") && !isRaw(r) { 465 start := time.Now() 466 status := 200 467 defer func() { 468 log.Info(h.app.ReqLog(r, status, time.Since(start))) 469 }() 470 471 // Serve static file 472 h.app.App().shttp.ServeHTTP(w, r) 473 return 474 } 475 476 h.Web(viewCollectionPost, UserLevelReader)(w, r) 477} 478 479// Web handles requests made in the web application. This provides user- 480// friendly HTML pages and actions that work in the browser. 481func (h *Handler) Web(f handlerFunc, ul UserLevelFunc) http.HandlerFunc { 482 return func(w http.ResponseWriter, r *http.Request) { 483 h.handleHTTPError(w, r, func() error { 484 var status int 485 start := time.Now() 486 487 defer func() { 488 if e := recover(); e != nil { 489 u := getUserSession(h.app.App(), r) 490 username := "None" 491 if u != nil { 492 username = u.Username 493 } 494 log.Error("User: %s\n\n%s: %s", username, e, debug.Stack()) 495 log.Info("Web deferred internal error render") 496 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 497 status = 500 498 } 499 500 log.Info(h.app.ReqLog(r, status, time.Since(start))) 501 }() 502 503 if ul(h.app.App().cfg) != UserLevelNoneType { 504 session, err := h.sessionStore.Get(r, cookieName) 505 if err != nil && (ul(h.app.App().cfg) == UserLevelNoneRequiredType || ul(h.app.App().cfg) == UserLevelUserType) { 506 // Cookie is required, but we can ignore this error 507 log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul(h.app.App().cfg), err) 508 } 509 510 _, gotUser := session.Values[cookieUserVal].(*User) 511 if ul(h.app.App().cfg) == UserLevelNoneRequiredType && gotUser { 512 to := correctPageFromLoginAttempt(r) 513 log.Info("Handler: Required NO user, but got one. Redirecting to %s", to) 514 err := impart.HTTPError{http.StatusFound, to} 515 status = err.Status 516 return err 517 } else if ul(h.app.App().cfg) == UserLevelUserType && !gotUser { 518 log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.") 519 err := ErrNotLoggedIn 520 status = err.Status 521 return err 522 } 523 } 524 525 // TODO: pass User object to function 526 err := f(h.app.App(), w, r) 527 if err == nil { 528 status = 200 529 } else if httpErr, ok := err.(impart.HTTPError); ok { 530 status = httpErr.Status 531 } else { 532 e := fmt.Sprintf("[Web handler] 500: %v", err) 533 log.Error(e) 534 log.Info("Web internal error render") 535 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 536 status = 500 537 } 538 539 return err 540 }()) 541 } 542} 543 544func (h *Handler) All(f handlerFunc) http.HandlerFunc { 545 return func(w http.ResponseWriter, r *http.Request) { 546 h.handleError(w, r, func() error { 547 // TODO: return correct "success" status 548 status := 200 549 start := time.Now() 550 551 defer func() { 552 if e := recover(); e != nil { 553 log.Error("%s:\n%s", e, debug.Stack()) 554 impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."}) 555 status = 500 556 } 557 558 log.Info(h.app.ReqLog(r, status, time.Since(start))) 559 }() 560 561 // TODO: do any needed authentication 562 563 err := f(h.app.App(), w, r) 564 if err != nil { 565 if err, ok := err.(impart.HTTPError); ok { 566 status = err.Status 567 } else { 568 status = 500 569 } 570 } 571 572 return err 573 }()) 574 } 575} 576 577func (h *Handler) PlainTextAPI(f handlerFunc) http.HandlerFunc { 578 return func(w http.ResponseWriter, r *http.Request) { 579 h.handleTextError(w, r, func() error { 580 // TODO: return correct "success" status 581 status := 200 582 start := time.Now() 583 584 defer func() { 585 if e := recover(); e != nil { 586 log.Error("%s:\n%s", e, debug.Stack()) 587 status = http.StatusInternalServerError 588 w.WriteHeader(status) 589 fmt.Fprintf(w, "Something didn't work quite right. The robots have alerted the humans.") 590 } 591 592 log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\" \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent(), r.Host)) 593 }() 594 595 err := f(h.app.App(), w, r) 596 if err != nil { 597 if err, ok := err.(impart.HTTPError); ok { 598 status = err.Status 599 } else { 600 status = http.StatusInternalServerError 601 } 602 } 603 604 return err 605 }()) 606 } 607} 608 609func (h *Handler) OAuth(f handlerFunc) http.HandlerFunc { 610 return func(w http.ResponseWriter, r *http.Request) { 611 h.handleOAuthError(w, r, func() error { 612 // TODO: return correct "success" status 613 status := 200 614 start := time.Now() 615 616 defer func() { 617 if e := recover(); e != nil { 618 log.Error("%s:\n%s", e, debug.Stack()) 619 impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."}) 620 status = 500 621 } 622 623 log.Info(h.app.ReqLog(r, status, time.Since(start))) 624 }() 625 626 err := f(h.app.App(), w, r) 627 if err != nil { 628 if err, ok := err.(impart.HTTPError); ok { 629 status = err.Status 630 } else { 631 status = 500 632 } 633 } 634 635 return err 636 }()) 637 } 638} 639 640func (h *Handler) AllReader(f handlerFunc) http.HandlerFunc { 641 return func(w http.ResponseWriter, r *http.Request) { 642 h.handleError(w, r, func() error { 643 status := 200 644 start := time.Now() 645 646 defer func() { 647 if e := recover(); e != nil { 648 log.Error("%s:\n%s", e, debug.Stack()) 649 impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."}) 650 status = 500 651 } 652 653 log.Info(h.app.ReqLog(r, status, time.Since(start))) 654 }() 655 656 // Allow any origin, as public endpoints are handled in here 657 w.Header().Set("Access-Control-Allow-Origin", "*") 658 659 if h.app.App().cfg.App.Private { 660 // This instance is private, so ensure it's being accessed by a valid user 661 // Check if authenticated with an access token 662 _, apiErr := optionalAPIAuth(h.app.App(), r) 663 if apiErr != nil { 664 if err, ok := apiErr.(impart.HTTPError); ok { 665 status = err.Status 666 } else { 667 status = 500 668 } 669 670 if apiErr == ErrNotLoggedIn { 671 // Fall back to web auth since there was no access token given 672 _, err := webAuth(h.app.App(), r) 673 if err != nil { 674 if err, ok := apiErr.(impart.HTTPError); ok { 675 status = err.Status 676 } else { 677 status = 500 678 } 679 return err 680 } 681 } else { 682 return apiErr 683 } 684 } 685 } 686 687 err := f(h.app.App(), w, r) 688 if err != nil { 689 if err, ok := err.(impart.HTTPError); ok { 690 status = err.Status 691 } else { 692 status = 500 693 } 694 } 695 696 return err 697 }()) 698 } 699} 700 701func (h *Handler) Download(f dataHandlerFunc, ul UserLevelFunc) http.HandlerFunc { 702 return func(w http.ResponseWriter, r *http.Request) { 703 h.handleHTTPError(w, r, func() error { 704 var status int 705 start := time.Now() 706 defer func() { 707 if e := recover(); e != nil { 708 log.Error("%s: %s", e, debug.Stack()) 709 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 710 status = 500 711 } 712 713 log.Info(h.app.ReqLog(r, status, time.Since(start))) 714 }() 715 716 data, filename, err := f(h.app.App(), w, r) 717 if err != nil { 718 if err, ok := err.(impart.HTTPError); ok { 719 status = err.Status 720 } else { 721 status = 500 722 } 723 return err 724 } 725 726 ext := ".json" 727 ct := "application/json" 728 if strings.HasSuffix(r.URL.Path, ".csv") { 729 ext = ".csv" 730 ct = "text/csv" 731 } else if strings.HasSuffix(r.URL.Path, ".zip") { 732 ext = ".zip" 733 ct = "application/zip" 734 } 735 w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s%s", filename, ext)) 736 w.Header().Set("Content-Type", ct) 737 w.Header().Set("Content-Length", strconv.Itoa(len(data))) 738 fmt.Fprint(w, string(data)) 739 740 status = 200 741 return nil 742 }()) 743 } 744} 745 746func (h *Handler) Redirect(url string, ul UserLevelFunc) http.HandlerFunc { 747 return func(w http.ResponseWriter, r *http.Request) { 748 h.handleHTTPError(w, r, func() error { 749 start := time.Now() 750 751 var status int 752 if ul(h.app.App().cfg) != UserLevelNoneType { 753 session, err := h.sessionStore.Get(r, cookieName) 754 if err != nil && (ul(h.app.App().cfg) == UserLevelNoneRequiredType || ul(h.app.App().cfg) == UserLevelUserType) { 755 // Cookie is required, but we can ignore this error 756 log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul(h.app.App().cfg), err) 757 } 758 759 _, gotUser := session.Values[cookieUserVal].(*User) 760 if ul(h.app.App().cfg) == UserLevelNoneRequiredType && gotUser { 761 to := correctPageFromLoginAttempt(r) 762 log.Info("Handler: Required NO user, but got one. Redirecting to %s", to) 763 err := impart.HTTPError{http.StatusFound, to} 764 status = err.Status 765 return err 766 } else if ul(h.app.App().cfg) == UserLevelUserType && !gotUser { 767 log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.") 768 err := ErrNotLoggedIn 769 status = err.Status 770 return err 771 } 772 } 773 774 status = sendRedirect(w, http.StatusFound, url) 775 776 log.Info(h.app.ReqLog(r, status, time.Since(start))) 777 778 return nil 779 }()) 780 } 781} 782 783func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err error) { 784 if err == nil { 785 return 786 } 787 788 if err, ok := err.(impart.HTTPError); ok { 789 if err.Status >= 300 && err.Status < 400 { 790 sendRedirect(w, err.Status, err.Message) 791 return 792 } else if err.Status == http.StatusUnauthorized { 793 q := "" 794 if r.URL.RawQuery != "" { 795 q = url.QueryEscape("?" + r.URL.RawQuery) 796 } 797 sendRedirect(w, http.StatusFound, "/login?to="+r.URL.Path+q) 798 return 799 } else if err.Status == http.StatusGone { 800 w.WriteHeader(err.Status) 801 p := &struct { 802 page.StaticPage 803 Content *template.HTML 804 }{ 805 StaticPage: pageForReq(h.app.App(), r), 806 } 807 if err.Message != "" { 808 co := template.HTML(err.Message) 809 p.Content = &co 810 } 811 h.errors.Gone.ExecuteTemplate(w, "base", p) 812 return 813 } else if err.Status == http.StatusNotFound { 814 w.WriteHeader(err.Status) 815 if strings.Contains(r.Header.Get("Accept"), "application/activity+json") { 816 // This is a fediverse request; simply return the header 817 return 818 } 819 h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 820 return 821 } else if err.Status == http.StatusInternalServerError { 822 w.WriteHeader(err.Status) 823 log.Info("handleHTTPErorr internal error render") 824 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 825 return 826 } else if err.Status == http.StatusServiceUnavailable { 827 w.WriteHeader(err.Status) 828 h.errors.UnavailableError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 829 return 830 } else if err.Status == http.StatusAccepted { 831 impart.WriteSuccess(w, "", err.Status) 832 return 833 } else { 834 p := &struct { 835 page.StaticPage 836 Title string 837 Content template.HTML 838 }{ 839 pageForReq(h.app.App(), r), 840 fmt.Sprintf("Uh oh (%d)", err.Status), 841 template.HTML(fmt.Sprintf("<p style=\"text-align: center\" class=\"introduction\">%s</p>", err.Message)), 842 } 843 h.errors.Blank.ExecuteTemplate(w, "base", p) 844 return 845 } 846 impart.WriteError(w, err) 847 return 848 } 849 850 impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."}) 851} 852 853func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) { 854 if err == nil { 855 return 856 } 857 858 if err, ok := err.(impart.HTTPError); ok { 859 if err.Status >= 300 && err.Status < 400 { 860 sendRedirect(w, err.Status, err.Message) 861 return 862 } 863 864 // if strings.Contains(r.Header.Get("Accept"), "text/html") { 865 impart.WriteError(w, err) 866 // } 867 return 868 } 869 870 if IsJSON(r) { 871 impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."}) 872 return 873 } 874 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 875} 876 877func (h *Handler) handleTextError(w http.ResponseWriter, r *http.Request, err error) { 878 if err == nil { 879 return 880 } 881 882 if err, ok := err.(impart.HTTPError); ok { 883 if err.Status >= 300 && err.Status < 400 { 884 sendRedirect(w, err.Status, err.Message) 885 return 886 } 887 888 w.WriteHeader(err.Status) 889 fmt.Fprintf(w, http.StatusText(err.Status)) 890 return 891 } 892 893 w.WriteHeader(http.StatusInternalServerError) 894 fmt.Fprintf(w, "This is an unhelpful error message for a miscellaneous internal error.") 895} 896 897func (h *Handler) handleOAuthError(w http.ResponseWriter, r *http.Request, err error) { 898 if err == nil { 899 return 900 } 901 902 if err, ok := err.(impart.HTTPError); ok { 903 if err.Status >= 300 && err.Status < 400 { 904 sendRedirect(w, err.Status, err.Message) 905 return 906 } 907 908 impart.WriteOAuthError(w, err) 909 return 910 } 911 912 impart.WriteOAuthError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."}) 913 return 914} 915 916func correctPageFromLoginAttempt(r *http.Request) string { 917 to := r.FormValue("to") 918 if to == "" { 919 to = "/" 920 } else if !strings.HasPrefix(to, "/") { 921 to = "/" + to 922 } 923 return to 924} 925 926func (h *Handler) LogHandlerFunc(f http.HandlerFunc) http.HandlerFunc { 927 return func(w http.ResponseWriter, r *http.Request) { 928 h.handleHTTPError(w, r, func() error { 929 status := 200 930 start := time.Now() 931 932 defer func() { 933 if e := recover(); e != nil { 934 log.Error("Handler.LogHandlerFunc\n\n%s: %s", e, debug.Stack()) 935 h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) 936 status = 500 937 } 938 939 // TODO: log actual status code returned 940 log.Info(h.app.ReqLog(r, status, time.Since(start))) 941 }() 942 943 if h.app.App().cfg.App.Private { 944 // This instance is private, so ensure it's being accessed by a valid user 945 // Check if authenticated with an access token 946 _, apiErr := optionalAPIAuth(h.app.App(), r) 947 if apiErr != nil { 948 if err, ok := apiErr.(impart.HTTPError); ok { 949 status = err.Status 950 } else { 951 status = 500 952 } 953 954 if apiErr == ErrNotLoggedIn { 955 // Fall back to web auth since there was no access token given 956 _, err := webAuth(h.app.App(), r) 957 if err != nil { 958 if err, ok := apiErr.(impart.HTTPError); ok { 959 status = err.Status 960 } else { 961 status = 500 962 } 963 return err 964 } 965 } else { 966 return apiErr 967 } 968 } 969 } 970 971 f(w, r) 972 973 return nil 974 }()) 975 } 976} 977 978func (h *Handler) Gopher(f gopherFunc) gopher.HandlerFunc { 979 return func(w gopher.ResponseWriter, r *gopher.Request) { 980 defer func() { 981 if e := recover(); e != nil { 982 log.Error("%s: %s", e, debug.Stack()) 983 w.WriteError("An internal error occurred") 984 } 985 log.Info("gopher: %s", r.Selector) 986 }() 987 988 err := f(h.app.App(), w, r) 989 if err != nil { 990 log.Error("failed: %s", err) 991 w.WriteError("the page failed for some reason (see logs)") 992 } 993 } 994} 995 996func sendRedirect(w http.ResponseWriter, code int, location string) int { 997 w.Header().Set("Location", location) 998 w.WriteHeader(code) 999 return code 1000} 1001 1002func cacheControl(next http.Handler) http.Handler { 1003 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1004 w.Header().Set("Cache-Control", "public, max-age=604800, immutable") 1005 next.ServeHTTP(w, r) 1006 }) 1007} 1008