github.com/bitcubate/cryptojournal@v1.2.5-0.20171102134152-f578b3d788ab/src/users/actions/password.go (about) 1 package useractions 2 3 import ( 4 "fmt" 5 "net/http" 6 "time" 7 8 "github.com/fragmenta/auth" 9 "github.com/fragmenta/mux" 10 "github.com/fragmenta/query" 11 "github.com/fragmenta/server" 12 "github.com/fragmenta/server/config" 13 "github.com/fragmenta/server/log" 14 "github.com/fragmenta/view" 15 16 "github.com/bitcubate/cryptojournal/src/lib/mail" 17 "github.com/bitcubate/cryptojournal/src/lib/session" 18 "github.com/bitcubate/cryptojournal/src/users" 19 ) 20 21 const ( 22 // ResetLifetime is the maximum time reset tokens are valid for 23 ResetLifetime = time.Hour 24 ) 25 26 // HandlePasswordResetShow responds to GET /users/password/reset 27 // by showing the password reset page. 28 func HandlePasswordResetShow(w http.ResponseWriter, r *http.Request) error { 29 // No authorisation required, just show the view 30 view := view.NewRenderer(w, r) 31 view.Template("users/views/password_reset.html.got") 32 return view.Render() 33 } 34 35 // HandlePasswordResetSend responds to POST /users/password/reset 36 // by sending a password reset email. 37 func HandlePasswordResetSend(w http.ResponseWriter, r *http.Request) error { 38 39 // No authorisation required 40 // Check the authenticity token 41 err := session.CheckAuthenticity(w, r) 42 if err != nil { 43 return server.NotAuthorizedError(err, "Invalid authenticity token") 44 } 45 46 params, err := mux.Params(r) 47 if err != nil { 48 return server.InternalError(err) 49 } 50 51 // Find the user by email (if not found let them know) 52 // Find the user by hex token in the db 53 email := params.Get("email") 54 user, err := users.FindFirst("email=?", email) 55 if err != nil { 56 return server.Redirect(w, r, "/users/password/reset?message=invalid_email") 57 } 58 59 // Generate a random token and url for the email 60 token := auth.BytesToHex(auth.RandomToken(32)) 61 62 // Update the user record with with this token 63 userParams := map[string]string{ 64 "password_reset_token": token, 65 "password_reset_at": query.TimeString(time.Now().UTC()), 66 } 67 // Direct access to the user columns, bypassing validation 68 user.Update(userParams) 69 70 // Generate the url to use in our email 71 url := fmt.Sprintf("%s/users/password?token=%s", config.Get("root_url"), token) 72 73 // Send a password reset email out to this user 74 emailContext := map[string]interface{}{ 75 "url": url, 76 "name": user.Name, 77 } 78 79 log.Info(log.V{"msg": "sending reset email", "user_email": user.Email, "user_id": user.ID}) 80 81 e := mail.New(user.Email) 82 e.Subject = "Reset Password" 83 e.Template = "users/views/password_reset_mail.html.got" 84 err = mail.Send(e, emailContext) 85 if err != nil { 86 return err 87 } 88 89 // Tell the user what we have done 90 return server.Redirect(w, r, "/users/password/sent") 91 } 92 93 // HandlePasswordResetSentShow responds to GET /users/password/sent 94 func HandlePasswordResetSentShow(w http.ResponseWriter, r *http.Request) error { 95 view := view.NewRenderer(w, r) 96 view.Template("users/views/password_sent.html.got") 97 return view.Render() 98 } 99 100 // HandlePasswordReset responds to GET /users/password?token=DEADFISH 101 // by logging the user in, removing the token 102 // and allowing them to set their password. 103 func HandlePasswordReset(w http.ResponseWriter, r *http.Request) error { 104 105 params, err := mux.Params(r) 106 if err != nil { 107 return server.InternalError(err) 108 } 109 110 // Note we have no authenticity check, just a random token to check 111 token := params.Get("token") 112 if len(token) < 10 || len(token) > 64 { 113 return server.NotAuthorizedError(fmt.Errorf("Invalid reset token"), "Invalid Token") 114 } 115 116 // Find the user by hex token in the db 117 user, err := users.FindFirst("password_reset_token=?", token) 118 if err != nil { 119 return server.NotAuthorizedError(err) 120 } 121 122 // Make sure the reset at time is less expire time 123 if time.Since(user.PasswordResetAt) > ResetLifetime { 124 return server.NotAuthorizedError(nil, "Token invalid", "Your password reset token has expired, please request another.") 125 } 126 127 // Remove the reset token from this user 128 // using direct access, bypassing validation 129 user.Update(map[string]string{"password_reset_token": ""}) 130 131 // Log in the user and store in the session 132 // Now save the user details in a secure cookie, so that we remember the next request 133 // Build the session from the secure cookie, or create a new one 134 session, err := auth.Session(w, r) 135 if err != nil { 136 return server.NotAuthorizedError(err) 137 } 138 139 // Save login the session cookie 140 session.Set(auth.SessionUserKey, fmt.Sprintf("%d", user.ID)) 141 session.Save(w) 142 143 // Log action 144 log.Info(log.V{"msg": "reset password", "user_email": user.Email, "user_id": user.ID}) 145 146 // Redirect to the user update page so that they can change their password 147 return server.Redirect(w, r, fmt.Sprintf("/users/%d/update", user.ID)) 148 }