func TestResetPasswordHandlerTokenMatchestInvalidPasswordReset(t *testing.T) { pool := newConnPool(t) _, localhost, _ := net.ParseCIDR("127.0.0.1/32") pwr := &data.PasswordReset{ Token: data.NewString("0123456789abcdef"), Email: data.NewString("*****@*****.**"), RequestTime: data.NewTime(time.Now()), RequestIP: data.NewIPNet(*localhost), } err := data.InsertPasswordReset(pool, pwr) if err != nil { t.Fatalf("repo.CreatePasswordReset returned error: %v", err) } buf := bytes.NewBufferString(`{"token": "0123456789abcdef", "password": "******"}`) req, err := http.NewRequest("POST", "http://example.com/", buf) if err != nil { t.Fatalf("http.NewRequest returned error: %v", err) } env := &environment{pool: pool} w := httptest.NewRecorder() ResetPasswordHandler(w, req, env) if w.Code != 404 { t.Errorf("Expected HTTP status %d, instead received %d", 404, w.Code) } }
func TestResetPasswordHandlerTokenMatchestUsedPasswordReset(t *testing.T) { pool := newConnPool(t) user := &data.User{ Name: data.NewString("test"), Email: data.NewString("*****@*****.**"), } SetPassword(user, "password") userID, err := data.CreateUser(pool, user) if err != nil { t.Fatalf("repo.CreateUser returned error: %v", err) } _, localhost, _ := net.ParseCIDR("127.0.0.1/32") pwr := &data.PasswordReset{ Token: data.NewString("0123456789abcdef"), Email: data.NewString("*****@*****.**"), UserID: data.NewInt32(userID), RequestTime: data.NewTime(time.Now()), RequestIP: data.NewIPNet(*localhost), CompletionTime: data.NewTime(time.Now()), CompletionIP: data.NewIPNet(*localhost), } err = data.InsertPasswordReset(pool, pwr) if err != nil { t.Fatalf("repo.CreatePasswordReset returned error: %v", err) } buf := bytes.NewBufferString(`{"token": "0123456789abcdef", "password": "******"}`) req, err := http.NewRequest("POST", "http://example.com/", buf) if err != nil { t.Fatalf("http.NewRequest returned error: %v", err) } env := &environment{pool: pool} w := httptest.NewRecorder() ResetPasswordHandler(w, req, env) if w.Code != 404 { t.Errorf("Expected HTTP status %d, instead received %d", 404, w.Code) } user, err = data.SelectUserByPK(pool, userID) if err != nil { t.Fatalf("repo.GetUser returned error: %v", err) } if IsPassword(user, "bigsecret") { t.Error("Expected password not to be changed but it was") } }
func TestResetPasswordHandlerTokenMatchestValidPasswordReset(t *testing.T) { pool := newConnPool(t) user := &data.User{ Name: data.NewString("test"), Email: data.NewString("*****@*****.**"), } SetPassword(user, "password") userID, err := data.CreateUser(pool, user) if err != nil { t.Fatalf("repo.CreateUser returned error: %v", err) } _, requestIP, _ := net.ParseCIDR("127.0.0.1/32") pwr := &data.PasswordReset{ Token: data.NewString("0123456789abcdef"), Email: data.NewString("*****@*****.**"), UserID: data.NewInt32(userID), RequestTime: data.NewTime(time.Now()), RequestIP: data.NewIPNet(*requestIP), } err = data.InsertPasswordReset(pool, pwr) if err != nil { t.Fatalf("repo.CreatePasswordReset returned error: %v", err) } buf := bytes.NewBufferString(`{"token": "0123456789abcdef", "password": "******"}`) req, err := http.NewRequest("POST", "http://example.com/", buf) if err != nil { t.Fatalf("http.NewRequest returned error: %v", err) } env := &environment{pool: pool} w := httptest.NewRecorder() ResetPasswordHandler(w, req, env) if w.Code != 200 { t.Errorf("Expected HTTP status %d, instead received %d", 200, w.Code) } user, err = data.SelectUserByPK(pool, userID) if err != nil { t.Fatalf("repo.GetUser returned error: %v", err) } if !IsPassword(user, "bigsecret") { t.Error("Expected password to be changed but it was not") } var response struct { Name string `json:"name"` SessionID string `json:"sessionID"` } decoder := json.NewDecoder(w.Body) if err := decoder.Decode(&response); err != nil { t.Errorf("Unable to decode response: %v", err) } }
func RequestPasswordResetHandler(w http.ResponseWriter, req *http.Request, env *environment) { pwr := &data.PasswordReset{} pwr.RequestTime = data.NewTime(time.Now()) if host, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { if ip := net.ParseIP(host); ip != nil { mask := net.CIDRMask(len(ip)*8, len(ip)*8) pwr.RequestIP = data.NewIPNet(net.IPNet{IP: ip, Mask: mask}) } } token, err := genLostPasswordToken() if err != nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("getLostPasswordToken failed", "error", err) return } pwr.Token = data.NewString(token) var reset struct { Email string `json:"email"` } decoder := json.NewDecoder(req.Body) if err := decoder.Decode(&reset); err != nil { w.WriteHeader(422) fmt.Fprintf(w, "Error decoding request: %v", err) return } if reset.Email == "" { w.WriteHeader(422) fmt.Fprint(w, "Error decoding request: missing email") return } pwr.Email = data.NewString(reset.Email) user, err := data.SelectUserByEmail(env.pool, reset.Email) switch err { case nil: pwr.UserID = user.ID case data.ErrNotFound: default: w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) return } err = data.InsertPasswordReset(env.pool, pwr) if err != nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("repo.CreatePasswordReset failed", "error", err) return } if user == nil { env.logger.Warn("Password reset requested for missing email", "email", reset.Email) return } if env.mailer == nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("Mail is not configured -- cannot send password reset email") return } err = env.mailer.SendPasswordResetMail(reset.Email, token) if err != nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("env.mailer.SendPasswordResetMail failed", "error", err) return } }