Noah Petherbridge
6d3de7da69
Since most of the `render.Vars{}` fields were hardcoded/not really editable for the templates, apart from .Data, this struct is now locked away in the render subpackage. End http.HandlerFunc's can then make any arbitrary template data structure they want to, available inside the templates as `.Data`.
180 lines
4.8 KiB
Go
180 lines
4.8 KiB
Go
package core
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/kirsle/blog/core/internal/forms"
|
|
"github.com/kirsle/blog/core/internal/log"
|
|
"github.com/kirsle/blog/core/internal/middleware/auth"
|
|
"github.com/kirsle/blog/core/internal/models/users"
|
|
"github.com/kirsle/blog/core/internal/render"
|
|
"github.com/kirsle/blog/core/internal/responses"
|
|
"github.com/kirsle/blog/core/internal/sessions"
|
|
)
|
|
|
|
// AuthRoutes attaches the auth routes to the app.
|
|
func (b *Blog) AuthRoutes(r *mux.Router) {
|
|
r.HandleFunc("/login", b.LoginHandler)
|
|
r.HandleFunc("/logout", b.LogoutHandler)
|
|
r.HandleFunc("/account", b.AccountHandler)
|
|
}
|
|
|
|
// MustLogin handles errors from the LoginRequired middleware by redirecting
|
|
// the user to the login page.
|
|
func (b *Blog) MustLogin(w http.ResponseWriter, r *http.Request) {
|
|
responses.Redirect(w, "/login?next="+r.URL.Path)
|
|
}
|
|
|
|
// Login logs the browser in as the given user.
|
|
func (b *Blog) Login(w http.ResponseWriter, r *http.Request, u *users.User) error {
|
|
session, err := sessions.Store.Get(r, "session") // TODO session name
|
|
if err != nil {
|
|
return err
|
|
}
|
|
session.Values["logged-in"] = true
|
|
session.Values["user-id"] = u.ID
|
|
session.Save(r, w)
|
|
return nil
|
|
}
|
|
|
|
// LoginHandler shows and handles the login page.
|
|
func (b *Blog) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|
vars := map[string]interface{}{
|
|
"Form": forms.Setup{},
|
|
}
|
|
|
|
var nextURL string
|
|
if r.Method == http.MethodPost {
|
|
nextURL = r.FormValue("next")
|
|
} else {
|
|
nextURL = r.URL.Query().Get("next")
|
|
}
|
|
vars["NextURL"] = nextURL
|
|
|
|
if r.Method == http.MethodPost {
|
|
form := &forms.Login{
|
|
Username: r.FormValue("username"),
|
|
Password: r.FormValue("password"),
|
|
}
|
|
vars["Form"] = form
|
|
err := form.Validate()
|
|
if err != nil {
|
|
vars["Error"] = err
|
|
} else {
|
|
// Test the login.
|
|
user, err := users.CheckAuth(form.Username, form.Password)
|
|
if err != nil {
|
|
vars["Error"] = errors.New("bad username or password")
|
|
} else {
|
|
// Login OK!
|
|
responses.Flash(w, r, "Login OK!")
|
|
b.Login(w, r, user)
|
|
|
|
// A next URL given? TODO: actually get to work
|
|
log.Info("Redirect after login to: %s", nextURL)
|
|
if len(nextURL) > 0 && nextURL[0] == '/' {
|
|
responses.Redirect(w, nextURL)
|
|
} else {
|
|
responses.Redirect(w, "/")
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
render.Template(w, r, "login", vars)
|
|
}
|
|
|
|
// LogoutHandler logs the user out and redirects to the home page.
|
|
func (b *Blog) LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
|
session, _ := sessions.Store.Get(r, "session")
|
|
delete(session.Values, "logged-in")
|
|
delete(session.Values, "user-id")
|
|
session.Save(r, w)
|
|
responses.Redirect(w, "/")
|
|
}
|
|
|
|
// AccountHandler shows the account settings page.
|
|
func (b *Blog) AccountHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !auth.LoggedIn(r) {
|
|
responses.FlashAndRedirect(w, r, "/login?next=/account", "You must be logged in to do that!")
|
|
return
|
|
}
|
|
currentUser, err := auth.CurrentUser(r)
|
|
if err != nil {
|
|
responses.FlashAndRedirect(w, r, "/login?next=/account", "You must be logged in to do that!!")
|
|
return
|
|
}
|
|
|
|
// Load an editable copy of the user.
|
|
user, err := users.Load(currentUser.ID)
|
|
if err != nil {
|
|
responses.FlashAndRedirect(w, r, "/login?next=/account", "User ID %d not loadable?", currentUser.ID)
|
|
return
|
|
}
|
|
|
|
form := &forms.Account{
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
Name: user.Name,
|
|
}
|
|
v := map[string]interface{}{
|
|
"Form": form,
|
|
}
|
|
|
|
if r.Method == http.MethodPost {
|
|
form.Username = users.Normalize(r.FormValue("username"))
|
|
form.Email = r.FormValue("email")
|
|
form.Name = r.FormValue("name")
|
|
form.OldPassword = r.FormValue("oldpassword")
|
|
form.NewPassword = r.FormValue("newpassword")
|
|
form.NewPassword2 = r.FormValue("newpassword2")
|
|
if err = form.Validate(); err != nil {
|
|
responses.Flash(w, r, err.Error())
|
|
} else {
|
|
var ok = true
|
|
|
|
// Validate the username is available.
|
|
if form.Username != user.Username {
|
|
if _, err = users.LoadUsername(form.Username); err == nil {
|
|
responses.Flash(w, r, "That username already exists.")
|
|
ok = false
|
|
}
|
|
}
|
|
|
|
// Changing their password?
|
|
if len(form.OldPassword) > 0 {
|
|
// Validate their old password.
|
|
if _, err = users.CheckAuth(form.Username, form.OldPassword); err != nil {
|
|
responses.Flash(w, r, "Your old password is incorrect.")
|
|
ok = false
|
|
} else {
|
|
err = user.SetPassword(form.NewPassword)
|
|
if err != nil {
|
|
responses.Flash(w, r, "Change password error: %s", err)
|
|
ok = false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Still good?
|
|
if ok {
|
|
user.Username = form.Username
|
|
user.Name = form.Name
|
|
user.Email = form.Email
|
|
err = user.Save()
|
|
if err != nil {
|
|
responses.Flash(w, r, "Error saving user: %s", err)
|
|
} else {
|
|
responses.FlashAndRedirect(w, r, "/account", "Settings saved!")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
render.Template(w, r, "account", v)
|
|
}
|