Various user fixes (#38)

This commit is contained in:
konrad 2018-12-19 21:05:25 +00:00 committed by Gitea
parent 3e4f7fb2f4
commit cbc5995ad3
16 changed files with 55 additions and 51 deletions

View file

@ -237,9 +237,9 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten.
### Bugfixes
* [ ] Panic wenn mailer nicht erreichbar -> Als workaround mailer deaktivierbar machen, bzw keine mails verschicken
* [x] Panic wenn mailer nicht erreichbar -> Als workaround mailer deaktivierbar machen, bzw keine mails verschicken
* [x] "unexpected EOF"
* [ ] Beim Login & Password reset gibt die API zurück dass der Nutzer nicht existiert
* [x] Beim Login & Password reset gibt die API zurück dass der Nutzer nicht existiert
### Docs

View file

@ -27,7 +27,7 @@ Content-Type: application/json
Accept: application/json
{
"user_name": "user"
"email": "k@knt.li"
}
### Request a token to reset a password

View file

@ -54,6 +54,8 @@ redis:
db: 0
mailer:
# Whether to enable the mailer or not. If it is disabled, all users are enabled right away and password reset is not possible.
enabled: false
# SMTP Host
host: ""
# SMTP Host port

View file

@ -78,6 +78,8 @@ redis:
db: 0
mailer:
# Whether to enable the mailer or not. If it is disabled, all users are enabled right away and password reset is not possible.
enabled: false
# SMTP Host
host: ""
# SMTP Host port

View file

@ -63,6 +63,7 @@ func init() {
viper.SetDefault("cache.type", "memory")
viper.SetDefault("cache.maxelementsize", 1000)
// Mailer
viper.SetDefault("mailer.enabled", false)
viper.SetDefault("mailer.host", "")
viper.SetDefault("mailer.port", "587")
viper.SetDefault("mailer.user", "user")

View file

@ -31,6 +31,10 @@ var Queue chan *gomail.Message
func StartMailDaemon() {
Queue = make(chan *gomail.Message, viper.GetInt("mailer.queuelength"))
if !viper.GetBool("mailer.enabled") {
return
}
if viper.GetString("mailer.host") == "" {
log.Log.Warning("Mailer seems to be not configured! Please see the config docs for more details.")
return

View file

@ -2,20 +2,27 @@
id: 1
username: 'user1'
password: '1234'
email: 'johndoe@example.com'
email: 'user1@example.com'
-
id: 2
username: 'user2'
password: '1234'
email: 'johndoe@example.com'
email: 'user2@example.com'
-
id: 3
username: 'user3'
password: '1234'
email: 'johndoe@example.com'
email: 'user3@example.com'
-
id: 4
username: 'user4'
password: '1234'
email: 'johndoe@example.com'
email: 'user4@example.com'
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
-
id: 5
username: 'user5'
password: '1234'
email: 'user4@example.com'
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
is_active: false

View file

@ -160,7 +160,7 @@ func TestListUser_ReadAll(t *testing.T) {
ID: 1,
Username: "user1",
Password: "1234",
Email: "johndoe@example.com",
Email: "user1@example.com",
},
Right: UserRightRead,
},
@ -169,7 +169,7 @@ func TestListUser_ReadAll(t *testing.T) {
ID: 2,
Username: "user2",
Password: "1234",
Email: "johndoe@example.com",
Email: "user2@example.com",
},
Right: UserRightRead,
},

View file

@ -161,7 +161,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
ID: 1,
Username: "user1",
Password: "1234",
Email: "johndoe@example.com",
Email: "user1@example.com",
},
Right: UserRightRead,
},
@ -170,7 +170,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
ID: 2,
Username: "user2",
Password: "1234",
Email: "johndoe@example.com",
Email: "user2@example.com",
},
Right: UserRightRead,
},

View file

@ -30,10 +30,6 @@ import (
"testing"
)
// IsTesting is set to true when we're running tests.
// We don't have a good solution to test email sending yet, so we disable email sending when testing
var IsTesting bool
// MainTest creates the test engine
func MainTest(m *testing.M, pathToRoot string) {
var err error
@ -42,8 +38,6 @@ func MainTest(m *testing.M, pathToRoot string) {
log.Log.Fatalf("Error creating test engine: %v\n", err)
}
IsTesting = true
// Start the pseudo mail queue
mail.StartMailDaemon()

View file

@ -78,9 +78,9 @@ func getUserWithError(a web.Auth) (*User, error) {
// APIUserPassword represents a user object without timestamps and a json password field.
type APIUserPassword struct {
ID int64 `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
Username string `json:"username" valid:"length(3|250)"`
Password string `json:"password" valid:"length(8|250)"`
Email string `json:"email" valid:"email,length(0|250)"`
}
// APIFormat formats an API User into a normal user struct
@ -125,7 +125,9 @@ func CheckUserCredentials(u *UserLogin) (User, error) {
// Check if the user exists
user, err := GetUser(User{Username: u.Username})
if err != nil {
return User{}, err
// hashing the password takes a long time, so we hash something to not make it clear if the username was wrong
bcrypt.GenerateFromPassword([]byte(u.Username), 14)
return User{}, ErrWrongUsernameOrPassword{}
}
// User is invalid if it needs to verify its email address

View file

@ -20,6 +20,7 @@ import (
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/utils"
"github.com/spf13/viper"
"golang.org/x/crypto/bcrypt"
)
@ -67,11 +68,13 @@ func CreateUser(user User) (newUser User, err error) {
return User{}, err
}
// Generate a confirm token
newUser.EmailConfirmToken = utils.MakeRandomString(400)
// The new user should not be activated until it confirms his mail address
newUser.IsActive = false
newUser.IsActive = true
if viper.GetBool("mailer.enabled") {
// The new user should not be activated until it confirms his mail address
newUser.IsActive = false
// Generate a confirm token
newUser.EmailConfirmToken = utils.MakeRandomString(400)
}
// Insert it
_, err = x.Insert(newUser)
@ -96,7 +99,7 @@ func CreateUser(user User) (newUser User, err error) {
}
// Dont send a mail if we're testing
if IsTesting {
if !viper.GetBool("mailer.enabled") {
return newUserOut, err
}

View file

@ -19,6 +19,7 @@ package models
import (
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/utils"
"github.com/spf13/viper"
)
// PasswordReset holds the data to reset a password
@ -59,7 +60,7 @@ func UserPasswordReset(reset *PasswordReset) (err error) {
}
// Dont send a mail if we're testing
if IsTesting {
if !viper.GetBool("mailer.enabled") {
return
}
@ -75,13 +76,13 @@ func UserPasswordReset(reset *PasswordReset) (err error) {
// PasswordTokenRequest defines the request format for password reset resqest
type PasswordTokenRequest struct {
Username string `json:"user_name"`
Email string `json:"email" valid:"email,length(0|250)"`
}
// RequestUserPasswordResetToken inserts a random token to reset a users password into the databsse
func RequestUserPasswordResetToken(tr *PasswordTokenRequest) (err error) {
// Check if the user exists
user, err := GetUser(User{Username: tr.Username})
user, err := GetUser(User{Email: tr.Email})
if err != nil {
return
}
@ -96,7 +97,7 @@ func RequestUserPasswordResetToken(tr *PasswordTokenRequest) (err error) {
}
// Dont send a mail if we're testing
if IsTesting {
if !viper.GetBool("mailer.enabled") {
return
}

View file

@ -78,7 +78,7 @@ func TestCreateUser(t *testing.T) {
assert.True(t, IsErrUserDoesNotExist(err))
// Check the user credentials with an unverified email
user, err := CheckUserCredentials(&UserLogin{"testuu", "1234"})
user, err := CheckUserCredentials(&UserLogin{"user5", "1234"})
assert.Error(t, err)
assert.True(t, IsErrEmailNotConfirmed(err))
@ -97,7 +97,7 @@ func TestCreateUser(t *testing.T) {
// Check usercredentials for a nonexistent user (should fail)
_, err = CheckUserCredentials(&UserLogin{"dfstestuu", "1234"})
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, IsErrWrongUsernameOrPassword(err))
// Update the user
uuser, err := UpdateUser(User{ID: theuser.ID, Password: "444444"})
@ -146,7 +146,7 @@ func TestCreateUser(t *testing.T) {
func TestUserPasswordReset(t *testing.T) {
// Request a new token
tr := &PasswordTokenRequest{
Username: "user1",
Email: "user1@example.com",
}
err := RequestUserPasswordResetToken(tr)
assert.NoError(t, err)

View file

@ -67,6 +67,10 @@ func UserRequestResetPasswordToken(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "No username provided.")
}
if err := c.Validate(pwTokenReset); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
err := models.RequestUserPasswordResetToken(&pwTokenReset)
if err != nil {
return handler.HandleHTTPError(err, c)

View file

@ -14,22 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 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/>.
// @title Vikunja API
// @license.name GPLv3
// @BasePath /api/v1