comparison 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
comparison
equal deleted inserted replaced
301:1781e5d7cb5a 302:0777aa6de45b
1 package controllers
2
3 import (
4 "database/sql"
5 "encoding/hex"
6 "fmt"
7 "log"
8 "net/http"
9 "strings"
10
11 "gemma.intevation.de/gemma/auth"
12 "gemma.intevation.de/gemma/config"
13 gomail "gopkg.in/gomail.v2"
14 )
15
16 const (
17 userExistsSQL = `SELECT email_address
18 FROM users.list_users WHERE username = $1
19 LIMIT 1`
20 )
21
22 const (
23 mailTmpl = `You have requested a password change for your account on
24 %s://%s
25
26 Please follow this link to get to the page where you can change your password.
27
28 %s://%s/#/users/passwordreset/%s
29
30 The link is only valid for one hour.
31
32 Best regards
33 Your service team`
34 )
35
36 func messageBody(https bool, hash, serverName string) string {
37
38 var proto string
39
40 if https {
41 proto = "https"
42 } else {
43 proto = "http"
44 }
45
46 return fmt.Sprintf(mailTmpl,
47 proto, serverName,
48 proto, serverName,
49 hash)
50 }
51
52 const hashLength = 32
53
54 func generateHash() string {
55 return hex.EncodeToString(auth.GenerateRandomKey(hashLength))
56 }
57
58 func passwordReset(
59 input interface{},
60 req *http.Request,
61 db *sql.DB,
62 ) (jr JSONResult, err error) {
63
64 log.Println("passwordreset")
65
66 user := input.(*PWResetUser)
67
68 if user.User == "" {
69 err = JSONError{http.StatusBadRequest, "Invalid user name"}
70 return
71 }
72
73 cfg := &config.Config
74
75 if db, err = auth.OpenDB(cfg.ServiceUser, cfg.ServicePassword); err != nil {
76 return
77 }
78 defer db.Close()
79
80 var email string
81 err = db.QueryRow(userExistsSQL, user.User).Scan(&email)
82
83 switch {
84 case err == sql.ErrNoRows:
85 err = JSONError{http.StatusNotFound, "User does not exist."}
86 return
87 case err != nil:
88 return
89 }
90
91 hash := generateHash()
92
93 serverName := req.Host
94 useHTTPS := strings.ToLower(req.URL.Scheme) == "https"
95
96 body := messageBody(useHTTPS, hash, serverName)
97
98 m := gomail.NewMessage()
99 m.SetHeader("From", cfg.MailFrom)
100 m.SetHeader("To", email)
101 m.SetHeader("Subject", "Password Reset Link")
102 m.SetBody("text/plain", body)
103
104 d := gomail.Dialer{
105 Host: cfg.MailHost,
106 Port: int(cfg.MailPort),
107 Username: cfg.MailUser,
108 Password: cfg.MailPassword,
109 LocalName: cfg.MailHelo,
110 SSL: cfg.MailPort == 465,
111 }
112
113 if err = d.DialAndSend(m); err != nil {
114 return
115 }
116
117 // TODO: Keep hash/user for one hour or till resolved.
118
119 jr.Result = &struct {
120 SendTo string `json:"send-to"`
121 }{
122 SendTo: email,
123 }
124 return
125 }