Skip to content
This repository was archived by the owner on Sep 2, 2024. It is now read-only.

Commit 238b11f

Browse files
committed
added reset password flow
1 parent 1c8a3a6 commit 238b11f

File tree

4 files changed

+78
-87
lines changed

4 files changed

+78
-87
lines changed

function/runtime.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,6 @@ func (env *ExecutionEnvironment) addVolatileFunctions(vm *goja.Runtime) {
328328
if err != nil {
329329
return vm.ToValue(Result{Content: fmt.Sprintf("error converting your data: %v", err)})
330330
}
331-
fmt.Println("DEBUG: ", string(b))
332331

333332
msg := internal.Command{
334333
SID: env.Data.ID.Hex(),

internal/account.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package internal
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"strings"
78
"time"
@@ -50,6 +51,7 @@ type Token struct {
5051
Email string `bson:"email" json:"email"`
5152
Password string `bson:"pw" json:"-"`
5253
Role int `bson:"role" json:"role"`
54+
ResetCode string `bson:"resetCode" json:"-"`
5355
}
5456

5557
type Login struct {
@@ -107,6 +109,26 @@ func FindTokenByEmail(db *mongo.Database, email string) (tok Token, err error) {
107109
return
108110
}
109111

112+
func SetPasswordResetCode(db *mongo.Database, id primitive.ObjectID, code string) error {
113+
update := bson.M{"$set": bson.M{"resetCode": code}}
114+
if _, err := db.Collection("sb_tokens").UpdateByID(ctx, id, update); err != nil {
115+
return err
116+
}
117+
return nil
118+
}
119+
120+
func ResetPassword(db *mongo.Database, email, code, password string) error {
121+
filter := bson.M{"email": email, "resetCode": code}
122+
update := bson.M{"$set": bson.M{"pw": password}}
123+
res, err := db.Collection("sb_tokens").UpdateOne(ctx, filter, update)
124+
if err != nil {
125+
return err
126+
} else if res.ModifiedCount != 1 {
127+
return errors.New("cannot find document")
128+
}
129+
return nil
130+
}
131+
110132
func FindAccount(db *mongo.Database, accountID primitive.ObjectID) (cus Customer, err error) {
111133
filter := bson.M{FieldID: accountID}
112134
sr := db.Collection("accounts").FindOne(ctx, filter)

membership.go

Lines changed: 54 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"time"
1212

1313
"staticbackend/cache"
14-
"staticbackend/email"
1514
"staticbackend/internal"
1615
"staticbackend/middleware"
1716

@@ -246,165 +245,134 @@ func (m *membership) createUser(db *mongo.Database, accountID primitive.ObjectID
246245
return jwtBytes, tok, nil
247246
}
248247

249-
func (m *membership) setRole(w http.ResponseWriter, r *http.Request) {
250-
conf, a, err := middleware.Extract(r, true)
251-
if err != nil || a.Role < 100 {
252-
http.Error(w, "insufficient priviledges", http.StatusUnauthorized)
248+
func (m *membership) setResetCode(w http.ResponseWriter, r *http.Request) {
249+
email := strings.ToLower(r.URL.Query().Get("e"))
250+
if len(email) == 0 || strings.Index(email, "@") <= 0 {
251+
http.Error(w, "invalid email", http.StatusBadRequest)
253252
return
254253
}
255254

256-
var data = new(struct {
257-
Email string `json:"email"`
258-
Role int `json:"role"`
259-
})
260-
if err := parseBody(r.Body, &data); err != nil {
261-
http.Error(w, err.Error(), http.StatusBadRequest)
255+
code := randStringRunes(10)
256+
257+
conf, _, err := middleware.Extract(r, false)
258+
if err != nil {
259+
http.Error(w, err.Error(), http.StatusUnauthorized)
262260
return
263261
}
264262

265-
db := client.Database(conf.Name)
263+
curDB := client.Database(conf.Name)
266264

267-
ctx, _ := context.WithTimeout(context.Background(), 2*time.Second)
268-
filter := bson.M{"email": data.Email}
269-
update := bson.M{"$set": bson.M{"role": data.Role}}
270-
if _, err := db.Collection("sb_tokens").UpdateOne(ctx, filter, update); err != nil {
265+
tok, err := internal.FindTokenByEmail(curDB, email)
266+
if err != nil {
267+
http.Error(w, "email not found", http.StatusNotFound)
268+
return
269+
}
270+
271+
if err := internal.SetPasswordResetCode(curDB, tok.ID, code); err != nil {
271272
http.Error(w, err.Error(), http.StatusInternalServerError)
272273
return
273274
}
274275

275-
respond(w, http.StatusOK, true)
276+
respond(w, http.StatusOK, code)
276277
}
277278

278-
func (m *membership) setPassword(w http.ResponseWriter, r *http.Request) {
279-
conf, a, err := middleware.Extract(r, true)
280-
if err != nil || a.Role < 100 {
281-
http.Error(w, "insufficient priviledges", http.StatusUnauthorized)
279+
func (m *membership) resetPassword(w http.ResponseWriter, r *http.Request) {
280+
conf, _, err := middleware.Extract(r, false)
281+
if err != nil {
282+
http.Error(w, err.Error(), http.StatusUnauthorized)
282283
return
283284
}
284285

286+
curDB := client.Database(conf.Name)
287+
285288
var data = new(struct {
286-
Email string `json:"email"`
287-
OldPassword string `json:"oldPassword"`
288-
NewPassword string `json:"newPassword"`
289+
Email string `json:"email"`
290+
Code string `json:"code"`
291+
Password string `json:"password"`
289292
})
290293
if err := parseBody(r.Body, &data); err != nil {
291294
http.Error(w, err.Error(), http.StatusBadRequest)
292295
return
293296
}
294297

295-
db := client.Database(conf.Name)
298+
data.Email = strings.ToLower(data.Email)
296299

297-
tok, err := m.validateUserPassword(db, data.Email, data.OldPassword)
298-
if err != nil {
299-
http.Error(w, err.Error(), http.StatusUnauthorized)
300-
return
301-
}
302-
303-
newpw, err := bcrypt.GenerateFromPassword([]byte(data.NewPassword), bcrypt.DefaultCost)
300+
b, err := bcrypt.GenerateFromPassword([]byte(data.Password), bcrypt.DefaultCost)
304301
if err != nil {
305302
http.Error(w, err.Error(), http.StatusInternalServerError)
306303
return
307304
}
308305

309-
ctx := context.Background()
310-
filter := bson.M{"_id": tok.ID}
311-
update := bson.M{"$set": bson.M{"pw": string(newpw)}}
312-
if _, err := db.Collection("sb_tokens").UpdateOne(ctx, filter, update); err != nil {
306+
if err := internal.ResetPassword(curDB, data.Email, data.Code, string(b)); err != nil {
313307
http.Error(w, err.Error(), http.StatusInternalServerError)
314308
return
315309
}
316310

317311
respond(w, http.StatusOK, true)
318312
}
319313

320-
func (m *membership) resetPassword(w http.ResponseWriter, r *http.Request) {
321-
conf, _, err := middleware.Extract(r, false)
322-
if err != nil {
323-
http.Error(w, "invalid StaticBackend key", http.StatusUnauthorized)
314+
func (m *membership) setRole(w http.ResponseWriter, r *http.Request) {
315+
conf, a, err := middleware.Extract(r, true)
316+
if err != nil || a.Role < 100 {
317+
http.Error(w, "insufficient priviledges", http.StatusUnauthorized)
324318
return
325319
}
326320

327-
db := client.Database(conf.Name)
328-
329321
var data = new(struct {
330322
Email string `json:"email"`
323+
Role int `json:"role"`
331324
})
332325
if err := parseBody(r.Body, &data); err != nil {
333326
http.Error(w, err.Error(), http.StatusBadRequest)
334327
return
335328
}
336329

337-
filter := bson.M{"email": strings.ToLower(data.Email)}
338-
count, err := db.Collection("sb_tokens").CountDocuments(context.Background(), filter)
339-
if err != nil {
340-
http.Error(w, err.Error(), http.StatusInternalServerError)
341-
return
342-
} else if count == 0 {
343-
http.Error(w, "not found", http.StatusNotFound)
344-
return
345-
}
346-
347-
code := randStringRunes(6)
348-
update := bson.M{"%set": bson.M{"sb_reset_code": code}}
349-
if _, err := db.Collection("sb_tokens").UpdateOne(context.Background(), filter, update); err != nil {
350-
http.Error(w, err.Error(), http.StatusInternalServerError)
351-
return
352-
}
353-
354-
//TODO: have HTML template for those
355-
body := fmt.Sprintf(`Your reset code is: %s`, code)
330+
db := client.Database(conf.Name)
356331

357-
ed := internal.SendMailData{
358-
From: FromEmail,
359-
FromName: FromName,
360-
To: data.Email,
361-
Subject: "Your password reset code",
362-
HTMLBody: body,
363-
TextBody: email.StripHTML(body),
364-
}
365-
if err := emailer.Send(ed); err != nil {
332+
ctx, _ := context.WithTimeout(context.Background(), 2*time.Second)
333+
filter := bson.M{"email": data.Email}
334+
update := bson.M{"$set": bson.M{"role": data.Role}}
335+
if _, err := db.Collection("sb_tokens").UpdateOne(ctx, filter, update); err != nil {
366336
http.Error(w, err.Error(), http.StatusInternalServerError)
367337
return
368338
}
369339

370340
respond(w, http.StatusOK, true)
371341
}
372342

373-
func (m *membership) changePassword(w http.ResponseWriter, r *http.Request) {
374-
conf, _, err := middleware.Extract(r, false)
375-
if err != nil {
376-
http.Error(w, "invalid StaticBackend key", http.StatusUnauthorized)
343+
func (m *membership) setPassword(w http.ResponseWriter, r *http.Request) {
344+
conf, a, err := middleware.Extract(r, true)
345+
if err != nil || a.Role < 100 {
346+
http.Error(w, "insufficient priviledges", http.StatusUnauthorized)
377347
return
378348
}
379349

380-
db := client.Database(conf.Name)
381-
382350
var data = new(struct {
383-
Email string `json:"email"`
384-
Code string `json:"code"`
385-
Password string `json:"password"`
351+
Email string `json:"email"`
352+
OldPassword string `json:"oldPassword"`
353+
NewPassword string `json:"newPassword"`
386354
})
387355
if err := parseBody(r.Body, &data); err != nil {
388356
http.Error(w, err.Error(), http.StatusBadRequest)
389357
return
390358
}
391359

392-
filter := bson.M{"email": strings.ToLower(data.Email), "sb_reset_code": data.Code}
393-
var tok internal.Token
394-
sr := db.Collection("sb_tokens").FindOne(context.Background(), filter)
395-
if err := sr.Decode(&tok); err != nil {
396-
http.Error(w, err.Error(), http.StatusInternalServerError)
360+
db := client.Database(conf.Name)
361+
362+
tok, err := m.validateUserPassword(db, data.Email, data.OldPassword)
363+
if err != nil {
364+
http.Error(w, err.Error(), http.StatusUnauthorized)
397365
return
398366
}
399367

400-
newpw, err := bcrypt.GenerateFromPassword([]byte(data.Password), bcrypt.DefaultCost)
368+
newpw, err := bcrypt.GenerateFromPassword([]byte(data.NewPassword), bcrypt.DefaultCost)
401369
if err != nil {
402370
http.Error(w, err.Error(), http.StatusInternalServerError)
403371
return
404372
}
405373

406374
ctx := context.Background()
407-
filter = bson.M{internal.FieldID: tok.ID}
375+
filter := bson.M{"_id": tok.ID}
408376
update := bson.M{"$set": bson.M{"pw": string(newpw)}}
409377
if _, err := db.Collection("sb_tokens").UpdateOne(ctx, filter, update); err != nil {
410378
http.Error(w, err.Error(), http.StatusInternalServerError)

server.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ func Start(dbHost, port string) {
124124
http.Handle("/login", middleware.Chain(http.HandlerFunc(m.login), pubWithDB...))
125125
http.Handle("/register", middleware.Chain(http.HandlerFunc(m.register), pubWithDB...))
126126
http.Handle("/email", middleware.Chain(http.HandlerFunc(m.emailExists), pubWithDB...))
127+
http.Handle("/password/resetcode", middleware.Chain(http.HandlerFunc(m.setResetCode), pubWithDB...))
128+
http.Handle("/password/reset", middleware.Chain(http.HandlerFunc(m.resetPassword), pubWithDB...))
127129
//http.Handle("/setrole", chain(http.HandlerFunc(setRole), withDB))
128130

129131
http.Handle("/sudogettoken/", middleware.Chain(http.HandlerFunc(m.sudoGetTokenFromAccountID), stdRoot...))

0 commit comments

Comments
 (0)