Add changing email for users

This commit is contained in:
kolaente 2020-04-17 16:01:45 +02:00
parent e1ab2095fa
commit 1dca8e96a7
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
6 changed files with 164 additions and 7 deletions

View File

@ -0,0 +1,72 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package v1
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler"
"fmt"
"github.com/labstack/echo/v4"
"net/http"
)
// UpdateUserEmail is the handler to let a user update their email address.
// @Summary Update email address
// @Description Lets the current user change their email address.
// @tags user
// @Accept json
// @Produce json
// @Param userEmailUpdate body user.EmailUpdate true "The new email address and current password."
// @Security JWTKeyAuth
// @Success 200 {object} models.Message
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Something's invalid."
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
// @Failure 500 {object} models.Message "Internal server error."
// @Router /user/settings/email [post]
func UpdateUserEmail(c echo.Context) (err error) {
var emailUpdate = &user.EmailUpdate{}
if err := c.Bind(emailUpdate); err != nil {
log.Debugf("Invalid model error. Internal error was: %s", err.Error())
if he, is := err.(*echo.HTTPError); is {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided. Error was: %s", he.Message))
}
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid model provided."))
}
emailUpdate.User, err = user.GetCurrentUser(c)
if err != nil {
return handler.HandleHTTPError(err, c)
}
emailUpdate.User, err = user.CheckUserCredentials(&user.Login{
Username: emailUpdate.User.Username,
Password: emailUpdate.Password,
})
if err != nil {
return handler.HandleHTTPError(err, c)
}
err = user.UpdateEmail(emailUpdate)
if err != nil {
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, models.Message{Message: "We sent you email with a link to confirm your email address."})
}

View File

@ -206,6 +206,7 @@ func registerAPIRoutes(a *echo.Group) {
a.POST("/user/password", apiv1.UserChangePassword) a.POST("/user/password", apiv1.UserChangePassword)
a.GET("/users", apiv1.UserList) a.GET("/users", apiv1.UserList)
a.POST("/user/token", apiv1.RenewToken) a.POST("/user/token", apiv1.RenewToken)
a.POST("/user/settings/email", apiv1.UpdateUserEmail)
listHandler := &handler.WebHandler{ listHandler := &handler.WebHandler{
EmptyStruct: func() handler.CObject { EmptyStruct: func() handler.CObject {

79
pkg/user/update_email.go Normal file
View File

@ -0,0 +1,79 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package user
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/utils"
)
// EmailUpdate is the data structure to update a user's email address
type EmailUpdate struct {
User *User `json:"-"`
// The new email address. Needs to be a valid email address.
NewEmail string `json:"new_email" valid:"email,length(0|250),required"`
// The password of the user for confirmation.
Password string `json:"password"`
}
// UpdateEmail lets a user update their email address
func UpdateEmail(update *EmailUpdate) (err error) {
// Check the email is not already used
user := &User{}
has, err := x.Where("email = ?", update.NewEmail).Get(user)
if err != nil {
return
}
if has {
return ErrUserEmailExists{UserID: user.ID, Email: update.NewEmail}
}
// Set the user as unconfirmed and the new email address
update.User, err = GetUserWithEmail(&User{ID: update.User.ID})
if err != nil {
return
}
update.User.IsActive = false
update.User.Email = update.NewEmail
update.User.EmailConfirmToken = utils.MakeRandomString(64)
_, err = x.
Where("id = ?", update.User.ID).
Cols("email", "is_active", "email_confirm_token").
Update(update.User)
if err != nil {
return
}
// Send the confirmation mail
if !config.MailerEnabled.GetBool() {
return
}
// Send the user a mail with a link to confirm the mail
data := map[string]interface{}{
"User": update.User,
"IsNew": false,
}
mail.SendMailWithTemplate(update.User.Email, update.User.Username+", please confirm your email address at Vikunja", "confirm-email", data)
return
}

View File

@ -252,7 +252,7 @@ func CreateUser(user *User) (newUser *User, err error) {
// The new user should not be activated until it confirms his mail address // The new user should not be activated until it confirms his mail address
newUser.IsActive = false newUser.IsActive = false
// Generate a confirm token // Generate a confirm token
newUser.EmailConfirmToken = utils.MakeRandomString(400) newUser.EmailConfirmToken = utils.MakeRandomString(60)
} }
// Insert it // Insert it
@ -277,7 +277,8 @@ func CreateUser(user *User) (newUser *User, err error) {
// Send the user a mail with a link to confirm the mail // Send the user a mail with a link to confirm the mail
data := map[string]interface{}{ data := map[string]interface{}{
"User": newUserOut, "User": newUserOut,
"IsNew": true,
} }
mail.SendMailWithTemplate(user.Email, newUserOut.Username+" + Vikunja = <3", "confirm-email", data) mail.SendMailWithTemplate(user.Email, newUserOut.Username+" + Vikunja = <3", "confirm-email", data)

View File

@ -1,10 +1,12 @@
{{template "mail-header.tmpl" .}} {{template "mail-header.tmpl" .}}
<p> <p>
Hi {{.User.Username}},<br> Hi {{.User.Username}},<br>
<br> {{if .IsNew}}
Welcome to Vikunja! <br>
Welcome to Vikunja!
{{end}}
<br/> <br/>
To confirm you email address, click the link below: To confirm your email address, click the link below:
</p> </p>
<a href="{{.FrontendURL}}?userEmailConfirm={{.User.EmailConfirmToken}}" title="Confirm your email address" style="background: rgb(20, 131, 175); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; border: 1px solid rgb(16, 106, 140); border-bottom-width: 3px; color: rgb(255, 255, 255); font-weight: 700; font-size: 13px; margin: 10px auto; padding: 5px 10px; text-decoration: none; text-align: center; text-rendering: optimizelegibility; text-transform: uppercase; display: block; width: 200px;"> <a href="{{.FrontendURL}}?userEmailConfirm={{.User.EmailConfirmToken}}" title="Confirm your email address" style="background: rgb(20, 131, 175); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; border: 1px solid rgb(16, 106, 140); border-bottom-width: 3px; color: rgb(255, 255, 255); font-weight: 700; font-size: 13px; margin: 10px auto; padding: 5px 10px; text-decoration: none; text-align: center; text-rendering: optimizelegibility; text-transform: uppercase; display: block; width: 200px;">
Confirm your email address Confirm your email address

View File

@ -1,7 +1,9 @@
Hi {{.User.Username}}, Hi {{.User.Username}},
Welcome to Vikunja! {{if .IsNew}}
Welcome to Vikunja!
To confirm you email address, click the link below: {{end}}
To confirm your email address, copy the link below and paste it in your browser:
{{.FrontendURL}}?userEmailConfirm={{.User.EmailConfirmToken}} {{.FrontendURL}}?userEmailConfirm={{.User.EmailConfirmToken}}