Add session handling for namespace and related types

This commit is contained in:
kolaente 2020-12-22 21:15:37 +01:00
parent a62d222132
commit ce2d359714
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
16 changed files with 213 additions and 129 deletions

View File

@ -163,7 +163,7 @@ var userCreateCmd = &cobra.Command{
log.Fatalf("Error creating new user: %s", err)
}
err = models.CreateNewNamespaceForUser(newUser)
err = models.CreateNewNamespaceForUser(s, newUser)
if err != nil {
log.Fatalf("Error creating new namespace for user: %s", err)
}

View File

@ -441,7 +441,7 @@ func (l *List) CheckIsArchived(s *xorm.Session) (err error) {
// When creating a new list, we check if the namespace is archived
if l.ID == 0 {
n := &Namespace{ID: l.NamespaceID}
return n.CheckIsArchived()
return n.CheckIsArchived(s)
}
nl := &NamespaceList{}

View File

@ -28,7 +28,6 @@ import (
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
"github.com/imdario/mergo"
"xorm.io/builder"
)
@ -134,8 +133,8 @@ func GetNamespaceByID(s *xorm.Session, id int64) (namespace *Namespace, err erro
}
// CheckIsArchived returns an ErrNamespaceIsArchived if the namepace is archived.
func (n *Namespace) CheckIsArchived() error {
exists, err := x.
func (n *Namespace) CheckIsArchived(s *xorm.Session) error {
exists, err := s.
Where("id = ? AND is_archived = true", n.ID).
Exist(&Namespace{})
if err != nil {
@ -159,8 +158,12 @@ func (n *Namespace) CheckIsArchived() error {
// @Failure 403 {object} web.HTTPError "The user does not have access to that namespace."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id} [get]
func (n *Namespace) ReadOne() (err error) {
*n, err = GetNamespaceByID(n.ID)
func (n *Namespace) ReadOne(s *xorm.Session) (err error) {
nn, err := GetNamespaceByID(s, n.ID)
if err != nil {
return err
}
*n = *nn
return
}
@ -199,7 +202,7 @@ func makeNamespaceSliceFromMap(namespaces map[int64]*NamespaceWithLists, userMap
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces [get]
//nolint:gocyclo
func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
func (n *Namespace) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
if _, is := a.(*LinkSharing); is {
return nil, 0, 0, ErrGenericForbidden{}
}
@ -241,7 +244,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
}
limit, start := getLimitFromPageIndex(page, perPage)
query := x.Select("namespaces.*").
query := s.Select("namespaces.*").
Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
@ -260,7 +263,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
return nil, 0, 0, err
}
numberOfTotalItems, err = x.
numberOfTotalItems, err = s.
Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
@ -286,7 +289,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
// Get all owners
userMap := make(map[int64]*user.User)
err = x.In("id", userIDs).Find(&userMap)
err = s.In("id", userIDs).Find(&userMap)
if err != nil {
return nil, 0, 0, err
}
@ -298,7 +301,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
// Get all lists
lists := []*List{}
listQuery := x.
listQuery := s.
In("namespace_id", namespaceids)
if !n.IsArchived {
@ -322,7 +325,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
// Get all lists individually shared with our user (not via a namespace)
individualLists := []*List{}
iListQuery := x.Select("l.*").
iListQuery := s.Select("l.*").
Table("list").
Alias("l").
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
@ -352,7 +355,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
}
// More details for the lists
err = addListDetails(lists)
err = addListDetails(s, lists)
if err != nil {
return nil, 0, 0, err
}
@ -378,7 +381,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
// Check if we have any favorites or favorited lists and remove the favorites namespace from the list if not
var favoriteCount int64
favoriteCount, err = x.
favoriteCount, err = s.
Join("INNER", "list", "tasks.list_id = list.id").
Join("INNER", "namespaces", "list.namespace_id = namespaces.id").
Where(builder.And(builder.Eq{"tasks.is_favorite": true}, builder.In("namespaces.id", namespaceids))).
@ -449,7 +452,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces [put]
func (n *Namespace) Create(a web.Auth) (err error) {
func (n *Namespace) Create(s *xorm.Session, a web.Auth) (err error) {
// Check if we have at least a name
if n.Title == "" {
return ErrNamespaceNameCannotBeEmpty{NamespaceID: 0, UserID: a.GetID()}
@ -464,7 +467,7 @@ func (n *Namespace) Create(a web.Auth) (err error) {
n.OwnerID = n.Owner.ID
// Insert
if _, err = x.Insert(n); err != nil {
if _, err = s.Insert(n); err != nil {
return err
}
@ -474,12 +477,12 @@ func (n *Namespace) Create(a web.Auth) (err error) {
// CreateNewNamespaceForUser creates a new namespace for a user. To prevent import cycles, we can't do that
// directly in the user.Create function.
func CreateNewNamespaceForUser(user *user.User) (err error) {
func CreateNewNamespaceForUser(s *xorm.Session, user *user.User) (err error) {
newN := &Namespace{
Title: user.Username,
Description: user.Username + "'s namespace.",
}
return newN.Create(user)
return newN.Create(s, user)
}
// Delete deletes a namespace
@ -494,22 +497,22 @@ func CreateNewNamespaceForUser(user *user.User) (err error) {
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id} [delete]
func (n *Namespace) Delete() (err error) {
func (n *Namespace) Delete(s *xorm.Session) (err error) {
// Check if the namespace exists
_, err = GetNamespaceByID(n.ID)
_, err = GetNamespaceByID(s, n.ID)
if err != nil {
return
}
// Delete the namespace
_, err = x.ID(n.ID).Delete(&Namespace{})
_, err = s.ID(n.ID).Delete(&Namespace{})
if err != nil {
return
}
// Delete all lists with their tasks
lists, err := GetListsByNamespaceID(n.ID, &user.User{})
lists, err := GetListsByNamespaceID(s, n.ID, &user.User{})
if err != nil {
return
}
@ -522,13 +525,13 @@ func (n *Namespace) Delete() (err error) {
}
// Delete tasks
_, err = x.In("list_id", listIDs).Delete(&Task{})
_, err = s.In("list_id", listIDs).Delete(&Task{})
if err != nil {
return
}
// Delete the lists
_, err = x.In("id", listIDs).Delete(&List{})
_, err = s.In("id", listIDs).Delete(&List{})
if err != nil {
return
}
@ -552,14 +555,14 @@ func (n *Namespace) Delete() (err error) {
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespace/{id} [post]
func (n *Namespace) Update() (err error) {
func (n *Namespace) Update(s *xorm.Session) (err error) {
// Check if we have at least a name
if n.Title == "" {
return ErrNamespaceNameCannotBeEmpty{NamespaceID: n.ID}
}
// Check if the namespace exists
currentNamespace, err := GetNamespaceByID(n.ID)
currentNamespace, err := GetNamespaceByID(s, n.ID)
if err != nil {
return
}
@ -591,7 +594,7 @@ func (n *Namespace) Update() (err error) {
}
// Do the actual update
_, err = x.
_, err = s.
ID(currentNamespace.ID).
Cols(colsToUpdate...).
Update(n)

View File

@ -19,37 +19,38 @@ package models
import (
"code.vikunja.io/web"
"xorm.io/builder"
"xorm.io/xorm"
)
// CanWrite checks if a user has write access to a namespace
func (n *Namespace) CanWrite(a web.Auth) (bool, error) {
can, _, err := n.checkRight(a, RightWrite, RightAdmin)
func (n *Namespace) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
can, _, err := n.checkRight(s, a, RightWrite, RightAdmin)
return can, err
}
// IsAdmin returns true or false if the user is admin on that namespace or not
func (n *Namespace) IsAdmin(a web.Auth) (bool, error) {
is, _, err := n.checkRight(a, RightAdmin)
func (n *Namespace) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
is, _, err := n.checkRight(s, a, RightAdmin)
return is, err
}
// CanRead checks if a user has read access to that namespace
func (n *Namespace) CanRead(a web.Auth) (bool, int, error) {
return n.checkRight(a, RightRead, RightWrite, RightAdmin)
func (n *Namespace) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
return n.checkRight(s, a, RightRead, RightWrite, RightAdmin)
}
// CanUpdate checks if the user can update the namespace
func (n *Namespace) CanUpdate(a web.Auth) (bool, error) {
return n.IsAdmin(a)
func (n *Namespace) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
return n.IsAdmin(s, a)
}
// CanDelete checks if the user can delete a namespace
func (n *Namespace) CanDelete(a web.Auth) (bool, error) {
return n.IsAdmin(a)
func (n *Namespace) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
return n.IsAdmin(s, a)
}
// CanCreate checks if the user can create a new namespace
func (n *Namespace) CanCreate(a web.Auth) (bool, error) {
func (n *Namespace) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
if _, is := a.(*LinkSharing); is {
return false, nil
}
@ -58,7 +59,7 @@ func (n *Namespace) CanCreate(a web.Auth) (bool, error) {
return true, nil
}
func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
func (n *Namespace) checkRight(s *xorm.Session, a web.Auth, rights ...Right) (bool, int, error) {
// If the auth is a link share, don't do anything
if _, is := a.(*LinkSharing); is {
@ -66,13 +67,12 @@ func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
}
// Get the namespace and check the right
nn := &Namespace{ID: n.ID}
err := nn.GetSimpleByID()
nn, err := getNamespaceSimpleByID(s, n.ID)
if err != nil {
return false, 0, err
}
if a.GetID() == n.OwnerID {
if a.GetID() == nn.OwnerID {
return true, int(RightAdmin), nil
}
@ -113,7 +113,8 @@ func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
var maxRights = 0
r := &allRights{}
exists, err := x.Select("*").
exists, err := s.
Select("*").
Table("namespaces").
// User stuff
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").

View File

@ -18,6 +18,7 @@ package models
import (
"time"
"xorm.io/xorm"
"code.vikunja.io/web"
)
@ -62,7 +63,7 @@ func (TeamNamespace) TableName() string {
// @Failure 403 {object} web.HTTPError "The team does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/teams [put]
func (tn *TeamNamespace) Create(a web.Auth) (err error) {
func (tn *TeamNamespace) Create(s *xorm.Session, a web.Auth) (err error) {
// Check if the rights are valid
if err = tn.Right.isValid(); err != nil {
@ -76,13 +77,14 @@ func (tn *TeamNamespace) Create(a web.Auth) (err error) {
}
// Check if the namespace exists
_, err = GetNamespaceByID(tn.NamespaceID)
_, err = GetNamespaceByID(s, tn.NamespaceID)
if err != nil {
return
}
// Check if the team already has access to the namespace
exists, err := x.Where("team_id = ?", tn.TeamID).
exists, err := s.
Where("team_id = ?", tn.TeamID).
And("namespace_id = ?", tn.NamespaceID).
Get(&TeamNamespace{})
if err != nil {
@ -93,7 +95,7 @@ func (tn *TeamNamespace) Create(a web.Auth) (err error) {
}
// Insert the new team
_, err = x.Insert(tn)
_, err = s.Insert(tn)
return
}
@ -110,7 +112,7 @@ func (tn *TeamNamespace) Create(a web.Auth) (err error) {
// @Failure 404 {object} web.HTTPError "team or namespace does not exist."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{namespaceID}/teams/{teamID} [delete]
func (tn *TeamNamespace) Delete() (err error) {
func (tn *TeamNamespace) Delete(s *xorm.Session) (err error) {
// Check if the team exists
_, err = GetTeamByID(tn.TeamID)
@ -119,7 +121,8 @@ func (tn *TeamNamespace) Delete() (err error) {
}
// Check if the team has access to the namespace
has, err := x.Where("team_id = ? AND namespace_id = ?", tn.TeamID, tn.NamespaceID).
has, err := s.
Where("team_id = ? AND namespace_id = ?", tn.TeamID, tn.NamespaceID).
Get(&TeamNamespace{})
if err != nil {
return
@ -129,7 +132,8 @@ func (tn *TeamNamespace) Delete() (err error) {
}
// Delete the relation
_, err = x.Where("team_id = ?", tn.TeamID).
_, err = s.
Where("team_id = ?", tn.TeamID).
And("namespace_id = ?", tn.NamespaceID).
Delete(TeamNamespace{})
@ -151,10 +155,10 @@ func (tn *TeamNamespace) Delete() (err error) {
// @Failure 403 {object} web.HTTPError "No right to see the namespace."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/teams [get]
func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
func (tn *TeamNamespace) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
// Check if the user can read the namespace
n := Namespace{ID: tn.NamespaceID}
canRead, _, err := n.CanRead(a)
canRead, _, err := n.CanRead(s, a)
if err != nil {
return nil, 0, 0, err
}
@ -167,7 +171,8 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
limit, start := getLimitFromPageIndex(page, perPage)
query := x.Table("teams").
query := s.
Table("teams").
Join("INNER", "team_namespaces", "team_id = teams.id").
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
Where("teams.name LIKE ?", "%"+search+"%")
@ -189,7 +194,8 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
return
}
numberOfTotalItems, err = x.Table("teams").
numberOfTotalItems, err = s.
Table("teams").
Join("INNER", "team_namespaces", "team_id = teams.id").
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
Where("teams.name LIKE ?", "%"+search+"%").
@ -213,14 +219,14 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
// @Failure 404 {object} web.HTTPError "Team or namespace does not exist."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{namespaceID}/teams/{teamID} [post]
func (tn *TeamNamespace) Update() (err error) {
func (tn *TeamNamespace) Update(s *xorm.Session) (err error) {
// Check if the right is valid
if err := tn.Right.isValid(); err != nil {
return err
}
_, err = x.
_, err = s.
Where("namespace_id = ? AND team_id = ?", tn.NamespaceID, tn.TeamID).
Cols("right").
Update(tn)

View File

@ -18,22 +18,23 @@ package models
import (
"code.vikunja.io/web"
"xorm.io/xorm"
)
// CanCreate checks if one can create a new team <-> namespace relation
func (tn *TeamNamespace) CanCreate(a web.Auth) (bool, error) {
func (tn *TeamNamespace) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
n := &Namespace{ID: tn.NamespaceID}
return n.IsAdmin(a)
return n.IsAdmin(s, a)
}
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
func (tn *TeamNamespace) CanDelete(a web.Auth) (bool, error) {
func (tn *TeamNamespace) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
n := &Namespace{ID: tn.NamespaceID}
return n.IsAdmin(a)
return n.IsAdmin(s, a)
}
// CanUpdate checks if a user can update a team from a Only namespace admins can do that.
func (tn *TeamNamespace) CanUpdate(a web.Auth) (bool, error) {
func (tn *TeamNamespace) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
n := &Namespace{ID: tn.NamespaceID}
return n.IsAdmin(a)
return n.IsAdmin(s, a)
}

View File

@ -80,6 +80,7 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
tn := &TeamNamespace{
ID: tt.fields.ID,
@ -91,15 +92,16 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
}
if got, _ := tn.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
if got, _ := tn.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
t.Errorf("TeamNamespace.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
}
if got, _ := tn.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
if got, _ := tn.CanDelete(s, tt.args.a); got != tt.want["CanDelete"] {
t.Errorf("TeamNamespace.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
}
if got, _ := tn.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
if got, _ := tn.CanUpdate(s, tt.args.a); got != tt.want["CanUpdate"] {
t.Errorf("TeamNamespace.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
}
_ = s.Close()
})
}
}

View File

@ -36,29 +36,35 @@ func TestTeamNamespace_ReadAll(t *testing.T) {
NamespaceID: 3,
}
db.LoadAndAssertFixtures(t)
teams, _, _, err := tn.ReadAll(u, "", 1, 50)
s := x.NewSession()
teams, _, _, err := tn.ReadAll(s, u, "", 1, 50)
assert.NoError(t, err)
assert.Equal(t, reflect.TypeOf(teams).Kind(), reflect.Slice)
s := reflect.ValueOf(teams)
assert.Equal(t, s.Len(), 2)
ts := reflect.ValueOf(teams)
assert.Equal(t, ts.Len(), 2)
_ = s.Close()
})
t.Run("nonexistant namespace", func(t *testing.T) {
tn := TeamNamespace{
NamespaceID: 9999,
}
db.LoadAndAssertFixtures(t)
_, _, _, err := tn.ReadAll(u, "", 1, 50)
s := x.NewSession()
_, _, _, err := tn.ReadAll(s, u, "", 1, 50)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
_ = s.Close()
})
t.Run("no right for namespace", func(t *testing.T) {
tn := TeamNamespace{
NamespaceID: 17,
}
db.LoadAndAssertFixtures(t)
_, _, _, err := tn.ReadAll(u, "", 1, 50)
s := x.NewSession()
_, _, _, err := tn.ReadAll(s, u, "", 1, 50)
assert.Error(t, err)
assert.True(t, IsErrNeedToHaveNamespaceReadAccess(err))
_ = s.Close()
})
}
@ -72,10 +78,15 @@ func TestTeamNamespace_Create(t *testing.T) {
Right: RightAdmin,
}
db.LoadAndAssertFixtures(t)
allowed, _ := tn.CanCreate(u)
s := x.NewSession()
allowed, _ := tn.CanCreate(s, u)
assert.True(t, allowed)
err := tn.Create(u)
err := tn.Create(s, u)
assert.NoError(t, err)
err = s.Commit()
assert.NoError(t, err)
db.AssertExists(t, "team_namespaces", map[string]interface{}{
"team_id": 1,
"namespace_id": 1,
@ -89,9 +100,11 @@ func TestTeamNamespace_Create(t *testing.T) {
Right: RightRead,
}
db.LoadAndAssertFixtures(t)
err := tn.Create(u)
s := x.NewSession()
err := tn.Create(s, u)
assert.Error(t, err)
assert.True(t, IsErrTeamAlreadyHasAccess(err))
_ = s.Close()
})
t.Run("invalid team right", func(t *testing.T) {
tn := TeamNamespace{
@ -100,9 +113,11 @@ func TestTeamNamespace_Create(t *testing.T) {
Right: RightUnknown,
}
db.LoadAndAssertFixtures(t)
err := tn.Create(u)
s := x.NewSession()
err := tn.Create(s, u)
assert.Error(t, err)
assert.True(t, IsErrInvalidRight(err))
_ = s.Close()
})
t.Run("nonexistant team", func(t *testing.T) {
tn := TeamNamespace{
@ -110,9 +125,11 @@ func TestTeamNamespace_Create(t *testing.T) {
NamespaceID: 1,
}
db.LoadAndAssertFixtures(t)
err := tn.Create(u)
s := x.NewSession()
err := tn.Create(s, u)
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotExist(err))
_ = s.Close()
})
t.Run("nonexistant namespace", func(t *testing.T) {
tn := TeamNamespace{
@ -120,9 +137,11 @@ func TestTeamNamespace_Create(t *testing.T) {
NamespaceID: 9999,
}
db.LoadAndAssertFixtures(t)
err := tn.Create(u)
s := x.NewSession()
err := tn.Create(s, u)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
_ = s.Close()
})
}
@ -135,10 +154,14 @@ func TestTeamNamespace_Delete(t *testing.T) {
NamespaceID: 9,
}
db.LoadAndAssertFixtures(t)
allowed, _ := tn.CanDelete(u)
s := x.NewSession()
allowed, _ := tn.CanDelete(s, u)
assert.True(t, allowed)
err := tn.Delete()
err := tn.Delete(s)
assert.NoError(t, err)
err = s.Commit()
assert.NoError(t, err)
db.AssertMissing(t, "team_namespaces", map[string]interface{}{
"team_id": 7,
"namespace_id": 9,
@ -150,9 +173,11 @@ func TestTeamNamespace_Delete(t *testing.T) {
NamespaceID: 3,
}
db.LoadAndAssertFixtures(t)
err := tn.Delete()
s := x.NewSession()
err := tn.Delete(s)
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotExist(err))
_ = s.Close()
})
t.Run("nonexistant namespace", func(t *testing.T) {
tn := TeamNamespace{
@ -160,9 +185,11 @@ func TestTeamNamespace_Delete(t *testing.T) {
NamespaceID: 9999,
}
db.LoadAndAssertFixtures(t)
err := tn.Delete()
s := x.NewSession()
err := tn.Delete(s)
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotHaveAccessToNamespace(err))
_ = s.Close()
})
}
@ -221,6 +248,7 @@ func TestTeamNamespace_Update(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
tl := &TeamNamespace{
ID: tt.fields.ID,
@ -232,13 +260,17 @@ func TestTeamNamespace_Update(t *testing.T) {
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
}
err := tl.Update()
err := tl.Update(s)
if (err != nil) != tt.wantErr {
t.Errorf("TeamNamespace.Update() error = %v, wantErr %v", err, tt.wantErr)
}
if (err != nil) && tt.wantErr && !tt.errType(err) {
t.Errorf("TeamNamespace.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
}
err = s.Commit()
assert.NoError(t, err)
if !tt.wantErr {
db.AssertExists(t, "team_namespaces", map[string]interface{}{
"team_id": tt.fields.TeamID,

View File

@ -36,8 +36,12 @@ func TestNamespace_Create(t *testing.T) {
t.Run("normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
err := dummynamespace.Create(user1)
s := x.NewSession()
err := dummynamespace.Create(s, user1)
assert.NoError(t, err)
err = s.Commit()
assert.NoError(t, err)
db.AssertExists(t, "namespaces", map[string]interface{}{
"title": "Test",
"description": "Lorem Ipsum",
@ -45,18 +49,22 @@ func TestNamespace_Create(t *testing.T) {
})
t.Run("no title", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
n2 := Namespace{}
err := n2.Create(user1)
err := n2.Create(s, user1)
assert.Error(t, err)
assert.True(t, IsErrNamespaceNameCannotBeEmpty(err))
_ = s.Close()
})
t.Run("nonexistant user", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
nUser := &user.User{ID: 9482385}
dnsp2 := dummynamespace
err := dnsp2.Create(nUser)
err := dnsp2.Create(s, nUser)
assert.Error(t, err)
assert.True(t, user.IsErrUserDoesNotExist(err))
_ = s.Close()
})
}
@ -64,28 +72,36 @@ func TestNamespace_ReadOne(t *testing.T) {
t.Run("normal", func(t *testing.T) {
n := &Namespace{ID: 1}
db.LoadAndAssertFixtures(t)
err := n.ReadOne()
s := x.NewSession()
err := n.ReadOne(s)
assert.NoError(t, err)
assert.Equal(t, n.Title, "testnamespace")
_ = s.Close()
})
t.Run("nonexistant", func(t *testing.T) {
n := &Namespace{ID: 99999}
db.LoadAndAssertFixtures(t)
err := n.ReadOne()
s := x.NewSession()
err := n.ReadOne(s)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
_ = s.Close()
})
}
func TestNamespace_Update(t *testing.T) {
t.Run("normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
n := &Namespace{
ID: 1,
Title: "Lorem Ipsum",
}
err := n.Update()
err := n.Update(s)
assert.NoError(t, err)
err = s.Commit()
assert.NoError(t, err)
db.AssertExists(t, "namespaces", map[string]interface{}{
"id": 1,
"title": "Lorem Ipsum",
@ -93,56 +109,68 @@ func TestNamespace_Update(t *testing.T) {
})
t.Run("nonexisting", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
n := &Namespace{
ID: 99999,
Title: "Lorem Ipsum",
}
err := n.Update()
err := n.Update(s)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
_ = s.Close()
})
t.Run("nonexisting owner", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
n := &Namespace{
ID: 1,
Title: "Lorem Ipsum",
Owner: &user.User{ID: 99999},
}
err := n.Update()
err := n.Update(s)
assert.Error(t, err)
assert.True(t, user.IsErrUserDoesNotExist(err))
_ = s.Close()
})
t.Run("no title", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
n := &Namespace{
ID: 1,
}
err := n.Update()
err := n.Update(s)
assert.Error(t, err)
assert.True(t, IsErrNamespaceNameCannotBeEmpty(err))
_ = s.Close()
})
}
func TestNamespace_Delete(t *testing.T) {
t.Run("normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
n := &Namespace{
ID: 1,
}
err := n.Delete()
err := n.Delete(s)
assert.NoError(t, err)
err = s.Commit()
assert.NoError(t, err)
db.AssertMissing(t, "namespaces", map[string]interface{}{
"id": 1,
})
})
t.Run("nonexisting", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
n := &Namespace{
ID: 9999,
}
err := n.Delete()
err := n.Delete(s)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
_ = s.Close()
})
}
@ -152,9 +180,12 @@ func TestNamespace_ReadAll(t *testing.T) {
user11 := &user.User{ID: 11}
user12 := &user.User{ID: 12}
s := x.NewSession()
defer s.Close()
t.Run("normal", func(t *testing.T) {
n := &Namespace{}
nn, _, _, err := n.ReadAll(user1, "", 1, -1)
nn, _, _, err := n.ReadAll(s, user1, "", 1, -1)
assert.NoError(t, err)
namespaces := nn.([]*NamespaceWithLists)
assert.NotNil(t, namespaces)
@ -174,7 +205,7 @@ func TestNamespace_ReadAll(t *testing.T) {
n := &Namespace{
NamespacesOnly: true,
}
nn, _, _, err := n.ReadAll(user1, "", 1, -1)
nn, _, _, err := n.ReadAll(s, user1, "", 1, -1)
assert.NoError(t, err)
namespaces := nn.([]*NamespaceWithLists)
assert.NotNil(t, namespaces)
@ -188,7 +219,7 @@ func TestNamespace_ReadAll(t *testing.T) {
n := &Namespace{
NamespacesOnly: true,
}
nn, _, _, err := n.ReadAll(user7, "13,14", 1, -1)
nn, _, _, err := n.ReadAll(s, user7, "13,14", 1, -1)
assert.NoError(t, err)
namespaces := nn.([]*NamespaceWithLists)
assert.NotNil(t, namespaces)
@ -200,7 +231,7 @@ func TestNamespace_ReadAll(t *testing.T) {
n := &Namespace{
NamespacesOnly: true,
}
nn, _, _, err := n.ReadAll(user1, "1,w", 1, -1)
nn, _, _, err := n.ReadAll(s, user1, "1,w", 1, -1)
assert.NoError(t, err)
namespaces := nn.([]*NamespaceWithLists)
assert.NotNil(t, namespaces)
@ -211,7 +242,7 @@ func TestNamespace_ReadAll(t *testing.T) {
n := &Namespace{
IsArchived: true,
}
nn, _, _, err := n.ReadAll(user1, "", 1, -1)
nn, _, _, err := n.ReadAll(s, user1, "", 1, -1)
namespaces := nn.([]*NamespaceWithLists)
assert.NoError(t, err)
assert.NotNil(t, namespaces)
@ -222,7 +253,7 @@ func TestNamespace_ReadAll(t *testing.T) {
})
t.Run("no favorites", func(t *testing.T) {
n := &Namespace{}
nn, _, _, err := n.ReadAll(user11, "", 1, -1)
nn, _, _, err := n.ReadAll(s, user11, "", 1, -1)
namespaces := nn.([]*NamespaceWithLists)
assert.NoError(t, err)
// Assert the first namespace is not the favorites namespace
@ -230,7 +261,7 @@ func TestNamespace_ReadAll(t *testing.T) {
})
t.Run("no favorite tasks but namespace", func(t *testing.T) {
n := &Namespace{}
nn, _, _, err := n.ReadAll(user12, "", 1, -1)
nn, _, _, err := n.ReadAll(s, user12, "", 1, -1)
namespaces := nn.([]*NamespaceWithLists)
assert.NoError(t, err)
// Assert the first namespace is the favorites namespace and contains lists
@ -239,7 +270,7 @@ func TestNamespace_ReadAll(t *testing.T) {
})
t.Run("no saved filters", func(t *testing.T) {
n := &Namespace{}
nn, _, _, err := n.ReadAll(user11, "", 1, -1)
nn, _, _, err := n.ReadAll(s, user11, "", 1, -1)
namespaces := nn.([]*NamespaceWithLists)
assert.NoError(t, err)
// Assert the first namespace is not the favorites namespace

View File

@ -18,6 +18,7 @@ package models
import (
"time"
"xorm.io/xorm"
user2 "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web"
@ -64,7 +65,7 @@ func (NamespaceUser) TableName() string {
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/users [put]
func (nu *NamespaceUser) Create(a web.Auth) (err error) {
func (nu *NamespaceUser) Create(s *xorm.Session, a web.Auth) (err error) {
// Reset the id
nu.ID = 0
@ -74,7 +75,7 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
}
// Check if the namespace exists
l, err := GetNamespaceByID(nu.NamespaceID)
l, err := GetNamespaceByID(s, nu.NamespaceID)
if err != nil {
return
}
@ -92,7 +93,9 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
return ErrUserAlreadyHasNamespaceAccess{UserID: nu.UserID, NamespaceID: nu.NamespaceID}
}
exist, err := x.Where("namespace_id = ? AND user_id = ?", nu.NamespaceID, nu.UserID).Get(&NamespaceUser{})
exist, err := s.
Where("namespace_id = ? AND user_id = ?", nu.NamespaceID, nu.UserID).
Get(&NamespaceUser{})
if err != nil {
return
}
@ -101,7 +104,7 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
}
// Insert user <-> namespace relation
_, err = x.Insert(nu)
_, err = s.Insert(nu)
return
}
@ -119,7 +122,7 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
// @Failure 404 {object} web.HTTPError "user or namespace does not exist."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{namespaceID}/users/{userID} [delete]
func (nu *NamespaceUser) Delete() (err error) {
func (nu *NamespaceUser) Delete(s *xorm.Session) (err error) {
// Check if the user exists
user, err := user2.GetUserByUsername(nu.Username)
@ -129,7 +132,8 @@ func (nu *NamespaceUser) Delete() (err error) {
nu.UserID = user.ID
// Check if the user has access to the namespace
has, err := x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
has, err := s.
Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
Get(&NamespaceUser{})
if err != nil {
return
@ -138,7 +142,8 @@ func (nu *NamespaceUser) Delete() (err error) {
return ErrUserDoesNotHaveAccessToNamespace{NamespaceID: nu.NamespaceID, UserID: nu.UserID}
}
_, err = x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
_, err = s.
Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
Delete(&NamespaceUser{})
return
}
@ -158,10 +163,10 @@ func (nu *NamespaceUser) Delete() (err error) {
// @Failure 403 {object} web.HTTPError "No right to see the namespace."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{id}/users [get]
func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
func (nu *NamespaceUser) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
// Check if the user has access to the namespace
l := Namespace{ID: nu.NamespaceID}
canRead, _, err := l.CanRead(a)
canRead, _, err := l.CanRead(s, a)
if err != nil {
return nil, 0, 0, err
}
@ -174,7 +179,7 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
limit, start := getLimitFromPageIndex(page, perPage)
query := x.
query := s.
Join("INNER", "users_namespace", "user_id = users.id").
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
Where("users.username LIKE ?", "%"+search+"%")
@ -191,7 +196,7 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
u.Email = ""
}
numberOfTotalItems, err = x.
numberOfTotalItems, err = s.
Join("INNER", "users_namespace", "user_id = users.id").
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
Where("users.username LIKE ?", "%"+search+"%").
@ -215,7 +220,7 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
// @Failure 404 {object} web.HTTPError "User or namespace does not exist."
// @Failure 500 {object} models.Message "Internal error"
// @Router /namespaces/{namespaceID}/users/{userID} [post]
func (nu *NamespaceUser) Update() (err error) {
func (nu *NamespaceUser) Update(s *xorm.Session) (err error) {
// Check if the right is valid
if err := nu.Right.isValid(); err != nil {
@ -229,7 +234,7 @@ func (nu *NamespaceUser) Update() (err error) {
}
nu.UserID = user.ID
_, err = x.
_, err = s.
Where("namespace_id = ? AND user_id = ?", nu.NamespaceID, nu.UserID).
Cols("right").
Update(nu)

View File

@ -18,24 +18,25 @@ package models
import (
"code.vikunja.io/web"
"xorm.io/xorm"
)
// CanCreate checks if the user can create a new user <-> namespace relation
func (nu *NamespaceUser) CanCreate(a web.Auth) (bool, error) {
return nu.canDoNamespaceUser(a)
func (nu *NamespaceUser) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
return nu.canDoNamespaceUser(s, a)
}
// CanDelete checks if the user can delete a user <-> namespace relation
func (nu *NamespaceUser) CanDelete(a web.Auth) (bool, error) {
return nu.canDoNamespaceUser(a)
func (nu *NamespaceUser) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
return nu.canDoNamespaceUser(s, a)
}
// CanUpdate checks if the user can update a user <-> namespace relation
func (nu *NamespaceUser) CanUpdate(a web.Auth) (bool, error) {
return nu.canDoNamespaceUser(a)
func (nu *NamespaceUser) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
return nu.canDoNamespaceUser(s, a)
}
func (nu *NamespaceUser) canDoNamespaceUser(a web.Auth) (bool, error) {
func (nu *NamespaceUser) canDoNamespaceUser(s *xorm.Session, a web.Auth) (bool, error) {
n := &Namespace{ID: nu.NamespaceID}
return n.IsAdmin(a)
return n.IsAdmin(s, a)
}

View File

@ -80,6 +80,8 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := x.NewSession()
defer s.Close()
nu := &NamespaceUser{
ID: tt.fields.ID,
@ -91,13 +93,13 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
}
if got, _ := nu.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
if got, _ := nu.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
t.Errorf("NamespaceUser.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
}
if got, _ := nu.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
if got, _ := nu.CanDelete(s, tt.args.a); got != tt.want["CanDelete"] {
t.Errorf("NamespaceUser.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
}
if got, _ := nu.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
if got, _ := nu.CanUpdate(s, tt.args.a); got != tt.want["CanUpdate"] {
t.Errorf("NamespaceUser.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
}
})

View File

@ -180,7 +180,7 @@ func getOrCreateUser(cl *claims, issuer, subject string) (u *user.User, err erro
}
// And create its namespace
err = models.CreateNewNamespaceForUser(u)
err = models.CreateNewNamespaceForUser(s, u)
if err != nil {
return nil, err
}

View File

@ -36,7 +36,7 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
// Create all namespaces
for _, n := range str {
err = n.Create(user)
err = n.Create(s, user)
if err != nil {
return
}

View File

@ -80,7 +80,7 @@ func getNamespace(c echo.Context) (namespace *models.Namespace, err error) {
return
}
namespace = &models.Namespace{ID: namespaceID}
canRead, _, err := namespace.CanRead(user)
canRead, _, err := namespace.CanRead(s, user)
if err != nil {
return namespace, err
}

View File

@ -57,7 +57,7 @@ func RegisterUser(c echo.Context) error {
}
// Add its namespace
err = models.CreateNewNamespaceForUser(newUser)
err = models.CreateNewNamespaceForUser(s, newUser)
if err != nil {
return handler.HandleHTTPError(err, c)
}