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

@ -16,7 +16,12 @@ In general, this api repo has the following structure:
* `docs` * `docs`
* `pkg` * `pkg`
* `caldav` * `caldav`
* `cmd`
* `config` * `config`
* `db`
* `fixtures`
* `files`
* `integration`
* `log` * `log`
* `mail` * `mail`
* `metrics` * `metrics`
@ -29,8 +34,11 @@ In general, this api repo has the following structure:
* `red` * `red`
* `routes` * `routes`
* `api/v1` * `api/v1`
* `static`
* `swagger` * `swagger`
* `user`
* `utils` * `utils`
* `version`
* `REST-Tests` * `REST-Tests`
* `templates` * `templates`
* `vendor` * `vendor`
@ -70,6 +78,21 @@ how to interpret which env variables for config etc.
If you want to add a new config parameter, you should add default value in this package. If you want to add a new config parameter, you should add default value in this package.
### db
This package contains the db connection handling and db fixtures for testing.
Each other package gets its db connection object from this package.
### files
This package is responsible for all file-related things.
This means it handles saving and retrieving files from the db and the underlying file system.
### integration
All integration tests live here.
See [integration tests]({{< ref "test.md" >}}#integration-tests) for more details.
### log ### log
Similar to `config`, this will set up the logging, based on differen logging backends. Similar to `config`, this will set up the logging, based on differen logging backends.
@ -127,11 +150,19 @@ To add a new route, see [adding a new route]({{< ref "../practical-instructions/
This is where all http-handler functions for the api are stored. This is where all http-handler functions for the api are stored.
Every handler function which does not use the standard web handler should live here. Every handler function which does not use the standard web handler should live here.
### static
All static files generated by `make generate` live here.
### swagger ### swagger
This is where the [generated]({{< ref "make.md#generate-swagger-definitions-from-code-comments">}} [api docs]({{< ref "../usage/api.md">}}) live. This is where the [generated]({{< ref "make.md#generate-swagger-definitions-from-code-comments">}} [api docs]({{< ref "../usage/api.md">}}) live.
You usually don't need to touch this package. You usually don't need to touch this package.
### user
All user-related things like registration etc. live in this package.
### utils ### utils
A small package, containing some helper functions: A small package, containing some helper functions:
@ -141,6 +172,12 @@ A small package, containing some helper functions:
See their function definitions for instructions on how to use them. See their function definitions for instructions on how to use them.
### version
The single purpouse of this package is to hold the current vikunja version which gets overridden through build flags
each time `make release` or `make build` is run.
It is a seperate package to avoid import cycles with other packages.
## REST-Tests ## REST-Tests
Holds all kinds of test files to directly test the api from inside of [jetbrains ide's](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html). Holds all kinds of test files to directly test the api from inside of [jetbrains ide's](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html).

View file

@ -42,3 +42,29 @@ The integration tests use the same config and fixtures as the unit tests and the
see at the beginning of this document. see at the beginning of this document.
To run integration tests, use `make integration-test`. To run integration tests, use `make integration-test`.
# Initializing db fixtures when writing tests
All db fixtures for all tests live in the `pkg/db/fixtures/` folder as yaml files.
Each file has the same name as the table the fixtures are for.
You should put new fixtures in this folder.
When initializing db fixtures, you are responsible for defining which tables your package needs in your test init function.
Usually, this is done as follows (this code snippet is taken from the `user` package):
```go
err = db.InitTestFixtures("users")
if err != nil {
log.Fatal(err)
}
```
In your actual tests, you then load the fixtures into the in-memory db like so:
```go
db.LoadAndAssertFixtures(t)
```
This will load all fixtures you defined in your test init method.
You should always use this method to load fixtures, the only exception is when your package tests require extra test
fixtures other than db fixtures (like files).

1
go.mod
View file

@ -59,7 +59,6 @@ require (
github.com/onsi/gomega v1.4.3 // indirect github.com/onsi/gomega v1.4.3 // indirect
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml v1.4.0 // indirect github.com/pelletier/go-toml v1.4.0 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v0.9.2 github.com/prometheus/client_golang v0.9.2
github.com/samedi/caldav-go v3.0.0+incompatible github.com/samedi/caldav-go v3.0.0+incompatible
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b

View file

@ -17,7 +17,7 @@
package caldav package caldav
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils" "code.vikunja.io/api/pkg/utils"
"fmt" "fmt"
"strconv" "strconv"
@ -49,7 +49,7 @@ type Todo struct {
Summary string Summary string
Description string Description string
CompletedUnix int64 CompletedUnix int64
Organizer *models.User Organizer *user.User
Priority int64 // 0-9, 1 is highest Priority int64 // 0-9, 1 is highest
RelatedToUID string RelatedToUID string

View file

@ -25,6 +25,7 @@ import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
migrator "code.vikunja.io/api/pkg/modules/migration" migrator "code.vikunja.io/api/pkg/modules/migration"
"code.vikunja.io/api/pkg/red" "code.vikunja.io/api/pkg/red"
"code.vikunja.io/api/pkg/user"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"os" "os"
@ -76,6 +77,10 @@ func initialize() {
if err != nil { if err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())
} }
err = user.InitDB()
if err != nil {
log.Fatal(err.Error())
}
err = files.SetEngine() err = files.SetEngine()
if err != nil { if err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())

View file

@ -23,7 +23,6 @@ import (
"fmt" "fmt"
"github.com/go-xorm/core" "github.com/go-xorm/core"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"os"
"strconv" "strconv"
"time" "time"
@ -85,33 +84,6 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
return return
} }
// CreateTestEngine creates an instance of the db engine which lives in memory
func CreateTestEngine() (engine *xorm.Engine, err error) {
if x != nil {
return x, nil
}
if os.Getenv("VIKUNJA_TESTS_USE_CONFIG") == "1" {
config.InitConfig()
engine, err = CreateDBEngine()
if err != nil {
return nil, err
}
} else {
engine, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
if err != nil {
return nil, err
}
}
engine.SetMapper(core.GonicMapper{})
engine.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
engine.SetLogger(xorm.NewSimpleLogger(log.GetLogWriter("database")))
x = engine
return
}
// RegisterTableStructsForCache registers tables in gob encoding for redis cache // RegisterTableStructsForCache registers tables in gob encoding for redis cache
func RegisterTableStructsForCache(val interface{}) { func RegisterTableStructsForCache(val interface{}) {
gob.Register(val) gob.Register(val)

69
pkg/db/test.go Normal file
View file

@ -0,0 +1,69 @@
// Copyright2018-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 db
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
"os"
)
// CreateTestEngine creates an instance of the db engine which lives in memory
func CreateTestEngine() (engine *xorm.Engine, err error) {
if x != nil {
return x, nil
}
if os.Getenv("VIKUNJA_TESTS_USE_CONFIG") == "1" {
config.InitConfig()
engine, err = CreateDBEngine()
if err != nil {
return nil, err
}
} else {
engine, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
if err != nil {
return nil, err
}
}
engine.SetMapper(core.GonicMapper{})
engine.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
engine.SetLogger(xorm.NewSimpleLogger(log.GetLogWriter("database")))
x = engine
return
}
// InitTestFixtures populates the db with all fixtures from the fixtures folder
func InitTestFixtures(tablenames ...string) (err error) {
// Create all fixtures
config.InitDefaultConfig()
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Sync fixtures
err = InitFixtures(tablenames...)
if err != nil {
log.Fatal(err)
}
return nil
}

View file

@ -18,15 +18,36 @@
package db package db
import ( import (
"code.vikunja.io/api/pkg/config"
"github.com/stretchr/testify/assert"
"gopkg.in/testfixtures.v2" "gopkg.in/testfixtures.v2"
"path/filepath"
"testing"
) )
var fixtures *testfixtures.Context var fixtures *testfixtures.Context
// InitFixtures initialize test fixtures for a test database // InitFixtures initialize test fixtures for a test database
func InitFixtures(helper testfixtures.Helper, dir string) (err error) { func InitFixtures(tablenames ...string) (err error) {
var helper testfixtures.Helper = &testfixtures.SQLite{}
if config.DatabaseType.GetString() == "mysql" {
helper = &testfixtures.MySQL{}
}
dir := filepath.Join(config.ServiceRootpath.GetString(), "pkg", "db", "fixtures")
testfixtures.SkipDatabaseNameCheck(true) testfixtures.SkipDatabaseNameCheck(true)
fixtures, err = testfixtures.NewFolder(x.DB().DB, helper, dir)
// If fixture table names are specified, load them
// Otherwise, load all fixtures
if len(tablenames) > 0 {
for i, name := range tablenames {
tablenames[i] = filepath.Join(dir, name+".yml")
}
fixtures, err = testfixtures.NewFiles(x.DB().DB, helper, tablenames...)
} else {
fixtures, err = testfixtures.NewFolder(x.DB().DB, helper, dir)
}
return err return err
} }
@ -34,3 +55,9 @@ func InitFixtures(helper testfixtures.Helper, dir string) (err error) {
func LoadFixtures() error { func LoadFixtures() error {
return fixtures.Load() return fixtures.Load()
} }
// LoadAndAssertFixtures loads all fixtures defined before and asserts they are correctly loaded
func LoadAndAssertFixtures(t *testing.T) {
err := LoadFixtures()
assert.NoError(t, err)
}

View file

@ -22,9 +22,7 @@ import (
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/testfixtures.v2"
"os" "os"
"path/filepath"
"testing" "testing"
) )
@ -45,10 +43,9 @@ func InitTestFileHandler() {
} }
func initFixtures(t *testing.T) { func initFixtures(t *testing.T) {
// Init db fixtures // DB fixtures
err := db.LoadFixtures() db.LoadAndAssertFixtures(t)
assert.NoError(t, err) // File fixtures
InitTestFileFixtures(t) InitTestFileFixtures(t)
} }
@ -73,17 +70,7 @@ func InitTests() {
log.Fatal(err) log.Fatal(err)
} }
config.InitDefaultConfig() err = db.InitTestFixtures("files")
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Sync fixtures
var fixturesHelper testfixtures.Helper = &testfixtures.SQLite{}
if config.DatabaseType.GetString() == "mysql" {
fixturesHelper = &testfixtures.MySQL{}
}
fixturesDir := filepath.Join(config.ServiceRootpath.GetString(), "pkg", "files", "fixtures")
err = db.InitFixtures(fixturesHelper, fixturesDir)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -23,6 +23,7 @@ import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes" "code.vikunja.io/api/pkg/routes"
v1 "code.vikunja.io/api/pkg/routes/api/v1" v1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
@ -38,34 +39,34 @@ import (
// These are the test users, the same way they are in the test database // These are the test users, the same way they are in the test database
var ( var (
testuser1 = models.User{ testuser1 = user.User{
ID: 1, ID: 1,
Username: "user1", Username: "user1",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user1@example.com", Email: "user1@example.com",
IsActive: true, IsActive: true,
} }
testuser2 = models.User{ testuser2 = user.User{
ID: 2, ID: 2,
Username: "user2", Username: "user2",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user2@example.com", Email: "user2@example.com",
} }
testuser3 = models.User{ testuser3 = user.User{
ID: 3, ID: 3,
Username: "user3", Username: "user3",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user3@example.com", Email: "user3@example.com",
PasswordResetToken: "passwordresettesttoken", PasswordResetToken: "passwordresettesttoken",
} }
testuser4 = models.User{ testuser4 = user.User{
ID: 4, ID: 4,
Username: "user4", Username: "user4",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
Email: "user4@example.com", Email: "user4@example.com",
EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael", EmailConfirmToken: "tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael",
} }
testuser5 = models.User{ testuser5 = user.User{
ID: 4, ID: 4,
Username: "user5", Username: "user5",
Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", Password: "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.",
@ -81,7 +82,8 @@ func setupTestEnv() (e *echo.Echo, err error) {
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH")) config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Some tests use the file engine, so we'll need to initialize that // Some tests use the file engine, so we'll need to initialize that
files.InitTests() files.InitTests()
models.SetupTests(config.ServiceRootpath.GetString()) user.InitTests()
models.SetupTests()
err = db.LoadFixtures() err = db.LoadFixtures()
if err != nil { if err != nil {
@ -114,7 +116,7 @@ func newTestRequest(t *testing.T, method string, handler func(ctx echo.Context)
return return
} }
func addUserTokenToContext(t *testing.T, user *models.User, c echo.Context) { func addUserTokenToContext(t *testing.T, user *user.User, c echo.Context) {
// Get the token as a string // Get the token as a string
token, err := v1.NewUserJWTAuthtoken(user) token, err := v1.NewUserJWTAuthtoken(user)
assert.NoError(t, err) assert.NoError(t, err)
@ -152,7 +154,7 @@ func testRequestSetup(t *testing.T, method string, payload string, queryParams u
return return
} }
func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *models.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) { func newTestRequestWithUser(t *testing.T, method string, handler echo.HandlerFunc, user *user.User, payload string, queryParams url.Values, urlParams map[string]string) (rec *httptest.ResponseRecorder, err error) {
rec, c := testRequestSetup(t, method, payload, queryParams, urlParams) rec, c := testRequestSetup(t, method, payload, queryParams, urlParams)
addUserTokenToContext(t, user, c) addUserTokenToContext(t, user, c)
err = handler(c) err = handler(c)
@ -185,7 +187,7 @@ func assertHandlerErrorCode(t *testing.T, err error, expectedErrorCode int) {
} }
type webHandlerTest struct { type webHandlerTest struct {
user *models.User user *user.User
linkShare *models.LinkSharing linkShare *models.LinkSharing
strFunc func() handler.CObject strFunc func() handler.CObject
t *testing.T t *testing.T

View file

@ -17,8 +17,8 @@
package integrations package integrations
import ( import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1" apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"net/http" "net/http"
"testing" "testing"
@ -36,7 +36,7 @@ func TestLogin(t *testing.T) {
t.Run("Empty payload", func(t *testing.T) { t.Run("Empty payload", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{}`) _, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{}`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword) assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
}) })
t.Run("Not existing user", func(t *testing.T) { t.Run("Not existing user", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{
@ -44,7 +44,7 @@ func TestLogin(t *testing.T) {
"password": "1234" "password": "1234"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeWrongUsernameOrPassword) assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
}) })
t.Run("Wrong password", func(t *testing.T) { t.Run("Wrong password", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{
@ -52,7 +52,7 @@ func TestLogin(t *testing.T) {
"password": "wrong" "password": "wrong"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeWrongUsernameOrPassword) assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
}) })
t.Run("user with unconfirmed email", func(t *testing.T) { t.Run("user with unconfirmed email", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.Login, `{
@ -60,6 +60,6 @@ func TestLogin(t *testing.T) {
"password": "1234" "password": "1234"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeEmailNotConfirmed) assertHandlerErrorCode(t, err, user.ErrCodeEmailNotConfirmed)
}) })
} }

View file

@ -17,8 +17,8 @@
package integrations package integrations
import ( import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1" apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"net/http" "net/http"
"testing" "testing"
@ -37,7 +37,7 @@ func TestRegister(t *testing.T) {
t.Run("Empty payload", func(t *testing.T) { t.Run("Empty payload", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{}`) _, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{}`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword) assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
}) })
t.Run("Empty username", func(t *testing.T) { t.Run("Empty username", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -46,7 +46,7 @@ func TestRegister(t *testing.T) {
"email": "email@example.com" "email": "email@example.com"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword) assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
}) })
t.Run("Empty password", func(t *testing.T) { t.Run("Empty password", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -55,7 +55,7 @@ func TestRegister(t *testing.T) {
"email": "email@example.com" "email": "email@example.com"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword) assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
}) })
t.Run("Empty email", func(t *testing.T) { t.Run("Empty email", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -64,7 +64,7 @@ func TestRegister(t *testing.T) {
"email": "" "email": ""
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword) assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
}) })
t.Run("Already existing username", func(t *testing.T) { t.Run("Already existing username", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -73,7 +73,7 @@ func TestRegister(t *testing.T) {
"email": "email@example.com" "email": "email@example.com"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrorCodeUsernameExists) assertHandlerErrorCode(t, err, user.ErrorCodeUsernameExists)
}) })
t.Run("Already existing email", func(t *testing.T) { t.Run("Already existing email", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.RegisterUser, `{
@ -82,6 +82,6 @@ func TestRegister(t *testing.T) {
"email": "user1@example.com" "email": "user1@example.com"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrorCodeUserEmailExists) assertHandlerErrorCode(t, err, user.ErrorCodeUserEmailExists)
}) })
} }

View file

@ -17,8 +17,8 @@
package integrations package integrations
import ( import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1" apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"net/http" "net/http"
"testing" "testing"
@ -39,7 +39,7 @@ func TestUserChangePassword(t *testing.T) {
"old_password": "invalid" "old_password": "invalid"
}`, nil, nil) }`, nil, nil)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeWrongUsernameOrPassword) assertHandlerErrorCode(t, err, user.ErrCodeWrongUsernameOrPassword)
}) })
t.Run("Empty old password", func(t *testing.T) { t.Run("Empty old password", func(t *testing.T) {
_, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserChangePassword, &testuser1, `{ _, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserChangePassword, &testuser1, `{
@ -47,7 +47,7 @@ func TestUserChangePassword(t *testing.T) {
"old_password": "" "old_password": ""
}`, nil, nil) }`, nil, nil)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeEmptyOldPassword) assertHandlerErrorCode(t, err, user.ErrCodeEmptyOldPassword)
}) })
t.Run("Empty new password", func(t *testing.T) { t.Run("Empty new password", func(t *testing.T) {
_, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserChangePassword, &testuser1, `{ _, err := newTestRequestWithUser(t, http.MethodPost, apiv1.UserChangePassword, &testuser1, `{
@ -55,6 +55,6 @@ func TestUserChangePassword(t *testing.T) {
"old_password": "1234" "old_password": "1234"
}`, nil, nil) }`, nil, nil)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeEmptyNewPassword) assertHandlerErrorCode(t, err, user.ErrCodeEmptyNewPassword)
}) })
} }

View file

@ -17,8 +17,8 @@
package integrations package integrations
import ( import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1" apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"net/http" "net/http"
@ -35,16 +35,16 @@ func TestUserConfirmEmail(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{}`) _, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{}`)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, http.StatusPreconditionFailed, err.(*echo.HTTPError).Code) assert.Equal(t, http.StatusPreconditionFailed, err.(*echo.HTTPError).Code)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidEmailConfirmToken) assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
}) })
t.Run("Empty token", func(t *testing.T) { t.Run("Empty token", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": ""}`) _, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": ""}`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidEmailConfirmToken) assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
}) })
t.Run("Invalid token", func(t *testing.T) { t.Run("Invalid token", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": "invalidToken"}`) _, err := newTestRequest(t, http.MethodPost, apiv1.UserConfirmEmail, `{"token": "invalidToken"}`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidEmailConfirmToken) assertHandlerErrorCode(t, err, user.ErrCodeInvalidEmailConfirmToken)
}) })
} }

View file

@ -17,8 +17,8 @@
package integrations package integrations
import ( import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1" apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"net/http" "net/http"
@ -34,7 +34,7 @@ func TestUserRequestResetPasswordToken(t *testing.T) {
t.Run("Empty payload", func(t *testing.T) { t.Run("Empty payload", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{}`) _, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{}`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword) assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
}) })
t.Run("Invalid email address", func(t *testing.T) { t.Run("Invalid email address", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1example.com"}`) _, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1example.com"}`)
@ -44,6 +44,6 @@ func TestUserRequestResetPasswordToken(t *testing.T) {
t.Run("No user with that email address", func(t *testing.T) { t.Run("No user with that email address", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1000@example.com"}`) _, err := newTestRequest(t, http.MethodPost, apiv1.UserRequestResetPasswordToken, `{"email": "user1000@example.com"}`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeUserDoesNotExist) assertHandlerErrorCode(t, err, user.ErrCodeUserDoesNotExist)
}) })
} }

View file

@ -17,8 +17,8 @@
package integrations package integrations
import ( import (
"code.vikunja.io/api/pkg/models"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1" apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"net/http" "net/http"
@ -45,7 +45,7 @@ func TestUserPasswordReset(t *testing.T) {
"token": "passwordresettesttoken" "token": "passwordresettesttoken"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeNoUsernamePassword) assertHandlerErrorCode(t, err, user.ErrCodeNoUsernamePassword)
}) })
t.Run("Invalid password reset token", func(t *testing.T) { t.Run("Invalid password reset token", func(t *testing.T) {
_, err := newTestRequest(t, http.MethodPost, apiv1.UserResetPassword, `{ _, err := newTestRequest(t, http.MethodPost, apiv1.UserResetPassword, `{
@ -53,6 +53,6 @@ func TestUserPasswordReset(t *testing.T) {
"token": "invalidtoken" "token": "invalidtoken"
}`) }`)
assert.Error(t, err) assert.Error(t, err)
assertHandlerErrorCode(t, err, models.ErrCodeInvalidPasswordResetToken) assertHandlerErrorCode(t, err, user.ErrCodeInvalidPasswordResetToken)
}) })
} }

View file

@ -22,6 +22,8 @@ import (
"code.vikunja.io/api/pkg/files" "code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/migration"
"code.vikunja.io/api/pkg/user"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"os" "os"
@ -138,5 +140,7 @@ func initSchema(tx *xorm.Engine) error {
schemeBeans := []interface{}{} schemeBeans := []interface{}{}
schemeBeans = append(schemeBeans, models.GetTables()...) schemeBeans = append(schemeBeans, models.GetTables()...)
schemeBeans = append(schemeBeans, files.GetTables()...) schemeBeans = append(schemeBeans, files.GetTables()...)
schemeBeans = append(schemeBeans, migration.GetTables()...)
schemeBeans = append(schemeBeans, user.GetTables()...)
return tx.Sync2(schemeBeans...) return tx.Sync2(schemeBeans...)
} }

View file

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

View file

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

View file

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

View file

@ -17,6 +17,7 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/go-xorm/builder" "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 // TODO: add an extra check for link share handling
// Get all tasks // Get all tasks
taskIDs, err := getUserTaskIDs(&User{ID: a.GetID()}) taskIDs, err := getUserTaskIDs(&user.User{ID: a.GetID()})
if err != nil { if err != nil {
return false, err return false, err
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -17,6 +17,7 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/go-xorm/builder" "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 // 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 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 (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 true, nil
} }
return l.checkRight(a, RightRead, RightWrite, RightAdmin) 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 all the things
// Check if the user is either owner or can write to the list // Check if the user is either owner or can write to the list
// Owners are always admins // Owners are always admins
if originalList.isOwner(&User{ID: a.GetID()}) { if originalList.isOwner(&user.User{ID: a.GetID()}) {
return true, nil return true, nil
} }
return originalList.checkRight(a, RightAdmin) return originalList.checkRight(a, RightAdmin)
} }
// Little helper function to check if a user is list owner // 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 return l.OwnerID == u.ID
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -18,6 +18,7 @@ package models
import ( import (
"code.vikunja.io/api/pkg/files" "code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"io" "io"
"time" "time"
@ -29,8 +30,8 @@ type TaskAttachment struct {
TaskID int64 `xorm:"int(11) not null" json:"task_id" param:"task"` TaskID int64 `xorm:"int(11) not null" json:"task_id" param:"task"`
FileID int64 `xorm:"int(11) not null" json:"-"` FileID int64 `xorm:"int(11) not null" json:"-"`
CreatedByID int64 `xorm:"int(11) not null" json:"-"` CreatedByID int64 `xorm:"int(11) not null" json:"-"`
CreatedBy *User `xorm:"-" json:"created_by"` CreatedBy *user.User `xorm:"-" json:"created_by"`
File *files.File `xorm:"-" json:"file"` 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 return nil, 0, 0, err
} }
us := make(map[int64]*User) us := make(map[int64]*user.User)
err = x.In("id", userIDs).Find(&us) err = x.In("id", userIDs).Find(&us)
if err != nil { if err != nil {
return nil, 0, 0, err return nil, 0, 0, err

View file

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

View file

@ -18,6 +18,7 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"time" "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. // 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. // This allows to use this function in Task.ReadAll with a possibility to deprecate the latter at some point.
if tf.ListID == 0 { 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 { if err != nil {
return nil, 0, 0, err return nil, 0, 0, err
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -16,7 +16,10 @@
package models 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 // Create implements the create method to assign a user to a team
// @Summary Add 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 // Check if the user exists
user, err := GetUserByUsername(tm.Username) user, err := user2.GetUserByUsername(tm.Username)
if err != nil { if err != nil {
return return
} }
@ -84,7 +87,7 @@ func (tm *TeamMember) Delete() (err error) {
} }
// Find the numeric user id // Find the numeric user id
user, err := GetUserByUsername(tm.Username) user, err := user2.GetUserByUsername(tm.Username)
if err != nil { if err != nil {
return return
} }

View file

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

View file

@ -18,6 +18,7 @@ package models
import ( import (
"code.vikunja.io/api/pkg/metrics" "code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
) )
@ -32,7 +33,7 @@ type Team struct {
CreatedByID int64 `xorm:"int(11) not null INDEX" json:"-"` CreatedByID int64 `xorm:"int(11) not null INDEX" json:"-"`
// The user who created this team. // 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. // An array of all members in this team.
Members []*TeamUser `xorm:"-" json:"members"` Members []*TeamUser `xorm:"-" json:"members"`
@ -53,7 +54,7 @@ func (Team) TableName() string {
// AfterLoad gets the created by user object // AfterLoad gets the created by user object
func (t *Team) AfterLoad() { func (t *Team) AfterLoad() {
// Get the owner // Get the owner
t.CreatedBy, _ = GetUserByID(t.CreatedByID) t.CreatedBy, _ = user.GetUserByID(t.CreatedByID)
// Get all members // Get all members
x.Select("*"). x.Select("*").
@ -90,7 +91,7 @@ func (TeamMember) TableName() string {
// TeamUser is the team member type // TeamUser is the team member type
type TeamUser struct { 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 // 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"` 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" // @Failure 500 {object} models.Message "Internal error"
// @Router /teams [put] // @Router /teams [put]
func (t *Team) Create(a web.Auth) (err error) { func (t *Team) Create(a web.Auth) (err error) {
doer, err := getUserWithError(a) doer, err := user.GetFromAuth(a)
if err != nil { if err != nil {
return err return err
} }

View file

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

View file

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

View file

@ -17,78 +17,49 @@
package models package models
import ( 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/config" // To trigger its init() which initializes the config
"code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail" "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. // 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. // 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 var err error
fixturesDir := filepath.Join(pathToRoot, "pkg", "models", "fixtures") x, err = db.CreateTestEngine()
if err = createTestEngine(fixturesDir); err != nil { if err != nil {
log.Fatalf("Error creating test engine: %v\n", err) 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 // Start the pseudo mail queue
mail.StartMailDaemon() 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,40 +1,26 @@
// Vikunja is a todo-list application to facilitate your life. // Copyright 2018-2020 Vikunja and contriubtors. All rights reserved.
// Copyright 2018-2020 Vikunja and contributors. 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (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 // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // 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 package models
import "github.com/go-xorm/builder" import (
"code.vikunja.io/api/pkg/user"
// ListUsers returns a list with all users, filtered by an optional searchstring "github.com/go-xorm/builder"
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
}
// ListUIDs hold all kinds of user IDs from accounts who have somehow access to a list // ListUIDs hold all kinds of user IDs from accounts who have somehow access to a list
type ListUIDs struct { 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 // 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{} userids := []*ListUIDs{}

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

View file

@ -19,12 +19,13 @@ package migration
import ( import (
"bytes" "bytes"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"io/ioutil" "io/ioutil"
) )
// InsertFromStructure takes a fully nested Vikunja data structure and a user and then creates everything for this user // InsertFromStructure takes a fully nested Vikunja data structure and a user and then creates everything for this user
// (Namespaces, tasks, etc. Even attachments and relations.) // (Namespaces, tasks, etc. Even attachments and relations.)
func InsertFromStructure(str []*models.NamespaceWithLists, user *models.User) (err error) { func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err error) {
// Create all namespaces // Create all namespaces
for _, n := range str { for _, n := range str {

View file

@ -19,6 +19,7 @@ package handler
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/migration" "code.vikunja.io/api/pkg/modules/migration"
user2 "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -53,7 +54,7 @@ func (mw *MigrationWeb) Migrate(c echo.Context) error {
ms := mw.MigrationStruct() ms := mw.MigrationStruct()
// Get the user from context // Get the user from context
user, err := models.GetCurrentUser(c) user, err := user2.GetCurrentUser(c)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }
@ -82,7 +83,7 @@ func (mw *MigrationWeb) Migrate(c echo.Context) error {
func (mw *MigrationWeb) Status(c echo.Context) error { func (mw *MigrationWeb) Status(c echo.Context) error {
ms := mw.MigrationStruct() ms := mw.MigrationStruct()
user, err := models.GetCurrentUser(c) user, err := user2.GetCurrentUser(c)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -16,7 +16,9 @@
package migration package migration
import "code.vikunja.io/api/pkg/models" import (
"code.vikunja.io/api/pkg/user"
)
// Status represents this migration status // Status represents this migration status
type Status struct { type Status struct {
@ -32,7 +34,7 @@ func (s *Status) TableName() string {
} }
// SetMigrationStatus sets the migration status for a user // SetMigrationStatus sets the migration status for a user
func SetMigrationStatus(m Migrator, u *models.User) (err error) { func SetMigrationStatus(m Migrator, u *user.User) (err error) {
status := &Status{ status := &Status{
UserID: u.ID, UserID: u.ID,
MigratorName: m.Name(), MigratorName: m.Name(),
@ -42,7 +44,7 @@ func SetMigrationStatus(m Migrator, u *models.User) (err error) {
} }
// GetMigrationStatus returns the migration status for a migration and a user // GetMigrationStatus returns the migration status for a migration and a user
func GetMigrationStatus(m Migrator, u *models.User) (status *Status, err error) { func GetMigrationStatus(m Migrator, u *user.User) (status *Status, err error) {
status = &Status{} status = &Status{}
_, err = x.Where("user_id = ? and migrator_name = ?", u.ID, m.Name()).Desc("id").Get(status) _, err = x.Where("user_id = ? and migrator_name = ?", u.ID, m.Name()).Desc("id").Get(status)
return return

View file

@ -17,13 +17,15 @@
package migration package migration
import "code.vikunja.io/api/pkg/models" import (
"code.vikunja.io/api/pkg/user"
)
// Migrator is the basic migrator interface which is shared among all migrators // Migrator is the basic migrator interface which is shared among all migrators
type Migrator interface { type Migrator interface {
// Migrate is the interface used to migrate a user's tasks from another platform to vikunja. // Migrate is the interface used to migrate a user's tasks from another platform to vikunja.
// The user object is the user who's tasks will be migrated. // The user object is the user who's tasks will be migrated.
Migrate(user *models.User) error Migrate(user *user.User) error
// AuthURL returns a url for clients to authenticate against. // AuthURL returns a url for clients to authenticate against.
// The use case for this are Oauth flows, where the server token should remain hidden and not // The use case for this are Oauth flows, where the server token should remain hidden and not
// known to the frontend. // known to the frontend.

View file

@ -23,6 +23,7 @@ import (
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/migration" "code.vikunja.io/api/pkg/modules/migration"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils" "code.vikunja.io/api/pkg/utils"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -341,7 +342,7 @@ func makeAuthGetRequest(token *wunderlistAuthToken, urlPart string, v interface{
// @Success 200 {object} models.Message "A message telling you everything was migrated successfully." // @Success 200 {object} models.Message "A message telling you everything was migrated successfully."
// @Failure 500 {object} models.Message "Internal server error" // @Failure 500 {object} models.Message "Internal server error"
// @Router /migration/wunderlist/migrate [post] // @Router /migration/wunderlist/migrate [post]
func (w *Migration) Migrate(user *models.User) (err error) { func (w *Migration) Migrate(user *user.User) (err error) {
log.Debugf("[Wunderlist migration] Starting wunderlist migration for user %d", user.ID) log.Debugf("[Wunderlist migration] Starting wunderlist migration for user %d", user.ID)

View file

@ -19,6 +19,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@ -34,7 +35,7 @@ const (
) )
// NewUserJWTAuthtoken generates and signes a new jwt token for a user. This is a global function to be able to call it from integration tests. // NewUserJWTAuthtoken generates and signes a new jwt token for a user. This is a global function to be able to call it from integration tests.
func NewUserJWTAuthtoken(user *models.User) (token string, err error) { func NewUserJWTAuthtoken(user *user.User) (token string, err error) {
t := jwt.New(jwt.SigningMethodHS256) t := jwt.New(jwt.SigningMethodHS256)
// Set claims // Set claims
@ -78,7 +79,7 @@ func GetAuthFromClaims(c echo.Context) (a web.Auth, err error) {
return models.GetLinkShareFromClaims(claims) return models.GetLinkShareFromClaims(claims)
} }
if typ == AuthTypeUser { if typ == AuthTypeUser {
return models.GetUserFromClaims(claims) return user.GetUserFromClaims(claims)
} }
return nil, echo.NewHTTPError(http.StatusBadRequest, models.Message{Message: "Invalid JWT token."}) return nil, echo.NewHTTPError(http.StatusBadRequest, models.Message{Message: "Invalid JWT token."})
} }

View file

@ -18,6 +18,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -46,7 +47,7 @@ func GetListsByNamespaceID(c echo.Context) error {
} }
// Get the lists // Get the lists
doer, err := models.GetCurrentUser(c) doer, err := user.GetCurrentUser(c)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }
@ -73,7 +74,7 @@ func getNamespace(c echo.Context) (namespace *models.Namespace, err error) {
} }
// Check if the user has acces to that namespace // Check if the user has acces to that namespace
user, err := models.GetCurrentUser(c) user, err := user.GetCurrentUser(c)
if err != nil { if err != nil {
return return
} }

View file

@ -18,6 +18,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
user2 "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@ -41,13 +42,13 @@ type Token struct {
// @Failure 403 {object} models.Message "Invalid username or password." // @Failure 403 {object} models.Message "Invalid username or password."
// @Router /login [post] // @Router /login [post]
func Login(c echo.Context) error { func Login(c echo.Context) error {
u := models.UserLogin{} u := user2.Login{}
if err := c.Bind(&u); err != nil { if err := c.Bind(&u); err != nil {
return c.JSON(http.StatusBadRequest, models.Message{"Please provide a username and password."}) return c.JSON(http.StatusBadRequest, models.Message{"Please provide a username and password."})
} }
// Check user // Check user
user, err := models.CheckUserCredentials(&u) user, err := user2.CheckUserCredentials(&u)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }
@ -80,7 +81,7 @@ func RenewToken(c echo.Context) error {
return echo.ErrBadRequest return echo.ErrBadRequest
} }
user, err := models.GetUserFromClaims(claims) user, err := user2.GetUserFromClaims(claims)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -18,6 +18,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
user2 "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -45,7 +46,7 @@ func UploadTaskAttachment(c echo.Context) error {
} }
// Rights check // Rights check
user, err := models.GetCurrentUser(c) user, err := user2.GetCurrentUser(c)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }
@ -114,7 +115,7 @@ func GetTaskAttachment(c echo.Context) error {
} }
// Rights check // Rights check
user, err := models.GetCurrentUser(c) user, err := user2.GetCurrentUser(c)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -19,6 +19,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -40,13 +41,20 @@ func RegisterUser(c echo.Context) error {
return echo.ErrNotFound return echo.ErrNotFound
} }
// Check for Request Content // Check for Request Content
var datUser *models.APIUserPassword var datUser *user.APIUserPassword
if err := c.Bind(&datUser); err != nil { if err := c.Bind(&datUser); err != nil {
return c.JSON(http.StatusBadRequest, models.Message{"No or invalid user model provided."}) return c.JSON(http.StatusBadRequest, models.Message{"No or invalid user model provided."})
} }
// Insert the user // Insert the user
newUser, err := models.CreateUser(datUser.APIFormat()) newUser, err := user.CreateUser(datUser.APIFormat())
if err != nil {
return handler.HandleHTTPError(err, c)
}
// Add its namespace
newN := &models.Namespace{Name: newUser.Username, Description: newUser.Username + "'s namespace.", Owner: newUser}
err = newN.Create(newUser)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -18,6 +18,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -36,12 +37,12 @@ import (
// @Router /user/confirm [post] // @Router /user/confirm [post]
func UserConfirmEmail(c echo.Context) error { func UserConfirmEmail(c echo.Context) error {
// Check for Request Content // Check for Request Content
var emailConfirm models.EmailConfirm var emailConfirm user.EmailConfirm
if err := c.Bind(&emailConfirm); err != nil { if err := c.Bind(&emailConfirm); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No token provided.") return echo.NewHTTPError(http.StatusBadRequest, "No token provided.")
} }
err := models.UserEmailConfirm(&emailConfirm) err := user.ConfirmEmail(&emailConfirm)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -1,65 +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 v1
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/web/handler"
"github.com/labstack/echo/v4"
"net/http"
"strconv"
)
// UserDelete is the handler to delete a user
func UserDelete(c echo.Context) error {
// TODO: only allow users to allow itself
id := c.Param("id")
// Make int
userID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return c.JSON(http.StatusBadRequest, models.Message{"User ID is invalid."})
}
// Check if the user exists
_, err = models.GetUserByID(userID)
if err != nil {
if models.IsErrUserDoesNotExist(err) {
return c.JSON(http.StatusNotFound, models.Message{"The user does not exist."})
}
return c.JSON(http.StatusInternalServerError, models.Message{"Could not get user."})
}
// Get the doer options
doer, err := models.GetCurrentUser(c)
if err != nil {
return err
}
// Delete it
err = models.DeleteUserByID(userID, doer)
if err != nil {
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, models.Message{"success"})
}

View file

@ -18,6 +18,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -38,7 +39,7 @@ import (
// @Router /users [get] // @Router /users [get]
func UserList(c echo.Context) error { func UserList(c echo.Context) error {
s := c.QueryParam("s") s := c.QueryParam("s")
users, err := models.ListUsers(s) users, err := user.ListUsers(s)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }
@ -72,7 +73,7 @@ func ListUsersForList(c echo.Context) error {
} }
list := models.List{ID: listID} list := models.List{ID: listID}
currentUser, err := models.GetCurrentUser(c) currentUser, err := user.GetCurrentUser(c)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -18,6 +18,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -36,12 +37,12 @@ import (
// @Router /user/password/reset [post] // @Router /user/password/reset [post]
func UserResetPassword(c echo.Context) error { func UserResetPassword(c echo.Context) error {
// Check for Request Content // Check for Request Content
var pwReset models.PasswordReset var pwReset user.PasswordReset
if err := c.Bind(&pwReset); err != nil { if err := c.Bind(&pwReset); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No password provided.") return echo.NewHTTPError(http.StatusBadRequest, "No password provided.")
} }
err := models.UserPasswordReset(&pwReset) err := user.ResetPassword(&pwReset)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }
@ -62,7 +63,7 @@ func UserResetPassword(c echo.Context) error {
// @Router /user/password/token [post] // @Router /user/password/token [post]
func UserRequestResetPasswordToken(c echo.Context) error { func UserRequestResetPasswordToken(c echo.Context) error {
// Check for Request Content // Check for Request Content
var pwTokenReset models.PasswordTokenRequest var pwTokenReset user.PasswordTokenRequest
if err := c.Bind(&pwTokenReset); err != nil { if err := c.Bind(&pwTokenReset); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No username provided.") return echo.NewHTTPError(http.StatusBadRequest, "No username provided.")
} }
@ -71,7 +72,7 @@ func UserRequestResetPasswordToken(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, err) return echo.NewHTTPError(http.StatusBadRequest, err)
} }
err := models.RequestUserPasswordResetToken(&pwTokenReset) err := user.RequestUserPasswordResetToken(&pwTokenReset)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -17,7 +17,7 @@
package v1 package v1
import ( import (
"code.vikunja.io/api/pkg/models" user2 "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -35,12 +35,12 @@ import (
// @Failure 500 {object} models.Message "Internal server error." // @Failure 500 {object} models.Message "Internal server error."
// @Router /user [get] // @Router /user [get]
func UserShow(c echo.Context) error { func UserShow(c echo.Context) error {
userInfos, err := models.GetCurrentUser(c) userInfos, err := user2.GetCurrentUser(c)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.") return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.")
} }
user, err := models.GetUserByID(userInfos.ID) user, err := user2.GetUserByID(userInfos.ID)
if err != nil { if err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -18,6 +18,7 @@ package v1
import ( import (
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -44,7 +45,7 @@ type UserPassword struct {
// @Router /user/password [post] // @Router /user/password [post]
func UserChangePassword(c echo.Context) error { func UserChangePassword(c echo.Context) error {
// Check if the user is itself // Check if the user is itself
doer, err := models.GetCurrentUser(c) doer, err := user.GetCurrentUser(c)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.") return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.")
} }
@ -56,16 +57,16 @@ func UserChangePassword(c echo.Context) error {
} }
if newPW.OldPassword == "" { if newPW.OldPassword == "" {
return handler.HandleHTTPError(models.ErrEmptyOldPassword{}, c) return handler.HandleHTTPError(user.ErrEmptyOldPassword{}, c)
} }
// Check the current password // Check the current password
if _, err = models.CheckUserCredentials(&models.UserLogin{Username: doer.Username, Password: newPW.OldPassword}); err != nil { if _, err = user.CheckUserCredentials(&user.Login{Username: doer.Username, Password: newPW.OldPassword}); err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }
// Update the password // Update the password
if err = models.UpdateUserPassword(doer, newPW.NewPassword); err != nil { if err = user.UpdateUserPassword(doer, newPW.NewPassword); err != nil {
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View file

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"fmt" "fmt"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@ -31,10 +32,10 @@ import (
"strings" "strings"
) )
func getBasicAuthUserFromContext(c echo.Context) (user models.User, err error) { func getBasicAuthUserFromContext(c echo.Context) (user.User, error) {
u, is := c.Get("userBasicAuth").(models.User) u, is := c.Get("userBasicAuth").(user.User)
if !is { if !is {
return models.User{}, fmt.Errorf("user is not user element, is %s", reflect.TypeOf(c.Get("userBasicAuth"))) return user.User{}, fmt.Errorf("user is not user element, is %s", reflect.TypeOf(c.Get("userBasicAuth")))
} }
return u, nil return u, nil
} }

View file

@ -19,6 +19,7 @@ package caldav
import ( import (
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
user2 "code.vikunja.io/api/pkg/user"
"github.com/samedi/caldav-go/data" "github.com/samedi/caldav-go/data"
"github.com/samedi/caldav-go/errs" "github.com/samedi/caldav-go/errs"
"strconv" "strconv"
@ -39,7 +40,7 @@ type VikunjaCaldavListStorage struct {
// Used when handling a single task, like updating // Used when handling a single task, like updating
task *models.Task task *models.Task
// The current user // The current user
user *models.User user *user2.User
isPrincipal bool isPrincipal bool
isEntry bool // Entry level handling should only return a link to the principal url isEntry bool // Entry level handling should only return a link to the principal url
} }

View file

@ -23,6 +23,7 @@ import (
"code.vikunja.io/api/pkg/metrics" "code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
v1 "code.vikunja.io/api/pkg/routes/api/v1" v1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"time" "time"
@ -51,7 +52,7 @@ func setupMetrics(a *echo.Group) {
}, },
{ {
metrics.UserCountKey, metrics.UserCountKey,
models.User{}, user.User{},
}, },
{ {
metrics.NamespaceCountKey, metrics.NamespaceCountKey,

View file

@ -53,6 +53,7 @@ import (
apiv1 "code.vikunja.io/api/pkg/routes/api/v1" apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/api/pkg/routes/caldav" "code.vikunja.io/api/pkg/routes/caldav"
_ "code.vikunja.io/api/pkg/swagger" // To generate swagger docs _ "code.vikunja.io/api/pkg/swagger" // To generate swagger docs
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"code.vikunja.io/web/handler" "code.vikunja.io/web/handler"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
@ -407,11 +408,11 @@ func registerCalDavRoutes(c *echo.Group) {
} }
func caldavBasicAuth(username, password string, c echo.Context) (bool, error) { func caldavBasicAuth(username, password string, c echo.Context) (bool, error) {
creds := &models.UserLogin{ creds := &user.Login{
Username: username, Username: username,
Password: password, Password: password,
} }
u, err := models.CheckUserCredentials(creds) u, err := user.CheckUserCredentials(creds)
if err != nil { if err != nil {
log.Errorf("Error during basic auth for caldav: %v", err) log.Errorf("Error during basic auth for caldav: %v", err)
return false, nil return false, nil

50
pkg/user/db.go Normal file
View file

@ -0,0 +1,50 @@
// 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 user
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"github.com/go-xorm/xorm"
)
var x *xorm.Engine
// InitDB sets up the database connection to use in this module
func InitDB() (err error) {
x, err = db.CreateDBEngine()
if err != nil {
log.Criticalf("Could not connect to db: %v", err.Error())
return
}
// Cache
if config.CacheEnabled.GetBool() && config.CacheType.GetString() == "redis" {
db.RegisterTableStructsForCache(GetTables())
}
return nil
}
// GetTables returns all structs which are also a table.
func GetTables() []interface{} {
return []interface{}{
&User{},
}
}

291
pkg/user/error.go Normal file
View file

@ -0,0 +1,291 @@
// Copyright2018-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 user
import (
"code.vikunja.io/web"
"fmt"
"net/http"
)
// =====================
// 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."}
}

Some files were not shown because too many files have changed in this diff Show more