blog/core/auth.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)
}