Mercurial > gemma
diff controllers/pwreset.go @ 302:0777aa6de45b
Password reset. Part I
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Wed, 01 Aug 2018 12:29:55 +0200 |
parents | |
children | 69e291f26bbd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/controllers/pwreset.go Wed Aug 01 12:29:55 2018 +0200 @@ -0,0 +1,125 @@ +package controllers + +import ( + "database/sql" + "encoding/hex" + "fmt" + "log" + "net/http" + "strings" + + "gemma.intevation.de/gemma/auth" + "gemma.intevation.de/gemma/config" + gomail "gopkg.in/gomail.v2" +) + +const ( + userExistsSQL = `SELECT email_address + FROM users.list_users WHERE username = $1 + LIMIT 1` +) + +const ( + mailTmpl = `You have requested a password change for your account on +%s://%s + +Please follow this link to get to the page where you can change your password. + +%s://%s/#/users/passwordreset/%s + +The link is only valid for one hour. + +Best regards + Your service team` +) + +func messageBody(https bool, hash, serverName string) string { + + var proto string + + if https { + proto = "https" + } else { + proto = "http" + } + + return fmt.Sprintf(mailTmpl, + proto, serverName, + proto, serverName, + hash) +} + +const hashLength = 32 + +func generateHash() string { + return hex.EncodeToString(auth.GenerateRandomKey(hashLength)) +} + +func passwordReset( + input interface{}, + req *http.Request, + db *sql.DB, +) (jr JSONResult, err error) { + + log.Println("passwordreset") + + user := input.(*PWResetUser) + + if user.User == "" { + err = JSONError{http.StatusBadRequest, "Invalid user name"} + return + } + + cfg := &config.Config + + if db, err = auth.OpenDB(cfg.ServiceUser, cfg.ServicePassword); err != nil { + return + } + defer db.Close() + + var email string + err = db.QueryRow(userExistsSQL, user.User).Scan(&email) + + switch { + case err == sql.ErrNoRows: + err = JSONError{http.StatusNotFound, "User does not exist."} + return + case err != nil: + return + } + + hash := generateHash() + + serverName := req.Host + useHTTPS := strings.ToLower(req.URL.Scheme) == "https" + + body := messageBody(useHTTPS, hash, serverName) + + m := gomail.NewMessage() + m.SetHeader("From", cfg.MailFrom) + m.SetHeader("To", email) + m.SetHeader("Subject", "Password Reset Link") + m.SetBody("text/plain", body) + + d := gomail.Dialer{ + Host: cfg.MailHost, + Port: int(cfg.MailPort), + Username: cfg.MailUser, + Password: cfg.MailPassword, + LocalName: cfg.MailHelo, + SSL: cfg.MailPort == 465, + } + + if err = d.DialAndSend(m); err != nil { + return + } + + // TODO: Keep hash/user for one hour or till resolved. + + jr.Result = &struct { + SendTo string `json:"send-to"` + }{ + SendTo: email, + } + return +}