Skip to content

Commit

Permalink
Implement POST /users/email/verify/send endpoint for APIv4
Browse files Browse the repository at this point in the history
  • Loading branch information
jwilander committed Mar 21, 2017
1 parent fd6e2f3 commit c71bd65
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 16 deletions.
40 changes: 33 additions & 7 deletions api4/user.go
Expand Up @@ -34,7 +34,8 @@ func InitUser() {
BaseRoutes.User.Handle("/password", ApiSessionRequired(updatePassword)).Methods("PUT")
BaseRoutes.Users.Handle("/password/reset", ApiHandler(resetPassword)).Methods("POST")
BaseRoutes.Users.Handle("/password/reset/send", ApiHandler(sendPasswordReset)).Methods("POST")
BaseRoutes.User.Handle("/email/verify", ApiHandler(verify)).Methods("POST")
BaseRoutes.Users.Handle("/email/verify", ApiHandler(verifyUserEmail)).Methods("POST")
BaseRoutes.Users.Handle("/email/verify/send", ApiHandler(sendVerificationEmail)).Methods("POST")

BaseRoutes.Users.Handle("/login", ApiHandler(login)).Methods("POST")
BaseRoutes.Users.Handle("/logout", ApiHandler(logout)).Methods("POST")
Expand Down Expand Up @@ -740,18 +741,18 @@ func getUserAudits(c *Context, w http.ResponseWriter, r *http.Request) {
}
}

func verify(c *Context, w http.ResponseWriter, r *http.Request) {
func verifyUserEmail(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)

userId := props["uid"]
userId := props["user_id"]
if len(userId) != 26 {
c.SetInvalidParam("uid")
c.SetInvalidParam("user_id")
return
}

hashedId := props["hid"]
hashedId := props["hash_id"]
if len(hashedId) == 0 {
c.SetInvalidParam("hid")
c.SetInvalidParam("hash_id")
return
}

Expand All @@ -766,7 +767,32 @@ func verify(c *Context, w http.ResponseWriter, r *http.Request) {
}
}

c.Err = model.NewLocAppError("verifyEmail", "api.user.verify_email.bad_link.app_error", nil, "")
c.Err = model.NewLocAppError("verifyUserEmail", "api.user.verify_email.bad_link.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}

func sendVerificationEmail(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)

email := props["email"]
if len(email) == 0 {
c.SetInvalidParam("email")
return
}

user, err := app.GetUserForLogin(email, false)
if err != nil {
// Don't want to leak whether the email is valid or not
ReturnStatusOK(w)
return
}

if _, err := app.GetStatus(user.Id); err != nil {
go app.SendVerifyEmail(user.Id, user.Email, user.Locale, c.GetSiteURL())
} else {
go app.SendEmailChangeVerifyEmail(user.Id, user.Email, user.Locale, c.GetSiteURL())
}

ReturnStatusOK(w)
}
31 changes: 25 additions & 6 deletions api4/user_test.go
Expand Up @@ -1139,7 +1139,7 @@ func TestGetUserAudits(t *testing.T) {
CheckNoError(t, resp)
}

func TestVerify(t *testing.T) {
func TestVerifyUserEmail(t *testing.T) {
th := Setup().InitBasic()
defer TearDown()
Client := th.Client
Expand All @@ -1156,15 +1156,34 @@ func TestVerify(t *testing.T) {
_, resp = Client.VerifyUserEmail(ruser.Id, hashId)
CheckBadRequestStatus(t, resp)

// Comment per request from Joram, he will investigate why it fail with a wrong status
// hashId = ruser.Id+GenerateTestId()
// _, resp = Client.VerifyUserEmail("", hashId)
// CheckBadRequestStatus(t, resp)

_, resp = Client.VerifyUserEmail(ruser.Id, "")
CheckBadRequestStatus(t, resp)
}

func TestSendVerificationEmail(t *testing.T) {
th := Setup().InitBasic()
defer TearDown()
Client := th.Client

pass, resp := Client.SendVerificationEmail(th.BasicUser.Email)
CheckNoError(t, resp)

if !pass {
t.Fatal("should have passed")
}

_, resp = Client.SendVerificationEmail("")
CheckBadRequestStatus(t, resp)

// Even non-existent emails should return 200 OK
_, resp = Client.SendVerificationEmail(GenerateTestEmail())
CheckNoError(t, resp)

Client.Logout()
_, resp = Client.SendVerificationEmail(th.BasicUser.Email)
CheckNoError(t, resp)
}

func TestSetProfileImage(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
defer TearDown()
Expand Down
19 changes: 16 additions & 3 deletions model/client4.go
Expand Up @@ -617,10 +617,23 @@ func (c *Client4) GetUserAudits(userId string, page int, perPage int, etag strin
}
}

// Verify user email user id and hash strings.
// VerifyUserEmail will verify a user's email using user id and hash strings.
func (c *Client4) VerifyUserEmail(userId, hashId string) (bool, *Response) {
requestBody := map[string]string{"uid": userId, "hid": hashId}
if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/email/verify", MapToJson(requestBody)); err != nil {
requestBody := map[string]string{"user_id": userId, "hash_id": hashId}
if r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify", MapToJson(requestBody)); err != nil {
return false, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
}

// SendVerificationEmail will send an email to the user with the provided email address, if
// that user exists. The email will contain a link that can be used to verify the user's
// email address.
func (c *Client4) SendVerificationEmail(email string) (bool, *Response) {
requestBody := map[string]string{"email": email}
if r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify/send", MapToJson(requestBody)); err != nil {
return false, &Response{StatusCode: r.StatusCode, Error: err}
} else {
defer closeBody(r)
Expand Down

0 comments on commit c71bd65

Please sign in to comment.