1
0

Refactor User and DB handling (#123)

fix copyright date

Add more user tests

More user tests

More user tests

Start refactoring user tests

Docs

Fix lint

Fix db fixtures init in tests

Fix models test

Fix loading fixtures

Fix ineffasign

Fix lint

Fix integration tests

Fix init of test engine creation

Fix user related tests

Better handling of creating test enging

Moved all fixtures to db package

Moved all fixtures to db package

Moved user related stuff to seperate package

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/api/pulls/123
This commit is contained in:
konrad
2020-01-26 17:08:06 +00:00
parent 8c33e24e92
commit 7e9446ea07
108 changed files with 1506 additions and 1024 deletions

View File

@ -1,6 +1,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"testing"
)
@ -9,7 +11,7 @@ func TestBulkTask_Update(t *testing.T) {
IDs []int64
Tasks []*Task
Task Task
User *User
User *user.User
}
tests := []struct {
name string
@ -24,7 +26,7 @@ func TestBulkTask_Update(t *testing.T) {
Task: Task{
Text: "bulkupdated",
},
User: &User{ID: 1},
User: &user.User{ID: 1},
},
},
{
@ -34,7 +36,7 @@ func TestBulkTask_Update(t *testing.T) {
Task: Task{
Text: "bulkupdated",
},
User: &User{ID: 1},
User: &user.User{ID: 1},
},
wantForbidden: true,
},
@ -45,13 +47,15 @@ func TestBulkTask_Update(t *testing.T) {
Task: Task{
Text: "bulkupdated",
},
User: &User{ID: 1},
User: &user.User{ID: 1},
},
wantForbidden: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
bt := &BulkTask{
IDs: tt.fields.IDs,
Tasks: tt.fields.Tasks,

View File

@ -46,273 +46,6 @@ func (err ErrGenericForbidden) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrorCodeGenericForbidden, Message: "You're not allowed to do this."}
}
// =====================
// User Operation Errors
// =====================
// ErrUsernameExists represents a "UsernameAlreadyExists" kind of error.
type ErrUsernameExists struct {
UserID int64
Username string
}
// IsErrUsernameExists checks if an error is a ErrUsernameExists.
func IsErrUsernameExists(err error) bool {
_, ok := err.(ErrUsernameExists)
return ok
}
func (err ErrUsernameExists) Error() string {
return fmt.Sprintf("User with that username already exists [user id: %d, username: %s]", err.UserID, err.Username)
}
// ErrorCodeUsernameExists holds the unique world-error code of this error
const ErrorCodeUsernameExists = 1001
// HTTPError holds the http error description
func (err ErrUsernameExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
}
// ErrUserEmailExists represents a "UserEmailExists" kind of error.
type ErrUserEmailExists struct {
UserID int64
Email string
}
// IsErrUserEmailExists checks if an error is a ErrUserEmailExists.
func IsErrUserEmailExists(err error) bool {
_, ok := err.(ErrUserEmailExists)
return ok
}
func (err ErrUserEmailExists) Error() string {
return fmt.Sprintf("User with that email already exists [user id: %d, email: %s]", err.UserID, err.Email)
}
// ErrorCodeUserEmailExists holds the unique world-error code of this error
const ErrorCodeUserEmailExists = 1002
// HTTPError holds the http error description
func (err ErrUserEmailExists) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
}
// ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error.
type ErrNoUsernamePassword struct{}
// IsErrNoUsernamePassword checks if an error is a ErrNoUsernamePassword.
func IsErrNoUsernamePassword(err error) bool {
_, ok := err.(ErrNoUsernamePassword)
return ok
}
func (err ErrNoUsernamePassword) Error() string {
return fmt.Sprintf("No username and password provided")
}
// ErrCodeNoUsernamePassword holds the unique world-error code of this error
const ErrCodeNoUsernamePassword = 1004
// HTTPError holds the http error description
func (err ErrNoUsernamePassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
}
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
type ErrUserDoesNotExist struct {
UserID int64
}
// IsErrUserDoesNotExist checks if an error is a ErrUserDoesNotExist.
func IsErrUserDoesNotExist(err error) bool {
_, ok := err.(ErrUserDoesNotExist)
return ok
}
func (err ErrUserDoesNotExist) Error() string {
return fmt.Sprintf("User does not exist [user id: %d]", err.UserID)
}
// ErrCodeUserDoesNotExist holds the unique world-error code of this error
const ErrCodeUserDoesNotExist = 1005
// HTTPError holds the http error description
func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
}
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
type ErrCouldNotGetUserID struct{}
// IsErrCouldNotGetUserID checks if an error is a ErrCouldNotGetUserID.
func IsErrCouldNotGetUserID(err error) bool {
_, ok := err.(ErrCouldNotGetUserID)
return ok
}
func (err ErrCouldNotGetUserID) Error() string {
return fmt.Sprintf("Could not get user ID")
}
// ErrCodeCouldNotGetUserID holds the unique world-error code of this error
const ErrCodeCouldNotGetUserID = 1006
// HTTPError holds the http error description
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
}
// ErrNoPasswordResetToken represents an error where no password reset token exists for that user
type ErrNoPasswordResetToken struct {
UserID int64
}
func (err ErrNoPasswordResetToken) Error() string {
return fmt.Sprintf("No token to reset a password [UserID: %d]", err.UserID)
}
// ErrCodeNoPasswordResetToken holds the unique world-error code of this error
const ErrCodeNoPasswordResetToken = 1008
// HTTPError holds the http error description
func (err ErrNoPasswordResetToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeNoPasswordResetToken, Message: "No token to reset a user's password provided."}
}
// ErrInvalidPasswordResetToken is an error where the password reset token is invalid
type ErrInvalidPasswordResetToken struct {
Token string
}
func (err ErrInvalidPasswordResetToken) Error() string {
return fmt.Sprintf("Invalid token to reset a password [Token: %s]", err.Token)
}
// ErrCodeInvalidPasswordResetToken holds the unique world-error code of this error
const ErrCodeInvalidPasswordResetToken = 1009
// HTTPError holds the http error description
func (err ErrInvalidPasswordResetToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidPasswordResetToken, Message: "Invalid token to reset a user's password."}
}
// IsErrInvalidPasswordResetToken checks if an error is a ErrInvalidPasswordResetToken.
func IsErrInvalidPasswordResetToken(err error) bool {
_, ok := err.(ErrInvalidPasswordResetToken)
return ok
}
// ErrInvalidEmailConfirmToken is an error where the email confirm token is invalid
type ErrInvalidEmailConfirmToken struct {
Token string
}
func (err ErrInvalidEmailConfirmToken) Error() string {
return fmt.Sprintf("Invalid email confirm token [Token: %s]", err.Token)
}
// ErrCodeInvalidEmailConfirmToken holds the unique world-error code of this error
const ErrCodeInvalidEmailConfirmToken = 1010
// HTTPError holds the http error description
func (err ErrInvalidEmailConfirmToken) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidEmailConfirmToken, Message: "Invalid email confirm token."}
}
// IsErrInvalidEmailConfirmToken checks if an error is a ErrInvalidEmailConfirmToken.
func IsErrInvalidEmailConfirmToken(err error) bool {
_, ok := err.(ErrInvalidEmailConfirmToken)
return ok
}
// ErrWrongUsernameOrPassword is an error where the email was not confirmed
type ErrWrongUsernameOrPassword struct {
}
func (err ErrWrongUsernameOrPassword) Error() string {
return fmt.Sprintf("Wrong username or password")
}
// ErrCodeWrongUsernameOrPassword holds the unique world-error code of this error
const ErrCodeWrongUsernameOrPassword = 1011
// HTTPError holds the http error description
func (err ErrWrongUsernameOrPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeWrongUsernameOrPassword, Message: "Wrong username or password."}
}
// IsErrWrongUsernameOrPassword checks if an error is a IsErrEmailNotConfirmed.
func IsErrWrongUsernameOrPassword(err error) bool {
_, ok := err.(ErrWrongUsernameOrPassword)
return ok
}
// ErrEmailNotConfirmed is an error where the email was not confirmed
type ErrEmailNotConfirmed struct {
UserID int64
}
func (err ErrEmailNotConfirmed) Error() string {
return fmt.Sprintf("Email is not confirmed [UserID: %d]", err.UserID)
}
// ErrCodeEmailNotConfirmed holds the unique world-error code of this error
const ErrCodeEmailNotConfirmed = 1012
// HTTPError holds the http error description
func (err ErrEmailNotConfirmed) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmailNotConfirmed, Message: "Please confirm your email address."}
}
// IsErrEmailNotConfirmed checks if an error is a IsErrEmailNotConfirmed.
func IsErrEmailNotConfirmed(err error) bool {
_, ok := err.(ErrEmailNotConfirmed)
return ok
}
// ErrEmptyNewPassword represents a "EmptyNewPassword" kind of error.
type ErrEmptyNewPassword struct{}
// IsErrEmptyNewPassword checks if an error is a ErrEmptyNewPassword.
func IsErrEmptyNewPassword(err error) bool {
_, ok := err.(ErrEmptyNewPassword)
return ok
}
func (err ErrEmptyNewPassword) Error() string {
return fmt.Sprintf("New password is empty")
}
// ErrCodeEmptyNewPassword holds the unique world-error code of this error
const ErrCodeEmptyNewPassword = 1013
// HTTPError holds the http error description
func (err ErrEmptyNewPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmptyNewPassword, Message: "Please specify new password."}
}
// ErrEmptyOldPassword represents a "EmptyOldPassword" kind of error.
type ErrEmptyOldPassword struct{}
// IsErrEmptyOldPassword checks if an error is a ErrEmptyOldPassword.
func IsErrEmptyOldPassword(err error) bool {
_, ok := err.(ErrEmptyOldPassword)
return ok
}
func (err ErrEmptyOldPassword) Error() string {
return fmt.Sprintf("Old password is empty")
}
// ErrCodeEmptyOldPassword holds the unique world-error code of this error
const ErrCodeEmptyOldPassword = 1014
// HTTPError holds the http error description
func (err ErrEmptyOldPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmptyOldPassword, Message: "Please specify old password."}
}
// ===================
// Empty things errors
// ===================

View File

@ -1 +0,0 @@
../../files/fixtures/files.yml

View File

@ -1,8 +0,0 @@
- id: 1
task_id: 1
label_id: 4
created: 0
- id: 2
task_id: 2
label_id: 4
created: 0

View File

@ -1,20 +0,0 @@
- id: 1
title: 'Label #1'
created_by_id: 1
updated: 0
created: 0
- id: 2
title: 'Label #2'
created_by_id: 1
updated: 0
created: 0
- id: 3
title: 'Label #3 - other user'
created_by_id: 2
updated: 0
created: 0
- id: 4
title: 'Label #4 - visible via other task'
created_by_id: 2
updated: 0
created: 0

View File

@ -1,24 +0,0 @@
- id: 1
hash: test
list_id: 1
right: 0
sharing_type: 1
shared_by_id: 1
created: 0
updated: 0
- id: 2
hash: test2
list_id: 2
right: 1
sharing_type: 1
shared_by_id: 1
created: 0
updated: 0
- id: 3
hash: test3
list_id: 3
right: 2
sharing_type: 1
shared_by_id: 1
created: 0
updated: 0

View File

@ -1,182 +0,0 @@
-
id: 1
title: Test1
description: Lorem Ipsum
identifier: test1
owner_id: 1
namespace_id: 1
updated: 0
created: 0
-
id: 2
title: Test2
description: Lorem Ipsum
identifier: test2
owner_id: 3
namespace_id: 1
updated: 0
created: 0
-
id: 3
title: Test3
description: Lorem Ipsum
identifier: test3
owner_id: 3
namespace_id: 2
updated: 0
created: 0
-
id: 4
title: Test4
description: Lorem Ipsum
identifier: test4
owner_id: 3
namespace_id: 3
updated: 0
created: 0
-
id: 5
title: Test5
description: Lorem Ipsum
identifier: test5
owner_id: 5
namespace_id: 5
updated: 0
created: 0
-
id: 6
title: Test6
description: Lorem Ipsum
identifier: test6
owner_id: 6
namespace_id: 6
updated: 0
created: 0
-
id: 7
title: Test7
description: Lorem Ipsum
identifier: test7
owner_id: 6
namespace_id: 6
updated: 0
created: 0
-
id: 8
title: Test8
description: Lorem Ipsum
identifier: test8
owner_id: 6
namespace_id: 6
updated: 0
created: 0
-
id: 9
title: Test9
description: Lorem Ipsum
identifier: test9
owner_id: 6
namespace_id: 6
updated: 0
created: 0
-
id: 10
title: Test10
description: Lorem Ipsum
identifier: test10
owner_id: 6
namespace_id: 6
updated: 0
created: 0
-
id: 11
title: Test11
description: Lorem Ipsum
identifier: test11
owner_id: 6
namespace_id: 6
updated: 0
created: 0
-
id: 12
title: Test12
description: Lorem Ipsum
identifier: test12
owner_id: 6
namespace_id: 7
updated: 0
created: 0
-
id: 13
title: Test13
description: Lorem Ipsum
identifier: test13
owner_id: 6
namespace_id: 8
updated: 0
created: 0
-
id: 14
title: Test14
description: Lorem Ipsum
identifier: test14
owner_id: 6
namespace_id: 9
updated: 0
created: 0
-
id: 15
title: Test15
description: Lorem Ipsum
identifier: test15
owner_id: 6
namespace_id: 10
updated: 0
created: 0
-
id: 16
title: Test16
description: Lorem Ipsum
identifier: test16
owner_id: 6
namespace_id: 11
updated: 0
created: 0
-
id: 17
title: Test17
description: Lorem Ipsum
identifier: test17
owner_id: 6
namespace_id: 12
updated: 0
created: 0
# This list is owned by user 7, and several other users have access to it via different methods.
# It is used to test the listUsers method.
-
id: 18
title: Test18
description: Lorem Ipsum
identifier: test18
owner_id: 7
namespace_id: 13
updated: 0
created: 0
-
id: 19
title: Test19
description: Lorem Ipsum
identifier: test19
owner_id: 7
namespace_id: 14
updated: 0
created: 0
-
id: 20
title: Test20
description: Lorem Ipsum
identifier: test20
owner_id: 13
namespace_id: 15
updated: 0
created: 0

View File

@ -1,78 +0,0 @@
- id: 1
name: testnamespace
description: Lorem Ipsum
owner_id: 1
updated: 0
created: 0
- id: 2
name: testnamespace2
description: Lorem Ipsum
owner_id: 2
updated: 0
created: 0
- id: 3
name: testnamespace3
description: Lorem Ipsum
owner_id: 3
updated: 0
created: 0
- id: 6
name: testnamespace6
description: Lorem Ipsum
owner_id: 6
updated: 0
created: 0
- id: 7
name: testnamespace7
description: Lorem Ipsum
owner_id: 6
updated: 0
created: 0
- id: 8
name: testnamespace8
description: Lorem Ipsum
owner_id: 6
updated: 0
created: 0
- id: 9
name: testnamespace9
description: Lorem Ipsum
owner_id: 6
updated: 0
created: 0
- id: 10
name: testnamespace10
description: Lorem Ipsum
owner_id: 6
updated: 0
created: 0
- id: 11
name: testnamespace11
description: Lorem Ipsum
owner_id: 6
updated: 0
created: 0
- id: 12
name: testnamespace12
description: Lorem Ipsum
owner_id: 6
updated: 0
created: 0
- id: 13
name: testnamespace13
description: Lorem Ipsum
owner_id: 7
updated: 0
created: 0
- id: 14
name: testnamespace14
description: Lorem Ipsum
owner_id: 7
updated: 0
created: 0
- id: 15
name: testnamespace15
description: Lorem Ipsum
owner_id: 13
updated: 0
created: 0

View File

@ -1,8 +0,0 @@
- id: 1
task_id: 30
user_id: 1
created: 0
- id: 2
task_id: 30
user_id: 2
created: 0

View File

@ -1,11 +0,0 @@
- id: 1
task_id: 1
file_id: 1
created_by_id: 1
created: 0
# The file for this attachment does not exist
- id: 2
task_id: 1
file_id: 9999
created_by_id: 1
created: 0

View File

@ -1,12 +0,0 @@
- id: 1
task_id: 1
other_task_id: 29
relation_kind: 'subtask'
created_by_id: 1
created: 0
- id: 2
task_id: 29
other_task_id: 1
relation_kind: 'parenttask'
created_by_id: 1
created: 0

View File

@ -1,8 +0,0 @@
- id: 1
task_id: 27
reminder_unix: 1543626724
created: 1543626724
- id: 2
task_id: 27
reminder_unix: 1543626824
created: 1543626724

View File

@ -1,246 +0,0 @@
- id: 1
text: 'task #1'
description: 'Lorem Ipsum'
created_by_id: 1
list_id: 1
index: 1
created: 1543626724
updated: 1543626724
- id: 2
text: 'task #2 done'
done: true
created_by_id: 1
list_id: 1
index: 2
created: 1543626724
updated: 1543626724
- id: 3
text: 'task #3 high prio'
created_by_id: 1
list_id: 1
index: 3
created: 1543626724
updated: 1543626724
priority: 100
- id: 4
text: 'task #4 low prio'
created_by_id: 1
list_id: 1
index: 4
created: 1543626724
updated: 1543626724
priority: 1
- id: 5
text: 'task #5 higher due date'
created_by_id: 1
list_id: 1
index: 5
created: 1543626724
updated: 1543626724
due_date_unix: 1543636724
- id: 6
text: 'task #6 lower due date'
created_by_id: 1
list_id: 1
index: 6
created: 1543626724
updated: 1543626724
due_date_unix: 1543616724
- id: 7
text: 'task #7 with start date'
created_by_id: 1
list_id: 1
index: 7
created: 1543626724
updated: 1543626724
start_date_unix: 1544600000
- id: 8
text: 'task #8 with end date'
created_by_id: 1
list_id: 1
index: 8
created: 1543626724
updated: 1543626724
end_date_unix: 1544700000
- id: 9
text: 'task #9 with start and end date'
created_by_id: 1
list_id: 1
index: 9
created: 1543626724
updated: 1543626724
start_date_unix: 1544600000
end_date_unix: 1544700000
- id: 10
text: 'task #10 basic'
created_by_id: 1
list_id: 1
index: 10
created: 1543626724
updated: 1543626724
- id: 11
text: 'task #11 basic'
created_by_id: 1
list_id: 1
index: 11
created: 1543626724
updated: 1543626724
- id: 12
text: 'task #12 basic'
created_by_id: 1
list_id: 1
index: 12
created: 1543626724
updated: 1543626724
- id: 13
text: 'task #13 basic other list'
created_by_id: 1
list_id: 2
index: 1
created: 1543626724
updated: 1543626724
- id: 14
text: 'task #14 basic other list'
created_by_id: 5
list_id: 5
index: 1
created: 1543626724
updated: 1543626724
- id: 15
text: 'task #15'
created_by_id: 6
list_id: 6
index: 1
created: 1543626724
updated: 1543626724
- id: 16
text: 'task #16'
created_by_id: 6
list_id: 7
index: 1
created: 1543626724
updated: 1543626724
- id: 17
text: 'task #17'
created_by_id: 6
list_id: 8
index: 1
created: 1543626724
updated: 1543626724
- id: 18
text: 'task #18'
created_by_id: 6
list_id: 9
index: 1
created: 1543626724
updated: 1543626724
- id: 19
text: 'task #19'
created_by_id: 6
list_id: 10
index: 1
created: 1543626724
updated: 1543626724
- id: 20
text: 'task #20'
created_by_id: 6
list_id: 11
index: 1
created: 1543626724
updated: 1543626724
- id: 21
text: 'task #21'
created_by_id: 6
list_id: 12
index: 1
created: 1543626724
updated: 1543626724
- id: 22
text: 'task #22'
created_by_id: 6
list_id: 13
index: 1
created: 1543626724
updated: 1543626724
- id: 23
text: 'task #23'
created_by_id: 6
list_id: 14
index: 1
created: 1543626724
updated: 1543626724
- id: 24
text: 'task #24'
created_by_id: 6
list_id: 15
index: 1
created: 1543626724
updated: 1543626724
- id: 25
text: 'task #25'
created_by_id: 6
list_id: 16
index: 1
created: 1543626724
updated: 1543626724
- id: 26
text: 'task #26'
created_by_id: 6
list_id: 17
index: 1
created: 1543626724
updated: 1543626724
- id: 27
text: 'task #27 with reminders'
created_by_id: 1
list_id: 1
index: 12
created: 1543626724
updated: 1543626724
- id: 28
text: 'task #28 with repeat after'
done: false
created_by_id: 1
repeat_after: 3600
list_id: 1
index: 13
created: 1543626724
updated: 1543626724
- id: 29
text: 'task #29 with parent task (1)'
created_by_id: 1
list_id: 1
index: 14
created: 1543626724
updated: 1543626724
- id: 30
text: 'task #30 with assignees'
created_by_id: 1
list_id: 1
index: 15
created: 1543626724
updated: 1543626724
- id: 31
text: 'task #31 with color'
created_by_id: 1
list_id: 1
index: 16
hex_color: f0f0f0
created: 1543626724
updated: 1543626724
- id: 32
text: 'task #32'
created_by_id: 1
list_id: 3
index: 1
created: 1543626724
updated: 1543626724
- id: 33
text: 'task #33 with percent done'
created_by_id: 1
list_id: 1
index: 17
percent_done: 0.5
created: 1543626724
updated: 1543626724

View File

@ -1,48 +0,0 @@
- id: 1
team_id: 1
list_id: 3
right: 0
updated: 0
created: 0
# This team has read only access on list 6
- id: 2
team_id: 2
list_id: 6
right: 0
updated: 0
created: 0
# This team has write access on list 7
- id: 3
team_id: 3
list_id: 7
right: 1
updated: 0
created: 0
# This team has admin access on list 8
- id: 4
team_id: 4
list_id: 8
right: 2
updated: 0
created: 0
# Readonly acces on list 19
- id: 5
team_id: 8
list_id: 19
right: 2
updated: 0
created: 0
# Write acces on list 19
- id: 6
team_id: 9
list_id: 19
right: 1
updated: 0
created: 0
# Admin acces on list 19
- id: 7
team_id: 10
list_id: 19
right: 2
updated: 0
created: 0

View File

@ -1,57 +0,0 @@
-
team_id: 1
user_id: 1
admin: true
created: 0
-
team_id: 1
user_id: 2
created: 0
-
team_id: 2
user_id: 1
created: 0
-
team_id: 3
user_id: 1
created: 0
-
team_id: 4
user_id: 1
created: 0
-
team_id: 5
user_id: 1
created: 0
-
team_id: 6
user_id: 1
created: 0
-
team_id: 7
user_id: 1
created: 0
-
team_id: 8
user_id: 1
created: 0
-
team_id: 9
user_id: 2
created: 0
-
team_id: 10
user_id: 3
created: 0
-
team_id: 11
user_id: 8
created: 0
-
team_id: 12
user_id: 9
created: 0
-
team_id: 13
user_id: 10
created: 0

View File

@ -1,52 +0,0 @@
- id: 1
team_id: 1
namespace_id: 3
right: 0
updated: 0
created: 0
- id: 2
team_id: 2
namespace_id: 3
right: 0
updated: 0
created: 0
- id: 3
team_id: 5
namespace_id: 7
right: 0
updated: 0
created: 0
- id: 4
team_id: 6
namespace_id: 8
right: 1
updated: 0
created: 0
- id: 5
team_id: 7
namespace_id: 9
right: 2
updated: 0
created: 0
- id: 6
team_id: 11
namespace_id: 14
right: 0
updated: 0
created: 0
- id: 7
team_id: 12
namespace_id: 14
right: 1
updated: 0
created: 0
- id: 8
team_id: 13
namespace_id: 14
right: 2
updated: 0
created: 0

View File

@ -1,40 +0,0 @@
- id: 1
name: testteam1
description: Lorem Ipsum
created_by_id: 1
- id: 2
name: testteam2_read_only_on_list6
created_by_id: 1
- id: 3
name: testteam3_write_on_list7
created_by_id: 1
- id: 4
name: testteam4_admin_on_list8
created_by_id: 1
- id: 5
name: testteam2_read_only_on_namespace7
created_by_id: 1
- id: 6
name: testteam3_write_on_namespace8
created_by_id: 1
- id: 7
name: testteam4_admin_on_namespace9
created_by_id: 1
- id: 8
name: testteam8
created_by_id: 7
- id: 9
name: testteam9
created_by_id: 7
- id: 10
name: testteam10
created_by_id: 7
- id: 11
name: testteam11
created_by_id: 7
- id: 12
name: testteam12
created_by_id: 7
- id: 13
name: testteam13
created_by_id: 7

View File

@ -1,97 +0,0 @@
-
id: 1
username: 'user1'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user1@example.com'
is_active: true
updated: 0
created: 0
-
id: 2
username: 'user2'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user2@example.com'
updated: 0
created: 0
-
id: 3
username: 'user3'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user3@example.com'
password_reset_token: passwordresettesttoken
updated: 0
created: 0
-
id: 4
username: 'user4'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user4@example.com'
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
updated: 0
created: 0
-
id: 5
username: 'user5'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user5@example.com'
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
is_active: false
updated: 0
created: 0
# This use is used to create a whole bunch of lists which are then shared directly with a user
- id: 6
username: 'user6'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user6@example.com'
is_active: true
updated: 0
created: 0
- id: 7
username: 'user7'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user7@example.com'
is_active: true
updated: 0
created: 0
- id: 8
username: 'user8'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user8@example.com'
is_active: true
updated: 0
created: 0
- id: 9
username: 'user9'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user9@example.com'
is_active: true
updated: 0
created: 0
- id: 10
username: 'user10'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user10@example.com'
is_active: true
updated: 0
created: 0
- id: 11
username: 'user11'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user11@example.com'
is_active: true
updated: 0
created: 0
- id: 12
username: 'user12'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user12@example.com'
is_active: true
updated: 0
created: 0
- id: 13
username: 'user13'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user14@example.com'
is_active: true
updated: 0
created: 0

View File

@ -1,48 +0,0 @@
- id: 1
user_id: 1
list_id: 3
right: 0
updated: 0
created: 0
- id: 2
user_id: 2
list_id: 3
right: 0
updated: 0
created: 0
- id: 3
user_id: 1
list_id: 9
right: 0
updated: 0
created: 0
- id: 4
user_id: 1
list_id: 10
right: 1
updated: 0
created: 0
- id: 5
user_id: 1
list_id: 11
right: 2
updated: 0
created: 0
- id: 6
user_id: 4
list_id: 19
right: 0
updated: 0
created: 0
- id: 7
user_id: 5
list_id: 19
right: 1
updated: 0
created: 0
- id: 8
user_id: 6
list_id: 19
right: 2
updated: 0
created: 0

View File

@ -1,52 +0,0 @@
- id: 1
user_id: 1
namespace_id: 3
right: 0
updated: 0
created: 0
- id: 2
user_id: 2
namespace_id: 3
right: 0
updated: 0
created: 0
- id: 3
user_id: 1
namespace_id: 10
right: 0
updated: 0
created: 0
- id: 4
user_id: 1
namespace_id: 11
right: 1
updated: 0
created: 0
- id: 5
user_id: 1
namespace_id: 12
right: 2
updated: 0
created: 0
- id: 6
user_id: 11
namespace_id: 14
right: 0
updated: 0
created: 0
- id: 7
user_id: 12
namespace_id: 14
right: 1
updated: 0
created: 0
- id: 8
user_id: 13
namespace_id: 14
right: 2
updated: 0
created: 0

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"time"
)
@ -34,7 +35,7 @@ type Label struct {
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
// The user who created this label
CreatedBy *User `xorm:"-" json:"created_by"`
CreatedBy *user.User `xorm:"-" json:"created_by"`
// A unix timestamp when this label was created. You cannot change this value.
Created int64 `xorm:"created not null" json:"created"`
@ -63,7 +64,7 @@ func (Label) TableName() string {
// @Failure 500 {object} models.Message "Internal error"
// @Router /labels [put]
func (l *Label) Create(a web.Auth) (err error) {
u, err := getUserWithError(a)
u, err := user.GetFromAuth(a)
if err != nil {
return
}
@ -136,7 +137,7 @@ func (l *Label) ReadAll(a web.Auth, search string, page int, perPage int) (ls in
return nil, 0, 0, ErrGenericForbidden{}
}
u := &User{ID: a.GetID()}
u := &user.User{ID: a.GetID()}
// Get all tasks
taskIDs, err := getUserTaskIDs(u)
@ -175,7 +176,7 @@ func (l *Label) ReadOne() (err error) {
}
*l = *label
user, err := GetUserByID(l.CreatedByID)
user, err := user.GetUserByID(l.CreatedByID)
if err != nil {
return err
}
@ -198,7 +199,7 @@ func getLabelByIDSimple(labelID int64) (*Label, error) {
}
// Helper method to get all task ids a user has
func getUserTaskIDs(u *User) (taskIDs []int64, err error) {
func getUserTaskIDs(u *user.User) (taskIDs []int64, err error) {
// Get all lists
lists, _, _, err := getRawListsForUser("", u, -1, 0)

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/go-xorm/builder"
)
@ -65,7 +66,7 @@ func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
// TODO: add an extra check for link share handling
// Get all tasks
taskIDs, err := getUserTaskIDs(&User{ID: a.GetID()})
taskIDs, err := getUserTaskIDs(&user.User{ID: a.GetID()})
if err != nil {
return false, err
}

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/go-xorm/builder"
)
@ -120,7 +121,7 @@ func (lt *LabelTask) ReadAll(a web.Auth, search string, page int, perPage int) (
}
return getLabelsByTaskIDs(&LabelByTaskIDsOptions{
User: &User{ID: a.GetID()},
User: &user.User{ID: a.GetID()},
Search: search,
Page: page,
TaskIDs: []int64{lt.TaskID},
@ -135,7 +136,7 @@ type labelWithTaskID struct {
// LabelByTaskIDsOptions is a struct to not clutter the function with too many optional parameters.
type LabelByTaskIDsOptions struct {
User *User
User *user.User
Search string
Page int
PerPage int
@ -185,7 +186,7 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
for _, l := range labels {
userids = append(userids, l.CreatedByID)
}
users := make(map[int64]*User)
users := make(map[int64]*user.User)
err = x.In("id", userids).Find(&users)
if err != nil {
return nil, 0, 0, err
@ -290,7 +291,7 @@ func (t *Task) updateTaskLabels(creator web.Auth, labels []*Label) (err error) {
return err
}
if !hasAccessToLabel {
user, _ := creator.(*User)
user, _ := creator.(*user.User)
return ErrUserHasNoAccessToLabel{LabelID: l.ID, UserID: user.ID}
}

View File

@ -1,6 +1,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"gopkg.in/d4l3k/messagediff.v1"
"reflect"
"runtime"
@ -37,7 +39,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
TaskID: 1,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantLabels: []*labelWithTaskID{
{
@ -46,7 +48,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
ID: 4,
Title: "Label #4 - visible via other task",
CreatedByID: 2,
CreatedBy: &User{
CreatedBy: &user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -62,7 +64,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
TaskID: 14,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantErr: true,
errType: IsErrNoRightToSeeTask,
@ -73,7 +75,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
TaskID: 9999,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantErr: true,
errType: IsErrTaskDoesNotExist,
@ -81,6 +83,8 @@ func TestLabelTask_ReadAll(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
@ -131,17 +135,17 @@ func TestLabelTask_Create(t *testing.T) {
LabelID: 1,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
},
{
name: "already existing",
fields: fields{
TaskID: 1,
LabelID: 1,
LabelID: 4,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantErr: true,
errType: IsErrLabelIsAlreadyOnTask,
@ -153,7 +157,7 @@ func TestLabelTask_Create(t *testing.T) {
LabelID: 9999,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantForbidden: true,
},
@ -164,7 +168,7 @@ func TestLabelTask_Create(t *testing.T) {
LabelID: 1,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantForbidden: true,
wantErr: true,
@ -173,6 +177,8 @@ func TestLabelTask_Create(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,
@ -217,9 +223,9 @@ func TestLabelTask_Delete(t *testing.T) {
name: "normal",
fields: fields{
TaskID: 1,
LabelID: 1,
LabelID: 4,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
{
name: "delete nonexistant",
@ -227,7 +233,7 @@ func TestLabelTask_Delete(t *testing.T) {
TaskID: 1,
LabelID: 1,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
{
@ -236,7 +242,7 @@ func TestLabelTask_Delete(t *testing.T) {
TaskID: 1,
LabelID: 9999,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
{
@ -245,7 +251,7 @@ func TestLabelTask_Delete(t *testing.T) {
TaskID: 9999,
LabelID: 1,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
{
@ -254,12 +260,14 @@ func TestLabelTask_Delete(t *testing.T) {
TaskID: 14,
LabelID: 1,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
l := &LabelTask{
ID: tt.fields.ID,
TaskID: tt.fields.TaskID,

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"gopkg.in/d4l3k/messagediff.v1"
"reflect"
"runtime"
@ -32,7 +33,7 @@ func TestLabel_ReadAll(t *testing.T) {
Description string
HexColor string
CreatedByID int64
CreatedBy *User
CreatedBy *user.User
Created int64
Updated int64
CRUDable web.CRUDable
@ -43,7 +44,7 @@ func TestLabel_ReadAll(t *testing.T) {
a web.Auth
page int
}
user1 := &User{
user1 := &user.User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -60,7 +61,7 @@ func TestLabel_ReadAll(t *testing.T) {
{
name: "normal",
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
wantLs: []*labelWithTaskID{
{
@ -85,7 +86,7 @@ func TestLabel_ReadAll(t *testing.T) {
ID: 4,
Title: "Label #4 - visible via other task",
CreatedByID: 2,
CreatedBy: &User{
CreatedBy: &user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -98,7 +99,7 @@ func TestLabel_ReadAll(t *testing.T) {
{
name: "invalid user",
args: args{
a: &User{ID: -1},
a: &user.User{ID: -1},
},
wantErr: true,
},
@ -136,13 +137,13 @@ func TestLabel_ReadOne(t *testing.T) {
Description string
HexColor string
CreatedByID int64
CreatedBy *User
CreatedBy *user.User
Created int64
Updated int64
CRUDable web.CRUDable
Rights web.Rights
}
user1 := &User{
user1 := &user.User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -169,7 +170,7 @@ func TestLabel_ReadOne(t *testing.T) {
CreatedByID: 1,
CreatedBy: user1,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
{
name: "Get nonexistant label",
@ -179,7 +180,7 @@ func TestLabel_ReadOne(t *testing.T) {
wantErr: true,
errType: IsErrLabelDoesNotExist,
wantForbidden: true,
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
{
name: "no rights",
@ -187,7 +188,7 @@ func TestLabel_ReadOne(t *testing.T) {
ID: 3,
},
wantForbidden: true,
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
{
name: "Get label #4 - other user",
@ -198,14 +199,14 @@ func TestLabel_ReadOne(t *testing.T) {
ID: 4,
Title: "Label #4 - visible via other task",
CreatedByID: 2,
CreatedBy: &User{
CreatedBy: &user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
AvatarURL: "ab53a2911ddf9b4817ac01ddcd3d975f",
},
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
}
for _, tt := range tests {
@ -248,7 +249,7 @@ func TestLabel_Create(t *testing.T) {
Description string
HexColor string
CreatedByID int64
CreatedBy *User
CreatedBy *user.User
Created int64
Updated int64
CRUDable web.CRUDable
@ -272,7 +273,7 @@ func TestLabel_Create(t *testing.T) {
HexColor: "ffccff",
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
},
}
@ -308,7 +309,7 @@ func TestLabel_Update(t *testing.T) {
Description string
HexColor string
CreatedByID int64
CreatedBy *User
CreatedBy *user.User
Created int64
Updated int64
CRUDable web.CRUDable
@ -327,7 +328,7 @@ func TestLabel_Update(t *testing.T) {
ID: 1,
Title: "new and better",
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
{
name: "nonexisting",
@ -335,7 +336,7 @@ func TestLabel_Update(t *testing.T) {
ID: 99999,
Title: "new and better",
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
wantErr: true,
},
@ -345,7 +346,7 @@ func TestLabel_Update(t *testing.T) {
ID: 3,
Title: "new and better",
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
{
@ -354,7 +355,7 @@ func TestLabel_Update(t *testing.T) {
ID: 4,
Title: "new and better",
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
}
@ -390,7 +391,7 @@ func TestLabel_Delete(t *testing.T) {
Description string
HexColor string
CreatedByID int64
CreatedBy *User
CreatedBy *user.User
Created int64
Updated int64
CRUDable web.CRUDable
@ -409,14 +410,14 @@ func TestLabel_Delete(t *testing.T) {
fields: fields{
ID: 1,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
},
{
name: "nonexisting",
fields: fields{
ID: 99999,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true, // When the label does not exist, it is forbidden. We should fix this, but for everything.
},
{
@ -424,7 +425,7 @@ func TestLabel_Delete(t *testing.T) {
fields: fields{
ID: 3,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
{
@ -432,7 +433,7 @@ func TestLabel_Delete(t *testing.T) {
fields: fields{
ID: 4,
},
auth: &User{ID: 1},
auth: &user.User{ID: 1},
wantForbidden: true,
},
}

View File

@ -18,6 +18,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/web"
"github.com/dgrijalva/jwt-go"
@ -48,8 +49,8 @@ type LinkSharing struct {
SharingType SharingType `xorm:"int(11) INDEX not null default 0" json:"sharing_type" valid:"length(0|2)" maximum:"2" default:"0"`
// The user who shared this list
SharedBy *User `xorm:"-" json:"shared_by"`
SharedByID int64 `xorm:"int(11) INDEX not null" json:"-"`
SharedBy *user.User `xorm:"-" json:"shared_by"`
SharedByID int64 `xorm:"int(11) INDEX not null" json:"-"`
// A unix timestamp when this list was shared. You cannot change this value.
Created int64 `xorm:"created not null" json:"created"`
@ -100,7 +101,7 @@ func (share *LinkSharing) Create(a web.Auth) (err error) {
share.SharedByID = a.GetID()
share.Hash = utils.MakeRandomString(40)
_, err = x.Insert(share)
share.SharedBy, _ = a.(*User)
share.SharedBy, _ = a.(*user.User)
return
}
@ -168,7 +169,7 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
userIDs = append(userIDs, s.SharedByID)
}
users := make(map[int64]*User)
users := make(map[int64]*user.User)
err = x.In("id", userIDs).Find(&users)
if err != nil {
return nil, 0, 0, err

View File

@ -18,6 +18,7 @@ package models
import (
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
)
@ -36,7 +37,7 @@ type List struct {
NamespaceID int64 `xorm:"int(11) INDEX not null" json:"-" param:"namespace"`
// The user who created this list.
Owner *User `xorm:"-" json:"owner" valid:"-"`
Owner *user.User `xorm:"-" json:"owner" valid:"-"`
// An array of tasks which belong to the list.
// Deprecated: you should use the dedicated task list endpoint because it has support for pagination and filtering
Tasks []*Task `xorm:"-" json:"-"`
@ -51,7 +52,7 @@ type List struct {
}
// GetListsByNamespaceID gets all lists in a namespace
func GetListsByNamespaceID(nID int64, doer *User) (lists []*List, err error) {
func GetListsByNamespaceID(nID int64, doer *user.User) (lists []*List, err error) {
if nID == -1 {
err = x.Select("l.*").
Table("list").
@ -103,7 +104,7 @@ func (l *List) ReadAll(a web.Auth, search string, page int, perPage int) (result
return lists, 0, 0, err
}
lists, resultCount, totalItems, err := getRawListsForUser(search, &User{ID: a.GetID()}, page, perPage)
lists, resultCount, totalItems, err := getRawListsForUser(search, &user.User{ID: a.GetID()}, page, perPage)
if err != nil {
return nil, 0, 0, err
}
@ -127,7 +128,7 @@ func (l *List) ReadAll(a web.Auth, search string, page int, perPage int) (result
// @Router /lists/{id} [get]
func (l *List) ReadOne() (err error) {
// Get list owner
l.Owner, err = GetUserByID(l.OwnerID)
l.Owner, err = user.GetUserByID(l.OwnerID)
return
}
@ -176,8 +177,8 @@ func GetListSimplByTaskID(taskID int64) (l *List, err error) {
}
// Gets the lists only, without any tasks or so
func getRawListsForUser(search string, u *User, page int, perPage int) (lists []*List, resultCount int, totalItems int64, err error) {
fullUser, err := GetUserByID(u.ID)
func getRawListsForUser(search string, u *user.User, page int, perPage int) (lists []*List, resultCount int, totalItems int64, err error) {
fullUser, err := user.GetUserByID(u.ID)
if err != nil {
return nil, 0, 0, err
}
@ -237,7 +238,7 @@ func AddListDetails(lists []*List) (err error) {
}
// Get all list owners
owners := []*User{}
owners := []*user.User{}
err = x.In("id", ownerIDs).Find(&owners)
if err != nil {
return
@ -348,7 +349,7 @@ func updateListByTaskID(taskID int64) (err error) {
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{namespaceID}/lists [put]
func (l *List) Create(a web.Auth) (err error) {
doer, err := getUserWithError(a)
doer, err := user.GetFromAuth(a)
if err != nil {
return err
}

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/go-xorm/builder"
)
@ -39,7 +40,7 @@ func (l *List) CanWrite(a web.Auth) (bool, error) {
}
// Check if the user is either owner or can write to the list
if originalList.isOwner(&User{ID: a.GetID()}) {
if originalList.isOwner(&user.User{ID: a.GetID()}) {
return true, nil
}
@ -60,7 +61,7 @@ func (l *List) CanRead(a web.Auth) (bool, error) {
(shareAuth.Right == RightRead || shareAuth.Right == RightWrite || shareAuth.Right == RightAdmin), nil
}
if l.isOwner(&User{ID: a.GetID()}) {
if l.isOwner(&user.User{ID: a.GetID()}) {
return true, nil
}
return l.checkRight(a, RightRead, RightWrite, RightAdmin)
@ -100,14 +101,14 @@ func (l *List) IsAdmin(a web.Auth) (bool, error) {
// Check all the things
// Check if the user is either owner or can write to the list
// Owners are always admins
if originalList.isOwner(&User{ID: a.GetID()}) {
if originalList.isOwner(&user.User{ID: a.GetID()}) {
return true, nil
}
return originalList.checkRight(a, RightAdmin)
}
// Little helper function to check if a user is list owner
func (l *List) isOwner(u *User) bool {
func (l *List) isOwner(u *user.User) bool {
return l.OwnerID == u.ID
}

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/stretchr/testify/assert"
"reflect"
@ -25,6 +27,8 @@ import (
)
func TestTeamList(t *testing.T) {
db.LoadAndAssertFixtures(t)
// Dummy relation
tl := TeamList{
TeamID: 1,
@ -33,7 +37,7 @@ func TestTeamList(t *testing.T) {
}
// Dummyuser
u, err := GetUserByID(1)
u, err := user.GetUserByID(1)
assert.NoError(t, err)
// Check normal creation
@ -164,6 +168,8 @@ func TestTeamList_Update(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
tl := &TeamList{
ID: tt.fields.ID,
TeamID: tt.fields.TeamID,

View File

@ -17,13 +17,15 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
)
func TestList_CreateOrUpdate(t *testing.T) {
user := &User{
usr := &user.User{
ID: 1,
Username: "user1",
Email: "user1@example.com",
@ -31,41 +33,41 @@ func TestList_CreateOrUpdate(t *testing.T) {
t.Run("create", func(t *testing.T) {
t.Run("normal", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
list := List{
Title: "test",
Description: "Lorem Ipsum",
NamespaceID: 1,
}
err := list.Create(user)
err := list.Create(usr)
assert.NoError(t, err)
})
t.Run("nonexistant namespace", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
list := List{
Title: "test",
Description: "Lorem Ipsum",
NamespaceID: 999999,
}
err := list.Create(user)
err := list.Create(usr)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
})
t.Run("nonexistant owner", func(t *testing.T) {
initFixtures(t)
user := &User{ID: 9482385}
db.LoadAndAssertFixtures(t)
usr := &user.User{ID: 9482385}
list := List{
Title: "test",
Description: "Lorem Ipsum",
NamespaceID: 1,
}
err := list.Create(user)
err := list.Create(usr)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, user.IsErrUserDoesNotExist(err))
})
t.Run("existing identifier", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
list := List{
Title: "test",
Description: "Lorem Ipsum",
@ -73,7 +75,7 @@ func TestList_CreateOrUpdate(t *testing.T) {
NamespaceID: 1,
}
err := list.Create(user)
err := list.Create(usr)
assert.Error(t, err)
assert.True(t, IsErrListIdentifierIsNotUnique(err))
})
@ -81,7 +83,7 @@ func TestList_CreateOrUpdate(t *testing.T) {
t.Run("update", func(t *testing.T) {
t.Run("normal", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
list := List{
ID: 1,
Title: "test",
@ -94,7 +96,7 @@ func TestList_CreateOrUpdate(t *testing.T) {
})
t.Run("nonexistant", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
list := List{
ID: 99999999,
Title: "test",
@ -105,7 +107,7 @@ func TestList_CreateOrUpdate(t *testing.T) {
})
t.Run("existing identifier", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
list := List{
Title: "test",
Description: "Lorem Ipsum",
@ -113,7 +115,7 @@ func TestList_CreateOrUpdate(t *testing.T) {
NamespaceID: 1,
}
err := list.Create(user)
err := list.Create(usr)
assert.Error(t, err)
assert.True(t, IsErrListIdentifierIsNotUnique(err))
})
@ -121,7 +123,7 @@ func TestList_CreateOrUpdate(t *testing.T) {
}
func TestList_Delete(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
list := List{
ID: 1,
}
@ -131,14 +133,16 @@ func TestList_Delete(t *testing.T) {
func TestList_ReadAll(t *testing.T) {
t.Run("all in namespace", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
// Get all lists for our namespace
lists, err := GetListsByNamespaceID(1, &User{})
lists, err := GetListsByNamespaceID(1, &user.User{})
assert.NoError(t, err)
assert.Equal(t, len(lists), 2)
})
t.Run("all lists for user", func(t *testing.T) {
u := &User{ID: 1}
db.LoadAndAssertFixtures(t)
u := &user.User{ID: 1}
list := List{}
lists3, _, _, err := list.ReadAll(u, "", 1, 50)
@ -148,10 +152,12 @@ func TestList_ReadAll(t *testing.T) {
assert.Equal(t, 16, s.Len())
})
t.Run("lists for nonexistant user", func(t *testing.T) {
user := &User{ID: 999999}
db.LoadAndAssertFixtures(t)
usr := &user.User{ID: 999999}
list := List{}
_, _, _, err := list.ReadAll(user, "", 1, 50)
_, _, _, err := list.ReadAll(usr, "", 1, 50)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, user.IsErrUserDoesNotExist(err))
})
}

View File

@ -16,7 +16,10 @@
package models
import "code.vikunja.io/web"
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
)
// ListUser represents a list <-> user relation
type ListUser struct {
@ -47,8 +50,8 @@ func (ListUser) TableName() string {
// UserWithRight represents a user in combination with the right it can have on a list/namespace
type UserWithRight struct {
User `xorm:"extends"`
Right Right `json:"right"`
user.User `xorm:"extends"`
Right Right `json:"right"`
}
// Create creates a new list <-> user relation
@ -80,7 +83,7 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
}
// Check if the user exists
user, err := GetUserByUsername(lu.Username)
user, err := user.GetUserByUsername(lu.Username)
if err != nil {
return err
}
@ -126,7 +129,7 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
func (lu *ListUser) Delete() (err error) {
// Check if the user exists
user, err := GetUserByUsername(lu.Username)
user, err := user.GetUserByUsername(lu.Username)
if err != nil {
return
}
@ -227,7 +230,7 @@ func (lu *ListUser) Update() (err error) {
}
// Check if the user exists
user, err := GetUserByUsername(lu.Username)
user, err := user.GetUserByUsername(lu.Username)
if err != nil {
return err
}

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"testing"
"code.vikunja.io/web"
@ -48,7 +50,7 @@ func TestListUser_CanDoSomething(t *testing.T) {
ListID: 3,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: map[string]bool{"CanCreate": true, "CanDelete": true, "CanUpdate": true},
},
@ -58,7 +60,7 @@ func TestListUser_CanDoSomething(t *testing.T) {
ListID: 300,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
},
@ -68,13 +70,15 @@ func TestListUser_CanDoSomething(t *testing.T) {
ListID: 3,
},
args: args{
a: &User{ID: 4},
a: &user.User{ID: 4},
},
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
lu := &ListUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"gopkg.in/d4l3k/messagediff.v1"
"reflect"
"runtime"
@ -58,7 +60,7 @@ func TestListUser_Create(t *testing.T) {
name: "ListUsers Create for duplicate",
fields: fields{
Username: "user1",
ListID: 2,
ListID: 3,
},
wantErr: true,
errType: IsErrUserAlreadyHasAccess,
@ -89,7 +91,7 @@ func TestListUser_Create(t *testing.T) {
ListID: 2,
},
wantErr: true,
errType: IsErrUserDoesNotExist,
errType: user.IsErrUserDoesNotExist,
},
{
name: "ListUsers Create with the owner as shared user",
@ -103,6 +105,8 @@ func TestListUser_Create(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
ul := &ListUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,
@ -155,11 +159,11 @@ func TestListUser_ReadAll(t *testing.T) {
ListID: 3,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: []*UserWithRight{
{
User: User{
User: user.User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -169,7 +173,7 @@ func TestListUser_ReadAll(t *testing.T) {
Right: RightRead,
},
{
User: User{
User: user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -185,7 +189,7 @@ func TestListUser_ReadAll(t *testing.T) {
ListID: 3,
},
args: args{
a: &User{ID: 4},
a: &user.User{ID: 4},
},
wantErr: true,
errType: IsErrNeedToHaveListReadAccess,
@ -193,6 +197,8 @@ func TestListUser_ReadAll(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
ul := &ListUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,
@ -271,6 +277,8 @@ func TestListUser_Update(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
lu := &ListUser{
ID: tt.fields.ID,
Username: tt.fields.Username,
@ -316,7 +324,7 @@ func TestListUser_Delete(t *testing.T) {
ListID: 2,
},
wantErr: true,
errType: IsErrUserDoesNotExist,
errType: user.IsErrUserDoesNotExist,
},
{
name: "Try deleting a user which does not has access but exists",
@ -337,6 +345,8 @@ func TestListUser_Delete(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
lu := &ListUser{
ID: tt.fields.ID,
Username: tt.fields.Username,

View File

@ -19,6 +19,7 @@ package models
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/user"
"os"
"testing"
)
@ -33,7 +34,9 @@ func TestMain(m *testing.M) {
// Some tests use the file engine, so we'll need to initialize that
files.InitTests()
SetupTests(config.ServiceRootpath.GetString())
user.InitTests()
SetupTests()
os.Exit(m.Run())
}

View File

@ -20,6 +20,7 @@ import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user"
_ "github.com/go-sql-driver/mysql" // Because.
"github.com/go-xorm/xorm"
@ -33,7 +34,7 @@ var (
// GetTables returns all structs which are also a table.
func GetTables() []interface{} {
return []interface{}{
&User{},
&user.User{},
&List{},
&Task{},
&Team{},

View File

@ -18,6 +18,7 @@ package models
import (
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/imdario/mergo"
"time"
@ -34,7 +35,7 @@ type Namespace struct {
OwnerID int64 `xorm:"int(11) not null INDEX" json:"-"`
// The user who owns this namespace
Owner *User `xorm:"-" json:"owner" valid:"-"`
Owner *user.User `xorm:"-" json:"owner" valid:"-"`
// A unix timestamp when this namespace was created. You cannot change this value.
Created int64 `xorm:"created not null" json:"created"`
@ -97,7 +98,7 @@ func GetNamespaceByID(id int64) (namespace Namespace, err error) {
}
// Get the namespace Owner
namespace.Owner, err = GetUserByID(namespace.OwnerID)
namespace.Owner, err = user.GetUserByID(namespace.OwnerID)
return
}
@ -115,7 +116,7 @@ func GetNamespaceByID(id int64) (namespace Namespace, err error) {
// @Router /namespaces/{id} [get]
func (n *Namespace) ReadOne() (err error) {
// Get the namespace Owner
n.Owner, err = GetUserByID(n.OwnerID)
n.Owner, err = user.GetUserByID(n.OwnerID)
return
}
@ -143,7 +144,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
return nil, 0, 0, ErrGenericForbidden{}
}
doer, err := getUserWithError(a)
doer, err := user.GetFromAuth(a)
if err != nil {
return nil, 0, 0, err
}
@ -176,7 +177,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
}
// Get all users
users := []*User{}
users := []*user.User{}
err = x.Select("users.*").
Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
@ -301,7 +302,7 @@ func (n *Namespace) Create(a web.Auth) (err error) {
n.ID = 0 // This would otherwise prevent the creation of new lists after one was created
// Check if the User exists
n.Owner, err = GetUserByID(a.GetID())
n.Owner, err = user.GetUserByID(a.GetID())
if err != nil {
return
}
@ -343,7 +344,7 @@ func (n *Namespace) Delete() (err error) {
}
// Delete all lists with their tasks
lists, err := GetListsByNamespaceID(n.ID, &User{})
lists, err := GetListsByNamespaceID(n.ID, &user.User{})
if err != nil {
return
}
@ -401,7 +402,7 @@ func (n *Namespace) Update() (err error) {
// Check if the (new) owner exists
n.OwnerID = n.Owner.ID
if currentNamespace.OwnerID != n.OwnerID {
n.Owner, err = GetUserByID(n.OwnerID)
n.Owner, err = user.GetUserByID(n.OwnerID)
if err != nil {
return
}

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"testing"
"code.vikunja.io/web"
@ -48,7 +50,7 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
NamespaceID: 3,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: map[string]bool{"CanCreate": true, "CanDelete": true, "CanUpdate": true},
},
@ -58,7 +60,7 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
NamespaceID: 300,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
},
@ -68,13 +70,15 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
NamespaceID: 3,
},
args: args{
a: &User{ID: 4},
a: &user.User{ID: 4},
},
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
tn := &TeamNamespace{
ID: tt.fields.ID,
TeamID: tt.fields.TeamID,

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/stretchr/testify/assert"
"reflect"
@ -25,6 +27,8 @@ import (
)
func TestTeamNamespace(t *testing.T) {
db.LoadAndAssertFixtures(t)
// Dummy team <-> namespace relation
tn := TeamNamespace{
TeamID: 1,
@ -32,7 +36,7 @@ func TestTeamNamespace(t *testing.T) {
Right: RightAdmin,
}
dummyuser, err := GetUserByID(1)
dummyuser, err := user.GetUserByID(1)
assert.NoError(t, err)
// Test normal creation
@ -80,7 +84,7 @@ func TestTeamNamespace(t *testing.T) {
assert.True(t, IsErrNamespaceDoesNotExist(err))
// Check with no right to read the namespace
nouser := &User{ID: 393}
nouser := &user.User{ID: 393}
_, _, _, err = tn.ReadAll(nouser, "", 1, 50)
assert.Error(t, err)
assert.True(t, IsErrNeedToHaveNamespaceReadAccess(err))
@ -156,6 +160,8 @@ func TestTeamNamespace_Update(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
tl := &TeamNamespace{
ID: tt.fields.ID,
TeamID: tt.fields.TeamID,

View File

@ -17,12 +17,16 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
)
func TestNamespace_Create(t *testing.T) {
db.LoadAndAssertFixtures(t)
// Create test database
//assert.NoError(t, LoadFixtures())
@ -33,7 +37,7 @@ func TestNamespace_Create(t *testing.T) {
}
// Doer
doer, err := GetUserByID(1)
doer, err := user.GetUserByID(1)
assert.NoError(t, err)
// Try creating it
@ -57,11 +61,11 @@ func TestNamespace_Create(t *testing.T) {
assert.True(t, IsErrNamespaceNameCannotBeEmpty(err))
// Try inserting one with a nonexistant user
nUser := &User{ID: 9482385}
nUser := &user.User{ID: 9482385}
dnsp2 := dummynamespace
err = dnsp2.Create(nUser)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, user.IsErrUserDoesNotExist(err))
// Update it
allowed, err = dummynamespace.CanUpdate(doer)
@ -85,7 +89,7 @@ func TestNamespace_Create(t *testing.T) {
dummynamespace.Owner.ID = 94829838572
err = dummynamespace.Update()
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, user.IsErrUserDoesNotExist(err))
// Try updating without a name
dummynamespace.Name = ""

View File

@ -16,7 +16,10 @@
package models
import "code.vikunja.io/web"
import (
user2 "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
)
// NamespaceUser represents a namespace <-> user relation
type NamespaceUser struct {
@ -75,7 +78,7 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
}
// Check if the user exists
user, err := GetUserByUsername(nu.Username)
user, err := user2.GetUserByUsername(nu.Username)
if err != nil {
return err
}
@ -117,7 +120,7 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
func (nu *NamespaceUser) Delete() (err error) {
// Check if the user exists
user, err := GetUserByUsername(nu.Username)
user, err := user2.GetUserByUsername(nu.Username)
if err != nil {
return
}
@ -213,7 +216,7 @@ func (nu *NamespaceUser) Update() (err error) {
}
// Check if the user exists
user, err := GetUserByUsername(nu.Username)
user, err := user2.GetUserByUsername(nu.Username)
if err != nil {
return err
}

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"testing"
"code.vikunja.io/web"
@ -48,7 +50,7 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
NamespaceID: 3,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: map[string]bool{"CanCreate": true, "CanDelete": true, "CanUpdate": true},
},
@ -58,7 +60,7 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
NamespaceID: 300,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
},
@ -68,13 +70,15 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
NamespaceID: 3,
},
args: args{
a: &User{ID: 4},
a: &user.User{ID: 4},
},
want: map[string]bool{"CanCreate": false, "CanDelete": false, "CanUpdate": false},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
nu := &NamespaceUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"gopkg.in/d4l3k/messagediff.v1"
"reflect"
@ -56,7 +58,7 @@ func TestNamespaceUser_Create(t *testing.T) {
name: "NamespaceUsers Create for duplicate",
fields: fields{
Username: "user1",
NamespaceID: 2,
NamespaceID: 3,
},
wantErr: true,
errType: IsErrUserAlreadyHasNamespaceAccess,
@ -87,7 +89,7 @@ func TestNamespaceUser_Create(t *testing.T) {
NamespaceID: 2,
},
wantErr: true,
errType: IsErrUserDoesNotExist,
errType: user.IsErrUserDoesNotExist,
},
{
name: "NamespaceUsers Create with the owner as shared user",
@ -101,6 +103,8 @@ func TestNamespaceUser_Create(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
un := &NamespaceUser{
ID: tt.fields.ID,
Username: tt.fields.Username,
@ -152,11 +156,11 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
NamespaceID: 3,
},
args: args{
a: &User{ID: 3},
a: &user.User{ID: 3},
},
want: []*UserWithRight{
{
User: User{
User: user.User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -166,7 +170,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
Right: RightRead,
},
{
User: User{
User: user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -182,7 +186,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
NamespaceID: 3,
},
args: args{
a: &User{ID: 4},
a: &user.User{ID: 4},
},
wantErr: true,
errType: IsErrNeedToHaveNamespaceReadAccess,
@ -190,6 +194,8 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
un := &NamespaceUser{
ID: tt.fields.ID,
UserID: tt.fields.UserID,
@ -269,6 +275,8 @@ func TestNamespaceUser_Update(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
nu := &NamespaceUser{
ID: tt.fields.ID,
Username: tt.fields.Username,
@ -314,7 +322,7 @@ func TestNamespaceUser_Delete(t *testing.T) {
NamespaceID: 2,
},
wantErr: true,
errType: IsErrUserDoesNotExist,
errType: user.IsErrUserDoesNotExist,
},
{
name: "Try deleting a user which does not has access but exists",
@ -335,6 +343,8 @@ func TestNamespaceUser_Delete(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
nu := &NamespaceUser{
ID: tt.fields.ID,
Username: tt.fields.Username,

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
)
@ -38,8 +39,8 @@ func (TaskAssginee) TableName() string {
// TaskAssigneeWithUser is a helper type to deal with user joins
type TaskAssigneeWithUser struct {
TaskID int64
User `xorm:"extends"`
TaskID int64
user.User `xorm:"extends"`
}
func getRawTaskAssigneesForTasks(taskIDs []int64) (taskAssignees []*TaskAssigneeWithUser, err error) {
@ -53,7 +54,7 @@ func getRawTaskAssigneesForTasks(taskIDs []int64) (taskAssignees []*TaskAssignee
}
// Create or update a bunch of task assignees
func (t *Task) updateTaskAssignees(assignees []*User) (err error) {
func (t *Task) updateTaskAssignees(assignees []*user.User) (err error) {
// Load the current assignees
currentAssignees, err := getRawTaskAssigneesForTasks([]int64{t.ID})
@ -61,7 +62,7 @@ func (t *Task) updateTaskAssignees(assignees []*User) (err error) {
return err
}
t.Assignees = make([]*User, 0, len(currentAssignees))
t.Assignees = make([]*user.User, 0, len(currentAssignees))
for _, assignee := range currentAssignees {
t.Assignees = append(t.Assignees, &assignee.User)
}
@ -80,7 +81,7 @@ func (t *Task) updateTaskAssignees(assignees []*User) (err error) {
}
// Make a hashmap of the new assignees for easier comparison
newAssignees := make(map[int64]*User, len(assignees))
newAssignees := make(map[int64]*user.User, len(assignees))
for _, newAssignee := range assignees {
newAssignees[newAssignee.ID] = newAssignee
}
@ -88,7 +89,7 @@ func (t *Task) updateTaskAssignees(assignees []*User) (err error) {
// Get old assignees to delete
var found bool
var assigneesToDelete []int64
oldAssignees := make(map[int64]*User, len(t.Assignees))
oldAssignees := make(map[int64]*user.User, len(t.Assignees))
for _, oldAssignee := range t.Assignees {
found = false
if newAssignees[oldAssignee.ID] != nil {
@ -142,7 +143,7 @@ func (t *Task) updateTaskAssignees(assignees []*User) (err error) {
}
// Small helper functions to set the new assignees in various places
func (t *Task) setTaskAssignees(assignees []*User) {
func (t *Task) setTaskAssignees(assignees []*user.User) {
if len(assignees) == 0 {
t.Assignees = nil
return
@ -200,7 +201,7 @@ func (la *TaskAssginee) Create(a web.Auth) (err error) {
func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
// Check if the user exists and has access to the list
newAssignee, err := GetUserByID(newAssigneeID)
newAssignee, err := user.GetUserByID(newAssigneeID)
if err != nil {
return err
}
@ -252,7 +253,7 @@ func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int
return nil, 0, 0, ErrGenericForbidden{}
}
var taskAssignees []*User
var taskAssignees []*user.User
err = x.Table("task_assignees").
Select("users.*").
Join("INNER", "users", "task_assignees.user_id = users.id").
@ -267,15 +268,15 @@ func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int
Select("users.*").
Join("INNER", "users", "task_assignees.user_id = users.id").
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
Count(&User{})
Count(&user.User{})
return taskAssignees, len(taskAssignees), numberOfTotalItems, err
}
// BulkAssignees is a helper struct used to update multiple assignees at once.
type BulkAssignees struct {
// A list with all assignees
Assignees []*User `json:"assignees"`
TaskID int64 `json:"-" param:"listtask"`
Assignees []*user.User `json:"assignees"`
TaskID int64 `json:"-" param:"listtask"`
web.CRUDable `json:"-"`
web.Rights `json:"-"`

View File

@ -18,6 +18,7 @@ package models
import (
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"io"
"time"
@ -29,8 +30,8 @@ type TaskAttachment struct {
TaskID int64 `xorm:"int(11) not null" json:"task_id" param:"task"`
FileID int64 `xorm:"int(11) not null" json:"-"`
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
CreatedBy *User `xorm:"-" json:"created_by"`
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
CreatedBy *user.User `xorm:"-" json:"created_by"`
File *files.File `xorm:"-" json:"file"`
@ -132,7 +133,7 @@ func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage i
return nil, 0, 0, err
}
us := make(map[int64]*User)
us := make(map[int64]*user.User)
err = x.In("id", userIDs).Find(&us)
if err != nil {
return nil, 0, 0, err

View File

@ -20,6 +20,7 @@ package models
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"io"
"os"
@ -95,7 +96,7 @@ func TestTaskAttachment_NewAttachment(t *testing.T) {
tf := &testfile{
content: []byte("testingstuff"),
}
testuser := &User{ID: 1}
testuser := &user.User{ID: 1}
err := ta.NewAttachment(tf, "testfile", 100, testuser)
assert.NoError(t, err)
@ -119,7 +120,7 @@ func TestTaskAttachment_NewAttachment(t *testing.T) {
func TestTaskAttachment_ReadAll(t *testing.T) {
files.InitTestFileFixtures(t)
ta := &TaskAttachment{TaskID: 1}
as, _, _, err := ta.ReadAll(&User{ID: 1}, "", 0, 50)
as, _, _, err := ta.ReadAll(&user.User{ID: 1}, "", 0, 50)
attachments, _ := as.([]*TaskAttachment)
assert.NoError(t, err)
assert.Len(t, attachments, 3)
@ -152,7 +153,7 @@ func TestTaskAttachment_Delete(t *testing.T) {
}
func TestTaskAttachment_Rights(t *testing.T) {
u := &User{ID: 1}
u := &user.User{ID: 1}
t.Run("Can Read", func(t *testing.T) {
t.Run("Allowed", func(t *testing.T) {
ta := &TaskAttachment{TaskID: 1}

View File

@ -18,6 +18,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"time"
)
@ -108,7 +109,7 @@ func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage i
// If the list ID is not set, we get all tasks for the user.
// This allows to use this function in Task.ReadAll with a possibility to deprecate the latter at some point.
if tf.ListID == 0 {
tf.Lists, _, _, err = getRawListsForUser("", &User{ID: a.GetID()}, -1, 0)
tf.Lists, _, _, err = getRawListsForUser("", &user.User{ID: a.GetID()}, -1, 0)
if err != nil {
return nil, 0, 0, err
}

View File

@ -19,30 +19,28 @@ package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/stretchr/testify/assert"
"gopkg.in/d4l3k/messagediff.v1"
"testing"
)
func TestTaskCollection_ReadAll(t *testing.T) {
assert.NoError(t, db.LoadFixtures())
// Dummy users
user1 := &User{
user1 := &user.User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "111d68d06e2d317b5a59c2c6c5bad808", // hash for ""
}
user2 := &User{
user2 := &user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
AvatarURL: "ab53a2911ddf9b4817ac01ddcd3d975f", // hash for ""
}
user6 := &User{
user6 := &user.User{
ID: 6,
Username: "user6",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -463,7 +461,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
CreatedByID: 1,
CreatedBy: user1,
ListID: 1,
Assignees: []*User{
Assignees: []*user.User{
user1,
user2,
},
@ -538,7 +536,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
fields: fields{},
args: args{
search: "",
a: &User{ID: 1},
a: &user.User{ID: 1},
page: 0,
},
want: []*Task{
@ -585,7 +583,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
},
args: args{
search: "",
a: &User{ID: 1},
a: &user.User{ID: 1},
page: 0,
},
want: []*Task{
@ -631,7 +629,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
},
args: args{
search: "",
a: &User{ID: 1},
a: &user.User{ID: 1},
page: 0,
},
want: []*Task{
@ -648,7 +646,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
},
args: args{
search: "",
a: &User{ID: 1},
a: &user.User{ID: 1},
page: 0,
},
want: []*Task{
@ -664,7 +662,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
},
args: args{
search: "",
a: &User{ID: 1},
a: &user.User{ID: 1},
page: 0,
},
want: []*Task{
@ -677,6 +675,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
lt := &TaskCollection{
ListID: tt.fields.ListID,
StartDateSortUnix: tt.fields.StartDateSortUnix,

View File

@ -18,6 +18,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
)
@ -83,7 +84,7 @@ type TaskRelation struct {
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
// The user who created this relation
CreatedBy *User `xorm:"-" json:"created_by"`
CreatedBy *user.User `xorm:"-" json:"created_by"`
// A unix timestamp when this label was created. You cannot change this value.
Created int64 `xorm:"created not null" json:"created"`

View File

@ -18,45 +18,55 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"testing"
)
func TestTaskRelation_Create(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 2,
RelationKind: RelationKindSubtask,
}
err := rel.Create(&User{ID: 1})
err := rel.Create(&user.User{ID: 1})
assert.NoError(t, err)
})
t.Run("Two Tasks In Different Lists", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 13,
RelationKind: RelationKindSubtask,
}
err := rel.Create(&User{ID: 1})
err := rel.Create(&user.User{ID: 1})
assert.NoError(t, err)
})
t.Run("Already Existing", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 29,
RelationKind: RelationKindSubtask,
}
err := rel.Create(&User{ID: 1})
err := rel.Create(&user.User{ID: 1})
assert.Error(t, err)
assert.True(t, IsErrRelationAlreadyExists(err))
})
t.Run("Same Task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 1,
}
err := rel.Create(&User{ID: 1})
err := rel.Create(&user.User{ID: 1})
assert.Error(t, err)
assert.True(t, IsErrRelationTasksCannotBeTheSame(err))
})
@ -64,6 +74,8 @@ func TestTaskRelation_Create(t *testing.T) {
func TestTaskRelation_Delete(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 29,
@ -73,6 +85,8 @@ func TestTaskRelation_Delete(t *testing.T) {
assert.NoError(t, err)
})
t.Run("Not existing", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 9999,
OtherTaskID: 3,
@ -86,73 +100,87 @@ func TestTaskRelation_Delete(t *testing.T) {
func TestTaskRelation_CanCreate(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 2,
RelationKind: RelationKindSubtask,
}
can, err := rel.CanCreate(&User{ID: 1})
can, err := rel.CanCreate(&user.User{ID: 1})
assert.NoError(t, err)
assert.True(t, can)
})
t.Run("Two tasks on different lists", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 13,
RelationKind: RelationKindSubtask,
}
can, err := rel.CanCreate(&User{ID: 1})
can, err := rel.CanCreate(&user.User{ID: 1})
assert.NoError(t, err)
assert.True(t, can)
})
t.Run("No update rights on base task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 14,
OtherTaskID: 1,
RelationKind: RelationKindSubtask,
}
can, err := rel.CanCreate(&User{ID: 1})
can, err := rel.CanCreate(&user.User{ID: 1})
assert.NoError(t, err)
assert.False(t, can)
})
t.Run("No update rights on base task, but read rights", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 15,
OtherTaskID: 1,
RelationKind: RelationKindSubtask,
}
can, err := rel.CanCreate(&User{ID: 1})
can, err := rel.CanCreate(&user.User{ID: 1})
assert.NoError(t, err)
assert.False(t, can)
})
t.Run("No read rights on other task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 14,
RelationKind: RelationKindSubtask,
}
can, err := rel.CanCreate(&User{ID: 1})
can, err := rel.CanCreate(&user.User{ID: 1})
assert.NoError(t, err)
assert.False(t, can)
})
t.Run("Nonexisting base task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 999999,
OtherTaskID: 1,
RelationKind: RelationKindSubtask,
}
can, err := rel.CanCreate(&User{ID: 1})
can, err := rel.CanCreate(&user.User{ID: 1})
assert.Error(t, err)
assert.True(t, IsErrTaskDoesNotExist(err))
assert.False(t, can)
})
t.Run("Nonexisting other task", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
rel := TaskRelation{
TaskID: 1,
OtherTaskID: 999999,
RelationKind: RelationKindSubtask,
}
can, err := rel.CanCreate(&User{ID: 1})
can, err := rel.CanCreate(&user.User{ID: 1})
assert.Error(t, err)
assert.True(t, IsErrTaskDoesNotExist(err))
assert.False(t, can)

View File

@ -19,6 +19,7 @@ package models
import (
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/web"
"github.com/imdario/mergo"
@ -55,7 +56,7 @@ type Task struct {
// When this task ends.
EndDateUnix int64 `xorm:"int(11) INDEX null" json:"endDate" query:"-"`
// An array of users who are assigned to this task
Assignees []*User `xorm:"-" json:"assignees"`
Assignees []*user.User `xorm:"-" json:"assignees"`
// An array of labels which are associated with this task.
Labels []*Label `xorm:"-" json:"labels"`
// The task color in hex
@ -87,7 +88,7 @@ type Task struct {
Updated int64 `xorm:"updated not null" json:"updated"`
// The user who initially created the task.
CreatedBy *User `xorm:"-" json:"createdBy" valid:"-"`
CreatedBy *user.User `xorm:"-" json:"createdBy" valid:"-"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
@ -365,7 +366,7 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (tasks []*Task, err error) {
// Get all users of a task
// aka the ones who created a task
users := make(map[int64]*User)
users := make(map[int64]*user.User)
err = x.In("id", userIDs).Find(&users)
if err != nil {
return
@ -487,7 +488,7 @@ func (t *Task) Create(a web.Auth) (err error) {
return
}
u, err := GetUserByID(a.GetID())
u, err := user.GetUserByID(a.GetID())
if err != nil {
return err
}

View File

@ -17,12 +17,14 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"testing"
)
func TestTask_Create(t *testing.T) {
user := &User{
usr := &user.User{
ID: 1,
Username: "user1",
Email: "user1@example.com",
@ -31,13 +33,13 @@ func TestTask_Create(t *testing.T) {
// We only test creating a task here, the rights are all well tested in the integration tests.
t.Run("normal", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{
Text: "Lorem",
Description: "Lorem Ipsum Dolor",
ListID: 1,
}
err := task.Create(user)
err := task.Create(usr)
assert.NoError(t, err)
// Assert getting a uid
assert.NotEmpty(t, task.UID)
@ -47,30 +49,30 @@ func TestTask_Create(t *testing.T) {
})
t.Run("empty text", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{
Text: "",
Description: "Lorem Ipsum Dolor",
ListID: 1,
}
err := task.Create(user)
err := task.Create(usr)
assert.Error(t, err)
assert.True(t, IsErrTaskCannotBeEmpty(err))
})
t.Run("nonexistant list", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{
Text: "Test",
Description: "Lorem Ipsum Dolor",
ListID: 9999999,
}
err := task.Create(user)
err := task.Create(usr)
assert.Error(t, err)
assert.True(t, IsErrListDoesNotExist(err))
})
t.Run("noneixtant user", func(t *testing.T) {
initFixtures(t)
nUser := &User{ID: 99999999}
db.LoadAndAssertFixtures(t)
nUser := &user.User{ID: 99999999}
task := &Task{
Text: "Test",
Description: "Lorem Ipsum Dolor",
@ -78,13 +80,13 @@ func TestTask_Create(t *testing.T) {
}
err := task.Create(nUser)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, user.IsErrUserDoesNotExist(err))
})
}
func TestTask_Update(t *testing.T) {
t.Run("normal", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{
ID: 1,
Text: "test10000",
@ -95,7 +97,7 @@ func TestTask_Update(t *testing.T) {
assert.NoError(t, err)
})
t.Run("nonexistant task", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{
ID: 9999999,
Text: "test10000",
@ -110,7 +112,7 @@ func TestTask_Update(t *testing.T) {
func TestTask_Delete(t *testing.T) {
t.Run("normal", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{
ID: 1,
}
@ -121,12 +123,14 @@ func TestTask_Delete(t *testing.T) {
func TestUpdateDone(t *testing.T) {
t.Run("marking a task as done", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
oldTask := &Task{Done: false}
newTask := &Task{Done: true}
updateDone(oldTask, newTask)
assert.NotEqual(t, int64(0), oldTask.DoneAtUnix)
})
t.Run("unmarking a task as done", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
oldTask := &Task{Done: true}
newTask := &Task{Done: false}
updateDone(oldTask, newTask)
@ -136,14 +140,14 @@ func TestUpdateDone(t *testing.T) {
func TestTask_ReadOne(t *testing.T) {
t.Run("default", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{ID: 1}
err := task.ReadOne()
assert.NoError(t, err)
assert.Equal(t, "task #1", task.Text)
})
t.Run("nonexisting", func(t *testing.T) {
initFixtures(t)
db.LoadAndAssertFixtures(t)
task := &Task{ID: 99999}
err := task.ReadOne()
assert.Error(t, err)

View File

@ -16,7 +16,10 @@
package models
import "code.vikunja.io/web"
import (
user2 "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
)
// Create implements the create method to assign a user to a team
// @Summary Add a user to a team
@ -41,7 +44,7 @@ func (tm *TeamMember) Create(a web.Auth) (err error) {
}
// Check if the user exists
user, err := GetUserByUsername(tm.Username)
user, err := user2.GetUserByUsername(tm.Username)
if err != nil {
return
}
@ -84,7 +87,7 @@ func (tm *TeamMember) Delete() (err error) {
}
// Find the numeric user id
user, err := GetUserByUsername(tm.Username)
user, err := user2.GetUserByUsername(tm.Username)
if err != nil {
return
}

View File

@ -17,11 +17,14 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"testing"
)
func TestTeamMember_Create(t *testing.T) {
db.LoadAndAssertFixtures(t)
// Dummy team member
dummyteammember := TeamMember{
@ -30,7 +33,7 @@ func TestTeamMember_Create(t *testing.T) {
}
// Doer
doer, err := GetUserByID(1)
doer, err := user.GetUserByID(1)
assert.NoError(t, err)
// Insert a new team member
@ -71,7 +74,7 @@ func TestTeamMember_Create(t *testing.T) {
dummyteammember.Username = "user9484"
err = dummyteammember.Create(doer)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, user.IsErrUserDoesNotExist(err))
// Try adding a user to a team which does not exist
tm = TeamMember{TeamID: 94824, Username: "user1"}

View File

@ -18,6 +18,7 @@ package models
import (
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
)
@ -32,7 +33,7 @@ type Team struct {
CreatedByID int64 `xorm:"int(11) not null INDEX" json:"-"`
// The user who created this team.
CreatedBy *User `xorm:"-" json:"createdBy"`
CreatedBy *user.User `xorm:"-" json:"createdBy"`
// An array of all members in this team.
Members []*TeamUser `xorm:"-" json:"members"`
@ -53,7 +54,7 @@ func (Team) TableName() string {
// AfterLoad gets the created by user object
func (t *Team) AfterLoad() {
// Get the owner
t.CreatedBy, _ = GetUserByID(t.CreatedByID)
t.CreatedBy, _ = user.GetUserByID(t.CreatedByID)
// Get all members
x.Select("*").
@ -90,7 +91,7 @@ func (TeamMember) TableName() string {
// TeamUser is the team member type
type TeamUser struct {
User `xorm:"extends"`
user.User `xorm:"extends"`
// Whether or not the member is an admin of the team. See the docs for more about what a team admin can do
Admin bool `json:"admin"`
}
@ -181,7 +182,7 @@ func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result
// @Failure 500 {object} models.Message "Internal error"
// @Router /teams [put]
func (t *Team) Create(a web.Auth) (err error) {
doer, err := getUserWithError(a)
doer, err := user.GetFromAuth(a)
if err != nil {
return err
}

View File

@ -17,6 +17,8 @@
package models
import (
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"testing"
"code.vikunja.io/web"
@ -28,7 +30,7 @@ func TestTeam_CanDoSomething(t *testing.T) {
Name string
Description string
CreatedByID int64
CreatedBy *User
CreatedBy *user.User
Members []*TeamUser
Created int64
Updated int64
@ -50,7 +52,7 @@ func TestTeam_CanDoSomething(t *testing.T) {
ID: 1,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
want: map[string]bool{"CanCreate": true, "IsAdmin": true, "CanRead": true, "CanDelete": true, "CanUpdate": true},
},
@ -60,7 +62,7 @@ func TestTeam_CanDoSomething(t *testing.T) {
ID: 300,
},
args: args{
a: &User{ID: 1},
a: &user.User{ID: 1},
},
want: map[string]bool{"CanCreate": true, "IsAdmin": false, "CanRead": false, "CanDelete": false, "CanUpdate": false},
},
@ -70,13 +72,15 @@ func TestTeam_CanDoSomething(t *testing.T) {
ID: 1,
},
args: args{
a: &User{ID: 4},
a: &user.User{ID: 4},
},
want: map[string]bool{"CanCreate": true, "IsAdmin": false, "CanRead": false, "CanDelete": false, "CanUpdate": false},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
tm := &Team{
ID: tt.fields.ID,
Name: tt.fields.Name,

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
@ -30,7 +31,7 @@ func TestTeam_Create(t *testing.T) {
}
// Doer
doer, err := GetUserByID(1)
doer, err := user.GetUserByID(1)
assert.NoError(t, err)
// Insert it

View File

@ -17,78 +17,49 @@
package models
import (
"code.vikunja.io/api/pkg/config"
_ "code.vikunja.io/api/pkg/config" // To trigger its init() which initializes the config
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail"
"fmt"
"github.com/go-xorm/xorm"
"github.com/stretchr/testify/assert"
"gopkg.in/testfixtures.v2"
"os"
"path/filepath"
"testing"
)
// SetupTests takes care of seting up the db, fixtures etc.
// This is an extra function to be able to call the fixtures setup from the integration tests.
func SetupTests(pathToRoot string) {
func SetupTests() {
var err error
fixturesDir := filepath.Join(pathToRoot, "pkg", "models", "fixtures")
if err = createTestEngine(fixturesDir); err != nil {
log.Fatalf("Error creating test engine: %v\n", err)
x, err = db.CreateTestEngine()
if err != nil {
log.Fatal(err)
}
err = x.Sync2(GetTables()...)
if err != nil {
log.Fatal(err)
}
err = db.InitTestFixtures(
"files",
"label_task",
"labels",
"link_sharing",
"list",
"namespaces",
"task_assignees",
"task_attachments",
"task_relations",
"task_reminders",
"tasks",
"team_list",
"team_members",
"team_namespaces",
"teams",
"users",
"users_list",
"users_namespace")
if err != nil {
log.Fatal(err)
}
// Start the pseudo mail queue
mail.StartMailDaemon()
// Create test database
if err = db.LoadFixtures(); err != nil {
log.Fatalf("Error preparing test database: %v", err.Error())
}
}
func createTestEngine(fixturesDir string) error {
var err error
var fixturesHelper testfixtures.Helper = &testfixtures.SQLite{}
// If set, use the config we provided instead of normal
if os.Getenv("VIKUNJA_TESTS_USE_CONFIG") == "1" {
x, err = db.CreateTestEngine()
if err != nil {
return fmt.Errorf("error getting test engine: %v", err)
}
err = initSchema(x)
if err != nil {
return err
}
if config.DatabaseType.GetString() == "mysql" {
fixturesHelper = &testfixtures.MySQL{}
}
} else {
x, err = db.CreateTestEngine()
if err != nil {
return fmt.Errorf("error getting test engine: %v", err)
}
// Sync dat shit
err = initSchema(x)
if err != nil {
return fmt.Errorf("sync database struct error: %v", err)
}
}
return db.InitFixtures(fixturesHelper, fixturesDir)
}
func initSchema(tx *xorm.Engine) error {
return tx.Sync2(GetTables()...)
}
func initFixtures(t *testing.T) {
// Init db fixtures
err := db.LoadFixtures()
assert.NoError(t, err)
}

View File

@ -1,383 +0,0 @@
// Vikunja is a todo-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 models
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/web"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/bcrypt"
"reflect"
)
// UserLogin Object to recive user credentials in JSON format
type UserLogin struct {
// The username used to log in.
Username string `json:"username"`
// The password for the user.
Password string `json:"password"`
}
// User holds information about an user
type User struct {
// The unique, numeric id of this user.
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
// The username of the user. Is always unique.
Username string `xorm:"varchar(250) not null unique" json:"username" valid:"length(3|250)" minLength:"3" maxLength:"250"`
Password string `xorm:"varchar(250) not null" json:"-"`
// The user's email address.
Email string `xorm:"varchar(250) null" json:"email,omitempty" valid:"email,length(0|250)" maxLength:"250"`
IsActive bool `xorm:"null" json:"-"`
// The users md5-hashed email address, used to get the avatar from gravatar and the likes.
AvatarURL string `xorm:"-" json:"avatarUrl"`
PasswordResetToken string `xorm:"varchar(450) null" json:"-"`
EmailConfirmToken string `xorm:"varchar(450) null" json:"-"`
// A unix timestamp when this task was created. You cannot change this value.
Created int64 `xorm:"created not null" json:"created"`
// A unix timestamp when this task was last updated. You cannot change this value.
Updated int64 `xorm:"updated not null" json:"updated"`
web.Auth `xorm:"-" json:"-"`
}
// AfterLoad is used to delete all emails to not have them leaked to the user
func (u *User) AfterLoad() {
u.AvatarURL = utils.Md5String(u.Email)
}
// GetID implements the Auth interface
func (u *User) GetID() int64 {
return u.ID
}
// TableName returns the table name for users
func (User) TableName() string {
return "users"
}
func getUserWithError(a web.Auth) (*User, error) {
u, is := a.(*User)
if !is {
return &User{}, fmt.Errorf("user is not user element, is %s", reflect.TypeOf(a))
}
return u, nil
}
// APIUserPassword represents a user object without timestamps and a json password field.
type APIUserPassword struct {
// The unique, numeric id of this user.
ID int64 `json:"id"`
// The username of the username. Is always unique.
Username string `json:"username" valid:"length(3|250)" minLength:"3" maxLength:"250"`
// The user's password in clear text. Only used when registering the user.
Password string `json:"password" valid:"length(8|250)" minLength:"8" maxLength:"250"`
// The user's email address
Email string `json:"email" valid:"email,length(0|250)" maxLength:"250"`
}
// APIFormat formats an API User into a normal user struct
func (apiUser *APIUserPassword) APIFormat() *User {
return &User{
ID: apiUser.ID,
Username: apiUser.Username,
Password: apiUser.Password,
Email: apiUser.Email,
}
}
// GetUserByID gets informations about a user by its ID
func GetUserByID(id int64) (user *User, err error) {
// Apparently xorm does otherwise look for all users but return only one, which leads to returing one even if the ID is 0
if id < 1 {
return &User{}, ErrUserDoesNotExist{}
}
return GetUser(&User{ID: id})
}
// GetUserByUsername gets a user from its user name. This is an extra function to be able to add an extra error check.
func GetUserByUsername(username string) (user *User, err error) {
if username == "" {
return &User{}, ErrUserDoesNotExist{}
}
return GetUser(&User{Username: username})
}
// GetUser gets a user object
func GetUser(user *User) (userOut *User, err error) {
return getUser(user, false)
}
// GetUserWithEmail returns a user object with email
func GetUserWithEmail(user *User) (userOut *User, err error) {
return getUser(user, true)
}
// getUser is a small helper function to avoid having duplicated code for almost the same use case
func getUser(user *User, withEmail bool) (userOut *User, err error) {
userOut = &User{} // To prevent a panic if user is nil
*userOut = *user
exists, err := x.Get(userOut)
if !exists {
return &User{}, ErrUserDoesNotExist{UserID: user.ID}
}
if !withEmail {
userOut.Email = ""
}
return userOut, err
}
// CheckUserCredentials checks user credentials
func CheckUserCredentials(u *UserLogin) (*User, error) {
// Check if we have any credentials
if u.Password == "" || u.Username == "" {
return &User{}, ErrNoUsernamePassword{}
}
// Check if the user exists
user, err := GetUserByUsername(u.Username)
if err != nil {
// 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
if !user.IsActive {
return &User{}, ErrEmailNotConfirmed{UserID: user.ID}
}
// Check the users password
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(u.Password))
if err != nil {
if err == bcrypt.ErrMismatchedHashAndPassword {
return &User{}, ErrWrongUsernameOrPassword{}
}
return &User{}, err
}
return user, nil
}
// GetCurrentUser returns the current user based on its jwt token
func GetCurrentUser(c echo.Context) (user *User, err error) {
jwtinf := c.Get("user").(*jwt.Token)
claims := jwtinf.Claims.(jwt.MapClaims)
return GetUserFromClaims(claims)
}
// GetUserFromClaims Returns a new user from jwt claims
func GetUserFromClaims(claims jwt.MapClaims) (user *User, err error) {
userID, ok := claims["id"].(float64)
if !ok {
return user, ErrCouldNotGetUserID{}
}
user = &User{
ID: int64(userID),
Email: claims["email"].(string),
Username: claims["username"].(string),
}
return
}
// CreateUser creates a new user and inserts it into the database
func CreateUser(user *User) (newUser *User, err error) {
newUser = user
// Check if we have all needed informations
if newUser.Password == "" || newUser.Username == "" || newUser.Email == "" {
return &User{}, ErrNoUsernamePassword{}
}
// Check if the user already existst with that username
exists := true
_, err = GetUserByUsername(newUser.Username)
if err != nil {
if IsErrUserDoesNotExist(err) {
exists = false
} else {
return &User{}, err
}
}
if exists {
return &User{}, ErrUsernameExists{newUser.ID, newUser.Username}
}
// Check if the user already existst with that email
exists = true
_, err = GetUser(&User{Email: newUser.Email})
if err != nil {
if IsErrUserDoesNotExist(err) {
exists = false
} else {
return &User{}, err
}
}
if exists {
return &User{}, ErrUserEmailExists{newUser.ID, newUser.Email}
}
// Hash the password
newUser.Password, err = hashPassword(user.Password)
if err != nil {
return &User{}, err
}
newUser.IsActive = true
if config.MailerEnabled.GetBool() {
// 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)
if err != nil {
return &User{}, err
}
// Update the metrics
metrics.UpdateCount(1, metrics.ActiveUsersKey)
// Get the full new User
newUserOut, err := GetUser(newUser)
if err != nil {
return &User{}, err
}
// Create the user's namespace
newN := &Namespace{Name: newUserOut.Username, Description: newUserOut.Username + "'s namespace.", Owner: newUserOut}
err = newN.Create(newUserOut)
if err != nil {
return &User{}, err
}
// Dont send a mail if we're testing
if !config.MailerEnabled.GetBool() {
return newUserOut, err
}
// Send the user a mail with a link to confirm the mail
data := map[string]interface{}{
"User": newUserOut,
}
mail.SendMailWithTemplate(user.Email, newUserOut.Username+" + Vikunja = <3", "confirm-email", data)
return newUserOut, err
}
// HashPassword hashes a password
func hashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 11)
return string(bytes), err
}
// UpdateUser updates a user
func UpdateUser(user *User) (updatedUser *User, err error) {
// Check if it exists
theUser, err := GetUserByID(user.ID)
if err != nil {
return &User{}, err
}
// Check if we have at least a username
if user.Username == "" {
//return User{}, ErrNoUsername{user.ID}
user.Username = theUser.Username // Dont change the username if we dont have one
}
user.Password = theUser.Password // set the password to the one in the database to not accedently resetting it
// Update it
_, err = x.Id(user.ID).Update(user)
if err != nil {
return &User{}, err
}
// Get the newly updated user
updatedUser, err = GetUserByID(user.ID)
if err != nil {
return &User{}, err
}
return updatedUser, err
}
// UpdateUserPassword updates the password of a user
func UpdateUserPassword(user *User, newPassword string) (err error) {
if newPassword == "" {
return ErrEmptyNewPassword{}
}
// Get all user details
theUser, err := GetUserByID(user.ID)
if err != nil {
return err
}
// Hash the new password and set it
hashed, err := hashPassword(newPassword)
if err != nil {
return err
}
theUser.Password = hashed
// Update it
_, err = x.Id(user.ID).Update(theUser)
if err != nil {
return err
}
return err
}
// DeleteUserByID deletes a user by its ID
func DeleteUserByID(id int64, doer *User) error {
// Check if the id is 0
if id == 0 {
return ErrIDCannotBeZero{}
}
// Delete the user
_, err := x.Id(id).Delete(&User{})
if err != nil {
return err
}
// Update the metrics
metrics.UpdateCount(-1, metrics.ActiveUsersKey)
return err
}

View File

@ -1,48 +0,0 @@
// Vikunja is a todo-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 models
// EmailConfirm holds the token to confirm a mail address
type EmailConfirm struct {
// The email confirm token sent via email.
Token string `json:"token"`
}
// UserEmailConfirm handles the confirmation of an email address
func UserEmailConfirm(c *EmailConfirm) (err error) {
// Check if we have an email confirm token
if c.Token == "" {
return ErrInvalidEmailConfirmToken{}
}
// Check if the token is valid
user := User{}
has, err := x.Where("email_confirm_token = ?", c.Token).Get(&user)
if err != nil {
return
}
if !has {
return ErrInvalidEmailConfirmToken{Token: c.Token}
}
user.IsActive = true
user.EmailConfirmToken = ""
_, err = x.Where("id = ?", user.ID).Cols("is_active", "email_confirm_token").Update(&user)
return
}

View File

@ -1,67 +0,0 @@
// Vikunja is a todo-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 models
import "testing"
func TestUserEmailConfirm(t *testing.T) {
type args struct {
c *EmailConfirm
}
tests := []struct {
name string
args args
wantErr bool
errType func(error) bool
}{
{
name: "Test Empty token",
args: args{
c: &EmailConfirm{
Token: "",
},
},
wantErr: true,
errType: IsErrInvalidEmailConfirmToken,
},
{
name: "Test invalid token",
args: args{
c: &EmailConfirm{
Token: "invalid",
},
},
wantErr: true,
errType: IsErrInvalidEmailConfirmToken,
},
{
name: "Test valid token",
args: args{
c: &EmailConfirm{
Token: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := UserEmailConfirm(tt.args.c); (err != nil) != tt.wantErr {
t.Errorf("UserEmailConfirm() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@ -1,40 +1,26 @@
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Copyright 2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// This file is part of Vikunja.
//
// Vikunja 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,
// Vikunja 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/>.
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package models
import "github.com/go-xorm/builder"
// ListUsers returns a list with all users, filtered by an optional searchstring
func ListUsers(searchterm string) (users []User, err error) {
if searchterm == "" {
err = x.Find(&users)
} else {
err = x.
Where("username LIKE ?", "%"+searchterm+"%").
Find(&users)
}
if err != nil {
return []User{}, err
}
return users, nil
}
import (
"code.vikunja.io/api/pkg/user"
"github.com/go-xorm/builder"
)
// ListUIDs hold all kinds of user IDs from accounts who have somehow access to a list
type ListUIDs struct {
@ -47,7 +33,7 @@ type ListUIDs struct {
}
// ListUsersFromList returns a list with all users who have access to a list, regardless of the method which gave them access
func ListUsersFromList(l *List, search string) (users []*User, err error) {
func ListUsersFromList(l *List, search string) (users []*user.User, err error) {
userids := []*ListUIDs{}

View File

@ -1,117 +0,0 @@
// Vikunja is a todo-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 models
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/utils"
)
// PasswordReset holds the data to reset a password
type PasswordReset struct {
// The previously issued reset token.
Token string `json:"token"`
// The new password for this user.
NewPassword string `json:"new_password"`
}
// UserPasswordReset resets a users password
func UserPasswordReset(reset *PasswordReset) (err error) {
// Check if the password is not empty
if reset.NewPassword == "" {
return ErrNoUsernamePassword{}
}
// Check if we have a token
var user User
exists, err := x.Where("password_reset_token = ?", reset.Token).Get(&user)
if err != nil {
return
}
if !exists {
return ErrInvalidPasswordResetToken{Token: reset.Token}
}
// Hash the password
user.Password, err = hashPassword(reset.NewPassword)
if err != nil {
return
}
// Save it
_, err = x.Where("id = ?", user.ID).Update(&user)
if err != nil {
return
}
// Dont send a mail if we're testing
if !config.MailerEnabled.GetBool() {
return
}
// Send a mail to the user to notify it his password was changed.
data := map[string]interface{}{
"User": user,
}
mail.SendMailWithTemplate(user.Email, "Your password on Vikunja was changed", "password-changed", data)
return
}
// PasswordTokenRequest defines the request format for password reset resqest
type PasswordTokenRequest struct {
Email string `json:"email" valid:"email,length(0|250)" maxLength:"250"`
}
// RequestUserPasswordResetToken inserts a random token to reset a users password into the databsse
func RequestUserPasswordResetToken(tr *PasswordTokenRequest) (err error) {
if tr.Email == "" {
return ErrNoUsernamePassword{}
}
// Check if the user exists
user, err := GetUserWithEmail(&User{Email: tr.Email})
if err != nil {
return
}
// Generate a token and save it
user.PasswordResetToken = utils.MakeRandomString(400)
// Save it
_, err = x.Where("id = ?", user.ID).Update(user)
if err != nil {
return
}
// Dont send a mail if we're testing
if !config.MailerEnabled.GetBool() {
return
}
data := map[string]interface{}{
"User": user,
}
// Send the user a mail with the reset token
mail.SendMailWithTemplate(user.Email, "Reset your password on Vikunja", "reset-password", data)
return
}

View File

@ -1,178 +0,0 @@
// Vikunja is a todo-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 models
import (
"code.vikunja.io/api/pkg/utils"
"github.com/stretchr/testify/assert"
"testing"
)
func TestCreateUser(t *testing.T) {
// Create test database
//assert.NoError(t, LoadFixtures())
// Get our doer
doer, err := GetUserByID(1)
assert.NoError(t, err)
// Our dummy user for testing
dummyuser := &User{
Username: "testuu",
Password: "1234",
Email: "noone@example.com",
}
// Create a new user
createdUser, err := CreateUser(dummyuser)
assert.NoError(t, err)
// Create a second new user
_, err = CreateUser(&User{Username: dummyuser.Username + "2", Email: dummyuser.Email + "m", Password: dummyuser.Password})
assert.NoError(t, err)
// Check if it fails to create the same user again
_, err = CreateUser(dummyuser)
assert.Error(t, err)
// Check if it fails to create a user with just the same username
_, err = CreateUser(&User{Username: dummyuser.Username, Password: "12345", Email: "email@example.com"})
assert.Error(t, err)
assert.True(t, IsErrUsernameExists(err))
// Check if it fails to create one with the same email
_, err = CreateUser(&User{Username: "noone", Password: "1234", Email: dummyuser.Email})
assert.Error(t, err)
assert.True(t, IsErrUserEmailExists(err))
// Check if it fails to create a user without password and username
_, err = CreateUser(&User{})
assert.Error(t, err)
assert.True(t, IsErrNoUsernamePassword(err))
// Check if he exists
theuser, err := GetUser(createdUser)
assert.NoError(t, err)
// Get by his ID
_, err = GetUserByID(theuser.ID)
assert.NoError(t, err)
// Passing 0 as ID should return an error
_, err = GetUserByID(0)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
// Check the user credentials with an unverified email
_, err = CheckUserCredentials(&UserLogin{"user5", "1234"})
assert.Error(t, err)
assert.True(t, IsErrEmailNotConfirmed(err))
// Update everything and check again
_, err = x.Cols("is_active").Where("true").Update(User{IsActive: true})
assert.NoError(t, err)
user, err := CheckUserCredentials(&UserLogin{"testuu", "1234"})
assert.NoError(t, err)
assert.Equal(t, "testuu", user.Username)
// Check wrong password (should also fail)
_, err = CheckUserCredentials(&UserLogin{"testuu", "12345"})
assert.Error(t, err)
assert.True(t, IsErrWrongUsernameOrPassword(err))
// Check usercredentials for a nonexistent user (should fail)
_, err = CheckUserCredentials(&UserLogin{"dfstestuu", "1234"})
assert.Error(t, err)
assert.True(t, IsErrWrongUsernameOrPassword(err))
// Update the user
uuser, err := UpdateUser(&User{ID: theuser.ID, Password: "444444"})
assert.NoError(t, err)
assert.Equal(t, theuser.Password, uuser.Password) // Password should not change
assert.Equal(t, theuser.Username, uuser.Username) // Username should not change either
// Try updating one which does not exist
_, err = UpdateUser(&User{ID: 99999, Username: "dg"})
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
// Update a users password
newpassword := "55555"
err = UpdateUserPassword(theuser, newpassword)
assert.NoError(t, err)
// Check if it was changed
_, err = CheckUserCredentials(&UserLogin{theuser.Username, newpassword})
assert.NoError(t, err)
// Check if the searchterm works
all, err := ListUsers("test")
assert.NoError(t, err)
assert.True(t, len(all) > 0)
all, err = ListUsers("")
assert.NoError(t, err)
assert.True(t, len(all) > 0)
// Try updating the password of a nonexistent user (should fail)
err = UpdateUserPassword(&User{ID: 9999}, newpassword)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
// Delete it
err = DeleteUserByID(theuser.ID, doer)
assert.NoError(t, err)
// Try deleting one with ID = 0
err = DeleteUserByID(0, doer)
assert.Error(t, err)
assert.True(t, IsErrIDCannotBeZero(err))
}
func TestUserPasswordReset(t *testing.T) {
// Request a new token
tr := &PasswordTokenRequest{
Email: "user1@example.com",
}
err := RequestUserPasswordResetToken(tr)
assert.NoError(t, err)
// Get the token / inside the user object
userWithToken, err := GetUserByID(1)
assert.NoError(t, err)
// Try resetting it
reset := &PasswordReset{
Token: userWithToken.PasswordResetToken,
}
// Try resetting it without a password
reset.NewPassword = ""
err = UserPasswordReset(reset)
assert.True(t, IsErrNoUsernamePassword(err))
// Reset it
reset.NewPassword = "1234"
err = UserPasswordReset(reset)
assert.NoError(t, err)
// Try resetting it with a wrong token
reset.Token = utils.MakeRandomString(400)
err = UserPasswordReset(reset)
assert.Error(t, err)
assert.True(t, IsErrInvalidPasswordResetToken(err))
}

View File

@ -1,38 +1,51 @@
// Copyright 2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja 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.
//
// Vikunja 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 Vikunja. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"code.vikunja.io/api/pkg/db"
"github.com/stretchr/testify/assert"
"code.vikunja.io/api/pkg/user"
"gopkg.in/d4l3k/messagediff.v1"
"testing"
)
func TestListUsersFromList(t *testing.T) {
err := db.LoadFixtures()
assert.NoError(t, err)
testuser1 := &User{
testuser1 := &user.User{
ID: 1,
Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "111d68d06e2d317b5a59c2c6c5bad808",
}
testuser2 := &User{
testuser2 := &user.User{
ID: 2,
Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
AvatarURL: "ab53a2911ddf9b4817ac01ddcd3d975f",
}
testuser3 := &User{
testuser3 := &user.User{
ID: 3,
Username: "user3",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
AvatarURL: "97d6d9441ff85fdc730e02a6068d267b",
PasswordResetToken: "passwordresettesttoken",
}
testuser4 := &User{
testuser4 := &user.User{
ID: 4,
Username: "user4",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -40,7 +53,7 @@ func TestListUsersFromList(t *testing.T) {
AvatarURL: "7e65550957227bd38fe2d7fbc6fd2f7b",
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
}
testuser5 := &User{
testuser5 := &user.User{
ID: 5,
Username: "user5",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -48,56 +61,56 @@ func TestListUsersFromList(t *testing.T) {
AvatarURL: "cfa35b8cd2ec278026357769582fa563",
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
}
testuser6 := &User{
testuser6 := &user.User{
ID: 6,
Username: "user6",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "3efbe51f864c6666bc27caf4c6ff90ed",
}
testuser7 := &User{
testuser7 := &user.User{
ID: 7,
Username: "user7",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "e80a711d4de44c30054806ebbd488464",
}
testuser8 := &User{
testuser8 := &user.User{
ID: 8,
Username: "user8",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "2b9b320416cd31020bb6844c3fadefd1",
}
testuser9 := &User{
testuser9 := &user.User{
ID: 9,
Username: "user9",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "f784fdb21d26dd2c64f5135f35ec401f",
}
testuser10 := &User{
testuser10 := &user.User{
ID: 10,
Username: "user10",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "fce8ff4ff56d75ad587d1bbaa5ef0563",
}
testuser11 := &User{
testuser11 := &user.User{
ID: 11,
Username: "user11",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "ad6d67d0c4495e186010732a7d360028",
}
testuser12 := &User{
testuser12 := &user.User{
ID: 12,
Username: "user12",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
IsActive: true,
AvatarURL: "ef1debc1364806281c42eeedfdeb943b",
}
testuser13 := &User{
testuser13 := &user.User{
ID: 13,
Username: "user13",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -112,19 +125,19 @@ func TestListUsersFromList(t *testing.T) {
tests := []struct {
name string
args args
wantUsers []*User
wantUsers []*user.User
wantErr bool
}{
{
name: "Check owner only",
args: args{l: &List{ID: 18, OwnerID: 7}},
wantUsers: []*User{testuser7},
wantUsers: []*user.User{testuser7},
},
{
// This list has another different user shared for each possible method
name: "Check with owner and other users",
args: args{l: &List{ID: 19, OwnerID: 7}},
wantUsers: []*User{
wantUsers: []*user.User{
testuser1, // Shared Via Team readonly
testuser2, // Shared Via Team write
testuser3, // Shared Via Team admin
@ -147,6 +160,8 @@ func TestListUsersFromList(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
gotUsers, err := ListUsersFromList(tt.args.l, tt.args.search)
if (err != nil) != tt.wantErr {
t.Errorf("ListUsersFromList() error = %v, wantErr %v", err, tt.wantErr)