comparison pkg/controllers/pwreset.go @ 501:c10c76c92797 metamorph-for-all

Use metamorphic database connections for auth.RunAs().
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 24 Aug 2018 15:30:31 +0200
parents b2dc9c2f69e0
children b96b1b258cfa
comparison
equal deleted inserted replaced
498:22e1bf563a04 501:c10c76c92797
1 package controllers 1 package controllers
2 2
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "context"
5 "database/sql" 6 "database/sql"
6 "encoding/hex" 7 "encoding/hex"
7 "log" 8 "log"
8 "net/http" 9 "net/http"
9 "os/exec" 10 "os/exec"
90 } 91 }
91 92
92 func removeOutdated() { 93 func removeOutdated() {
93 for { 94 for {
94 time.Sleep(cleanupPause) 95 time.Sleep(cleanupPause)
95 err := auth.RunAs(pwResetRole, func(db *sql.DB) error { 96 err := auth.RunAs(
96 good := time.Now().Add(-passwordResetValid) 97 pwResetRole, context.Background(),
97 _, err := db.Exec(cleanupRequestsSQL, good) 98 func(conn *sql.Conn) error {
98 return err 99 good := time.Now().Add(-passwordResetValid)
99 }) 100 _, err := conn.ExecContext(
101 context.Background(), cleanupRequestsSQL, good)
102 return err
103 })
100 if err != nil { 104 if err != nil {
101 log.Printf("error: %v\n", err) 105 log.Printf("error: %v\n", err)
102 } 106 }
103 } 107 }
104 } 108 }
175 return 179 return
176 } 180 }
177 181
178 var hash, email string 182 var hash, email string
179 183
180 if err = auth.RunAs(pwResetRole, func(db *sql.DB) error { 184 ctx := req.Context()
181 185
182 var count int64 186 if err = auth.RunAs(
183 if err := db.QueryRow(countRequestsSQL).Scan(&count); err != nil { 187 pwResetRole, ctx,
188 func(conn *sql.Conn) error {
189
190 var count int64
191 if err := conn.QueryRowContext(
192 ctx, countRequestsSQL).Scan(&count); err != nil {
193 return err
194 }
195
196 // Limit total number of password requests.
197 if count >= maxPasswordResets {
198 return JSONError{
199 Code: http.StatusServiceUnavailable,
200 Message: "Too much password reset request",
201 }
202 }
203
204 err := conn.QueryRowContext(ctx, userExistsSQL, user.User).Scan(&email)
205
206 switch {
207 case err == sql.ErrNoRows:
208 return JSONError{http.StatusNotFound, "User does not exist."}
209 case err != nil:
210 return err
211 }
212
213 if err := conn.QueryRowContext(
214 ctx, countRequestsUserSQL, user.User).Scan(&count); err != nil {
215 return err
216 }
217
218 // Limit requests per user
219 if count >= maxPasswordRequestsPerUser {
220 return JSONError{
221 Code: http.StatusServiceUnavailable,
222 Message: "Too much password reset requests for user",
223 }
224 }
225
226 hash = generateHash()
227 _, err = conn.ExecContext(ctx, insertRequestSQL, hash, user.User)
184 return err 228 return err
185 } 229 }); err == nil {
186
187 // Limit total number of password requests.
188 if count >= maxPasswordResets {
189 return JSONError{
190 Code: http.StatusServiceUnavailable,
191 Message: "Too much password reset request",
192 }
193 }
194
195 err := db.QueryRow(userExistsSQL, user.User).Scan(&email)
196
197 switch {
198 case err == sql.ErrNoRows:
199 return JSONError{http.StatusNotFound, "User does not exist."}
200 case err != nil:
201 return err
202 }
203
204 if err := db.QueryRow(countRequestsUserSQL, user.User).Scan(&count); err != nil {
205 return err
206 }
207
208 // Limit requests per user
209 if count >= maxPasswordRequestsPerUser {
210 return JSONError{
211 Code: http.StatusServiceUnavailable,
212 Message: "Too much password reset requests for user",
213 }
214 }
215
216 hash = generateHash()
217 _, err = db.Exec(insertRequestSQL, hash, user.User)
218 return err
219 }); err == nil {
220 body := requestMessageBody(useHTTPS(req), user.User, hash, req.Host) 230 body := requestMessageBody(useHTTPS(req), user.User, hash, req.Host)
221 231
222 if err = misc.SendMail(email, "Password Reset Link", body); err == nil { 232 if err = misc.SendMail(email, "Password Reset Link", body); err == nil {
223 jr.Result = &struct { 233 jr.Result = &struct {
224 SendTo string `json:"send-to"` 234 SendTo string `json:"send-to"`
240 return 250 return
241 } 251 }
242 252
243 var email, user, password string 253 var email, user, password string
244 254
245 if err = auth.RunAs(pwResetRole, func(db *sql.DB) error { 255 ctx := req.Context()
246 err := db.QueryRow(findRequestSQL, hash).Scan(&email, &user) 256
247 switch { 257 if err = auth.RunAs(
248 case err == sql.ErrNoRows: 258 pwResetRole, ctx, func(conn *sql.Conn) error {
249 return JSONError{http.StatusNotFound, "No such hash"} 259 err := conn.QueryRowContext(ctx, findRequestSQL, hash).Scan(&email, &user)
250 case err != nil: 260 switch {
261 case err == sql.ErrNoRows:
262 return JSONError{http.StatusNotFound, "No such hash"}
263 case err != nil:
264 return err
265 }
266 password = generateNewPassword()
267 res, err := conn.ExecContext(ctx, updatePasswordSQL, password, user)
268 if err != nil {
269 return err
270 }
271 if n, err2 := res.RowsAffected(); err2 == nil && n == 0 {
272 return JSONError{http.StatusNotFound, "User not found"}
273 }
274 _, err = conn.ExecContext(ctx, deleteRequestSQL, hash)
251 return err 275 return err
252 } 276 }); err == nil {
253 password = generateNewPassword()
254 res, err := db.Exec(updatePasswordSQL, password, user)
255 if err != nil {
256 return err
257 }
258 if n, err2 := res.RowsAffected(); err2 == nil && n == 0 {
259 return JSONError{http.StatusNotFound, "User not found"}
260 }
261 _, err = db.Exec(deleteRequestSQL, hash)
262 return err
263 }); err == nil {
264 body := changedMessageBody(useHTTPS(req), user, password, req.Host) 277 body := changedMessageBody(useHTTPS(req), user, password, req.Host)
265 if err = misc.SendMail(email, "Password Reset Done", body); err == nil { 278 if err = misc.SendMail(email, "Password Reset Done", body); err == nil {
266 jr.Result = &struct { 279 jr.Result = &struct {
267 SendTo string `json:"send-to"` 280 SendTo string `json:"send-to"`
268 }{email} 281 }{email}