144 lines
5 KiB
Go
144 lines
5 KiB
Go
package admin
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"arimelody-web/controller"
|
|
"arimelody-web/model"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func accountHandler(app *model.AppState) http.Handler {
|
|
mux := http.NewServeMux()
|
|
|
|
mux.Handle("/password", changePasswordHandler(app))
|
|
mux.Handle("/delete", deleteAccountHandler(app))
|
|
mux.Handle("/", accountIndexHandler(app))
|
|
|
|
return mux
|
|
}
|
|
|
|
func accountIndexHandler(app *model.AppState) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
session := r.Context().Value("session").(*model.Session)
|
|
|
|
totps, err := controller.GetTOTPsForAccount(app.DB, session.Account.ID)
|
|
if err != nil {
|
|
fmt.Printf("WARN: Failed to fetch TOTPs: %v\n", err)
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
}
|
|
|
|
type accountResponse struct {
|
|
Session *model.Session
|
|
TOTPs []model.TOTP
|
|
}
|
|
|
|
err = pages["account"].Execute(w, accountResponse{
|
|
Session: session,
|
|
TOTPs: totps,
|
|
})
|
|
if err != nil {
|
|
fmt.Printf("WARN: Failed to render admin account page: %v\n", err)
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
}
|
|
})
|
|
}
|
|
|
|
func changePasswordHandler(app *model.AppState) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
session := r.Context().Value("session").(*model.Session)
|
|
|
|
r.ParseForm()
|
|
|
|
currentPassword := r.Form.Get("current-password")
|
|
if err := bcrypt.CompareHashAndPassword([]byte(session.Account.Password), []byte(currentPassword)); err != nil {
|
|
controller.SetSessionMessage(app.DB, session, "Incorrect password.")
|
|
http.Redirect(w, r, "/admin/account", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
newPassword := r.Form.Get("new-password")
|
|
|
|
controller.SetSessionMessage(app.DB, session, fmt.Sprintf("Updating password to <code>%s</code>", newPassword))
|
|
http.Redirect(w, r, "/admin/account", http.StatusFound)
|
|
})
|
|
}
|
|
|
|
func deleteAccountHandler(app *model.AppState) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if !r.Form.Has("password") || !r.Form.Has("totp") {
|
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
session := r.Context().Value("session").(*model.Session)
|
|
|
|
// check password
|
|
if err := bcrypt.CompareHashAndPassword([]byte(session.Account.Password), []byte(r.Form.Get("password"))); err != nil {
|
|
fmt.Printf(
|
|
"[%s] WARN: Account \"%s\" attempted account deletion with incorrect password.\n",
|
|
time.Now().Format("2006-02-01 15:04:05"),
|
|
session.Account.Username,
|
|
)
|
|
controller.SetSessionMessage(app.DB, session, "Incorrect password.")
|
|
http.Redirect(w, r, "/admin/account", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
totpMethod, err := controller.CheckTOTPForAccount(app.DB, session.Account.ID, r.Form.Get("totp"))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to fetch account: %v\n", err)
|
|
controller.SetSessionMessage(app.DB, session, "Something went wrong. Please try again.")
|
|
http.Redirect(w, r, "/admin/account", http.StatusFound)
|
|
return
|
|
}
|
|
if totpMethod == nil {
|
|
fmt.Printf(
|
|
"[%s] WARN: Account \"%s\" attempted account deletion with incorrect TOTP.\n",
|
|
time.Now().Format("2006-02-01 15:04:05"),
|
|
session.Account.Username,
|
|
)
|
|
controller.SetSessionMessage(app.DB, session, "Incorrect TOTP.")
|
|
http.Redirect(w, r, "/admin/account", http.StatusFound)
|
|
}
|
|
|
|
err = controller.DeleteAccount(app.DB, session.Account.ID)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to delete account: %v\n", err)
|
|
controller.SetSessionMessage(app.DB, session, "Something went wrong. Please try again.")
|
|
http.Redirect(w, r, "/admin/account", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
fmt.Printf(
|
|
"[%s] INFO: Account \"%s\" deleted by user request.\n",
|
|
time.Now().Format("2006-02-01 15:04:05"),
|
|
session.Account.Username,
|
|
)
|
|
|
|
session.Account = nil
|
|
controller.SetSessionMessage(app.DB, session, "Account deleted successfully.")
|
|
http.Redirect(w, r, "/admin/login", http.StatusFound)
|
|
})
|
|
}
|