2020-11-21 17:38:58 +01:00
|
|
|
// Vikunja is a to-do list application to facilitate your life.
|
2021-02-02 20:19:13 +01:00
|
|
|
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
2020-11-21 17:38:58 +01:00
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
2020-12-23 16:41:52 +01:00
|
|
|
// it under the terms of the GNU Affero General Public Licensee as published by
|
2020-11-21 17:38:58 +01:00
|
|
|
// 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
|
2020-12-23 16:41:52 +01:00
|
|
|
// GNU Affero General Public Licensee for more details.
|
2020-11-21 17:38:58 +01:00
|
|
|
//
|
2020-12-23 16:41:52 +01:00
|
|
|
// You should have received a copy of the GNU Affero General Public Licensee
|
2020-11-21 17:38:58 +01:00
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package user
|
|
|
|
|
|
|
|
import (
|
|
|
|
"code.vikunja.io/api/pkg/config"
|
2021-02-02 23:48:37 +01:00
|
|
|
"code.vikunja.io/api/pkg/events"
|
2020-11-21 17:38:58 +01:00
|
|
|
"code.vikunja.io/api/pkg/mail"
|
|
|
|
"code.vikunja.io/api/pkg/utils"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
2020-12-23 16:32:28 +01:00
|
|
|
"xorm.io/xorm"
|
2020-11-21 17:38:58 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
const issuerLocal = `local`
|
|
|
|
|
|
|
|
// CreateUser creates a new user and inserts it into the database
|
2020-12-23 16:32:28 +01:00
|
|
|
func CreateUser(s *xorm.Session, user *User) (newUser *User, err error) {
|
2020-11-21 17:38:58 +01:00
|
|
|
|
|
|
|
if user.Issuer == "" {
|
|
|
|
user.Issuer = issuerLocal
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we have all needed information
|
|
|
|
err = checkIfUserIsValid(user)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the user already exists with that username
|
2020-12-23 16:32:28 +01:00
|
|
|
err = checkIfUserExists(s, user)
|
2020-11-21 17:38:58 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if user.Issuer == issuerLocal {
|
|
|
|
// Hash the password
|
|
|
|
user.Password, err = hashPassword(user.Password)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
user.IsActive = true
|
|
|
|
if config.MailerEnabled.GetBool() && user.Issuer == issuerLocal {
|
|
|
|
// The new user should not be activated until it confirms his mail address
|
|
|
|
user.IsActive = false
|
|
|
|
// Generate a confirm token
|
|
|
|
user.EmailConfirmToken = utils.MakeRandomString(60)
|
|
|
|
}
|
|
|
|
|
|
|
|
user.AvatarProvider = "initials"
|
|
|
|
|
|
|
|
// Insert it
|
2020-12-23 16:32:28 +01:00
|
|
|
_, err = s.Insert(user)
|
2020-11-21 17:38:58 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the full new User
|
2020-12-23 16:32:28 +01:00
|
|
|
newUserOut, err := GetUserByID(s, user.ID)
|
2020-11-21 17:38:58 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-02-02 23:48:37 +01:00
|
|
|
err = events.Dispatch(&CreatedEvent{
|
|
|
|
User: newUserOut,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-11-21 17:38:58 +01:00
|
|
|
sendConfirmEmail(user)
|
|
|
|
|
|
|
|
return newUserOut, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// HashPassword hashes a password
|
|
|
|
func hashPassword(password string) (string, error) {
|
|
|
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 11)
|
|
|
|
return string(bytes), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkIfUserIsValid(user *User) error {
|
|
|
|
if user.Email == "" ||
|
|
|
|
(user.Issuer != issuerLocal && user.Subject == "") ||
|
|
|
|
(user.Issuer == issuerLocal && (user.Password == "" ||
|
|
|
|
user.Username == "")) {
|
|
|
|
return ErrNoUsernamePassword{}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-12-23 16:32:28 +01:00
|
|
|
func checkIfUserExists(s *xorm.Session, user *User) (err error) {
|
2020-11-21 17:38:58 +01:00
|
|
|
exists := true
|
2020-12-23 16:32:28 +01:00
|
|
|
_, err = GetUserByUsername(s, user.Username)
|
2020-11-21 17:38:58 +01:00
|
|
|
if err != nil {
|
|
|
|
if IsErrUserDoesNotExist(err) {
|
|
|
|
exists = false
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if exists {
|
|
|
|
return ErrUsernameExists{user.ID, user.Username}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the user already existst with that email
|
|
|
|
exists = true
|
|
|
|
userToCheck := &User{
|
|
|
|
Email: user.Email,
|
|
|
|
Issuer: user.Issuer,
|
|
|
|
Subject: user.Subject,
|
|
|
|
}
|
|
|
|
|
|
|
|
if user.Issuer != issuerLocal {
|
|
|
|
userToCheck.Email = ""
|
|
|
|
}
|
|
|
|
|
2020-12-23 16:32:28 +01:00
|
|
|
_, err = getUser(s, userToCheck, false)
|
2020-11-21 17:38:58 +01:00
|
|
|
if err != nil {
|
|
|
|
if IsErrUserDoesNotExist(err) {
|
|
|
|
exists = false
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if exists && user.Issuer == issuerLocal {
|
|
|
|
return ErrUserEmailExists{user.ID, user.Email}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func sendConfirmEmail(user *User) {
|
|
|
|
// Dont send a mail if no mailer is configured
|
|
|
|
if !config.MailerEnabled.GetBool() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the user a mail with a link to confirm the mail
|
|
|
|
data := map[string]interface{}{
|
|
|
|
"User": user,
|
|
|
|
"IsNew": true,
|
|
|
|
}
|
|
|
|
|
|
|
|
mail.SendMailWithTemplate(user.Email, user.Username+" + Vikunja = <3", "confirm-email", data)
|
|
|
|
}
|