diff --git a/admin/accounthttp.go b/admin/accounthttp.go index b2cf18a..b354fd5 100644 --- a/admin/accounthttp.go +++ b/admin/accounthttp.go @@ -3,6 +3,7 @@ package admin import ( "fmt" "net/http" + "net/url" "os" "strings" "time" @@ -13,13 +14,22 @@ import ( "golang.org/x/crypto/bcrypt" ) -type TemplateData struct { +type loginRegisterResponse struct { Account *model.Account Message string Token string } func AccountHandler(app *model.AppState) http.Handler { + mux := http.NewServeMux() + + mux.Handle("/password", changePasswordHandler(app)) + mux.Handle("/", accountIndexHandler(app)) + + return mux +} + +func accountIndexHandler(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { account := r.Context().Value("account").(*model.Account) @@ -29,14 +39,18 @@ func AccountHandler(app *model.AppState) http.Handler { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } - type AccountResponse struct { + type accountResponse struct { Account *model.Account TOTPs []model.TOTP + Message string + Error string } - err = pages["account"].Execute(w, AccountResponse{ + err = pages["account"].Execute(w, accountResponse{ Account: account, TOTPs: totps, + Message: r.URL.Query().Get("message"), + Error: r.URL.Query().Get("error"), }) if err != nil { fmt.Printf("WARN: Failed to render admin account page: %v\n", err) @@ -59,7 +73,7 @@ func LoginHandler(app *model.AppState) http.Handler { return } - err = pages["login"].Execute(w, TemplateData{}) + err = pages["login"].Execute(w, loginRegisterResponse{}) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Error rendering admin login page: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -213,12 +227,12 @@ func createAccountHandler(app *model.AppState) http.Handler { return } - type CreateAccountResponse struct { + type CreateaccountResponse struct { Account *model.Account Message string } - render := func(data CreateAccountResponse) { + render := func(data CreateaccountResponse) { err := pages["create-account"].Execute(w, data) if err != nil { fmt.Printf("WARN: Error rendering create account page: %s\n", err) @@ -227,7 +241,7 @@ func createAccountHandler(app *model.AppState) http.Handler { } if r.Method == http.MethodGet { - render(CreateAccountResponse{}) + render(CreateaccountResponse{}) return } @@ -238,7 +252,7 @@ func createAccountHandler(app *model.AppState) http.Handler { err = r.ParseForm() if err != nil { - render(CreateAccountResponse{ + render(CreateaccountResponse{ Message: "Malformed data.", }) return @@ -261,7 +275,7 @@ func createAccountHandler(app *model.AppState) http.Handler { invite, err := controller.GetInvite(app.DB, credentials.Invite) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve invite: %v\n", err) - render(CreateAccountResponse{ + render(CreateaccountResponse{ Message: "Something went wrong. Please try again.", }) return @@ -271,7 +285,7 @@ func createAccountHandler(app *model.AppState) http.Handler { err := controller.DeleteInvite(app.DB, invite.Code) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) } } - render(CreateAccountResponse{ + render(CreateaccountResponse{ Message: "Invalid invite code.", }) return @@ -280,7 +294,7 @@ func createAccountHandler(app *model.AppState) http.Handler { hashedPassword, err := bcrypt.GenerateFromPassword([]byte(credentials.Password), bcrypt.DefaultCost) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to generate password hash: %v\n", err) - render(CreateAccountResponse{ + render(CreateaccountResponse{ Message: "Something went wrong. Please try again.", }) return @@ -295,13 +309,13 @@ func createAccountHandler(app *model.AppState) http.Handler { err = controller.CreateAccount(app.DB, &account) if err != nil { if strings.HasPrefix(err.Error(), "pq: duplicate key") { - render(CreateAccountResponse{ + render(CreateaccountResponse{ Message: "An account with that username already exists.", }) return } fmt.Fprintf(os.Stderr, "WARN: Failed to create account: %v\n", err) - render(CreateAccountResponse{ + render(CreateaccountResponse{ Message: "Something went wrong. Please try again.", }) return @@ -330,14 +344,41 @@ func createAccountHandler(app *model.AppState) http.Handler { cookie.Path = "/" http.SetCookie(w, &cookie) - err = pages["login"].Execute(w, TemplateData{ + err = pages["login"].Execute(w, loginRegisterResponse{ Account: &account, Token: token.Token, }) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to render login page: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } }) } + +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 + } + + account := r.Context().Value("account").(*model.Account) + + r.ParseForm() + + currentPassword := r.Form.Get("current-password") + if err := bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(currentPassword)); err != nil { + http.Redirect(w, r, "/admin/account?error=" + url.PathEscape("Incorrect password."), http.StatusFound) + return + } + + newPassword := r.Form.Get("new-password") + + http.Redirect( + w, r, "/admin/account?message=" + + url.PathEscape(fmt.Sprintf("Updating password to %s", newPassword)), + http.StatusFound, + ) + }) +} diff --git a/admin/http.go b/admin/http.go index b44cfa9..763537a 100644 --- a/admin/http.go +++ b/admin/http.go @@ -17,7 +17,7 @@ func Handler(app *model.AppState) http.Handler { mux.Handle("/login", LoginHandler(app)) mux.Handle("/register", createAccountHandler(app)) mux.Handle("/logout", RequireAccount(app, LogoutHandler(app))) - mux.Handle("/account", RequireAccount(app, AccountHandler(app))) + mux.Handle("/account/", RequireAccount(app, http.StripPrefix("/account", AccountHandler(app)))) mux.Handle("/static/", http.StripPrefix("/static", staticHandler())) mux.Handle("/release/", RequireAccount(app, http.StripPrefix("/release", serveRelease(app)))) mux.Handle("/artist/", RequireAccount(app, http.StripPrefix("/artist", serveArtist(app)))) diff --git a/admin/views/edit-account.html b/admin/views/edit-account.html index 4d89052..fd527b4 100644 --- a/admin/views/edit-account.html +++ b/admin/views/edit-account.html @@ -6,22 +6,28 @@ {{define "content"}}
+ {{if .Message}} +

{{.Message}}

+ {{end}} + {{if .Error}} +

{{.Error}}

+ {{end}}

Account Settings ({{.Account.Username}})

Change Password

-
+
- + - + - +
diff --git a/admin/views/layout.html b/admin/views/layout.html index 0a33b72..bacf014 100644 --- a/admin/views/layout.html +++ b/admin/views/layout.html @@ -26,7 +26,10 @@
{{if .Account}} + {{else}}