(incomplete) change password feature
This commit is contained in:
parent
5531ef6bab
commit
0052c470f9
|
@ -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,7 +344,7 @@ 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,
|
||||
})
|
||||
|
@ -341,3 +355,30 @@ func createAccountHandler(app *model.AppState) http.Handler {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
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 <code>%s</code>", newPassword)),
|
||||
http.StatusFound,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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))))
|
||||
|
|
|
@ -6,22 +6,28 @@
|
|||
|
||||
{{define "content"}}
|
||||
<main>
|
||||
{{if .Message}}
|
||||
<p id="message">{{.Message}}</p>
|
||||
{{end}}
|
||||
{{if .Error}}
|
||||
<p id="error">{{.Error}}</p>
|
||||
{{end}}
|
||||
<h1>Account Settings ({{.Account.Username}})</h1>
|
||||
|
||||
<div class="card-title">
|
||||
<h2>Change Password</h2>
|
||||
</div>
|
||||
<div class="card">
|
||||
<form action="/api/v1/change-password" method="POST" id="change-password">
|
||||
<form action="/admin/account/password" method="POST" id="change-password">
|
||||
<div>
|
||||
<label for="current-password">Current Password</label>
|
||||
<input type="password" name="current-password" value="" autocomplete="current-password">
|
||||
<input type="password" id="current-password" name="current-password" value="" autocomplete="current-password">
|
||||
|
||||
<label for="new-password">Password</label>
|
||||
<input type="password" name="new-password" value="" autocomplete="new-password">
|
||||
<input type="password" id="new-password" name="new-password" value="" autocomplete="new-password">
|
||||
|
||||
<label for="confirm-password">Confirm Password</label>
|
||||
<input type="password" name="confirm-password" value="" autocomplete="new-password">
|
||||
<input type="password" id="confirm-password" value="" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="save">Change Password</button>
|
||||
|
|
|
@ -26,7 +26,10 @@
|
|||
<div class="flex-fill"></div>
|
||||
{{if .Account}}
|
||||
<div class="nav-item">
|
||||
<a href="/admin/logout" id="logout">logged in as {{.Account.Username}}. log out</a>
|
||||
<a href="/admin/account">account ({{.Account.Username}})</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/logout" id="logout">log out</a>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="nav-item">
|
||||
|
|
|
@ -19,6 +19,7 @@ func GetConfig() model.Config {
|
|||
|
||||
config := model.Config{
|
||||
BaseUrl: "https://arimelody.me",
|
||||
Host: "0.0.0.0",
|
||||
Port: 8080,
|
||||
DB: model.DBConfig{
|
||||
Host: "127.0.0.1",
|
||||
|
@ -55,6 +56,7 @@ func handleConfigOverrides(config *model.Config) error {
|
|||
var err error
|
||||
|
||||
if env, has := os.LookupEnv("ARIMELODY_BASE_URL"); has { config.BaseUrl = env }
|
||||
if env, has := os.LookupEnv("ARIMELODY_HOST"); has { config.Host = env }
|
||||
if env, has := os.LookupEnv("ARIMELODY_PORT"); has {
|
||||
config.Port, err = strconv.ParseInt(env, 10, 0)
|
||||
if err != nil { return errors.New("ARIMELODY_PORT: " + err.Error()) }
|
||||
|
|
4
main.go
4
main.go
|
@ -338,9 +338,9 @@ func main() {
|
|||
|
||||
// start the web server!
|
||||
mux := createServeMux(&app)
|
||||
fmt.Printf("Now serving at %s:%d\n", app.Config.BaseUrl, app.Config.Port)
|
||||
fmt.Printf("Now serving at http://%s:%d\n", app.Config.Host, app.Config.Port)
|
||||
log.Fatal(
|
||||
http.ListenAndServe(fmt.Sprintf(":%d", app.Config.Port),
|
||||
http.ListenAndServe(fmt.Sprintf("%s:%d", app.Config.Host, app.Config.Port),
|
||||
HTTPLog(DefaultHeaders(mux)),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ type (
|
|||
|
||||
Config struct {
|
||||
BaseUrl string `toml:"base_url" comment:"Used for OAuth redirects."`
|
||||
Host string `toml:"host"`
|
||||
Port int64 `toml:"port"`
|
||||
DataDirectory string `toml:"data_dir"`
|
||||
DB DBConfig `toml:"db"`
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
CREATE SCHEMA IF NOT EXISTS arimelody;
|
||||
|
||||
--
|
||||
-- Tables
|
||||
--
|
||||
|
|
Loading…
Reference in a new issue