New structure #7

Merged
konrad merged 6 commits from new-structure into master 2018-10-31 12:42:38 +00:00
121 changed files with 1197 additions and 875 deletions
Showing only changes of commit 4e2f2e4f31 - Show all commits

12
main.go
View File

@ -1,9 +1,11 @@
package main
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/models/mail"
"code.vikunja.io/api/routes"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes"
"context"
"github.com/spf13/viper"
@ -18,10 +20,10 @@ var Version = "0.1"
func main() {
// Init logging
models.InitLogger()
log.InitLogger()
// Init Config
err := models.InitConfig()
err := config.InitConfig()
if err != nil {
models.Log.Error(err.Error())
os.Exit(1)

View File

@ -1,39 +0,0 @@
package models
// CanDelete checks if the user can delete an task
func (i *ListTask) CanDelete(doer *User) bool {
// Get the task
lI, err := GetListTaskByID(i.ID)
if err != nil {
Log.Error("Error occurred during CanDelete for ListTask: %s", err)
return false
}
// A user can delete an task if he has write acces to its list
list := &List{ID: lI.ListID}
list.ReadOne()
return list.CanWrite(doer)
}
// CanUpdate determines if a user has the right to update a list task
func (i *ListTask) CanUpdate(doer *User) bool {
// Get the task
lI, err := GetListTaskByID(i.ID)
if err != nil {
Log.Error("Error occurred during CanDelete for ListTask: %s", err)
return false
}
// A user can update an task if he has write acces to its list
list := &List{ID: lI.ListID}
list.ReadOne()
return list.CanWrite(doer)
}
// CanCreate determines if a user has the right to create a list task
func (i *ListTask) CanCreate(doer *User) bool {
// A user can create an task if he has write acces to its list
list := &List{ID: i.ListID}
list.ReadOne()
return list.CanWrite(doer)
}

View File

@ -1,25 +0,0 @@
package models
// ListUser represents a list <-> user relation
type ListUser struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
UserID int64 `xorm:"int(11) not null INDEX" json:"user_id" param:"user"`
ListID int64 `xorm:"int(11) not null INDEX" json:"list_id" param:"list"`
Right UserRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
}
// TableName is the table name for ListUser
func (ListUser) TableName() string {
return "users_list"
}
type userWithRight struct {
User `xorm:"extends"`
Right UserRight `json:"right"`
}

View File

@ -1,60 +0,0 @@
package models
// UserRight defines the rights users can have for lists/namespaces
type UserRight int
// define unknown user right
const (
UserRightUnknown = -1
)
// Enumerate all the user rights
const (
// Can read lists in a User
UserRightRead UserRight = iota
// Can write tasks in a User like lists and todo tasks. Cannot create new lists.
UserRightWrite
// Can manage a list/namespace, can do everything
UserRightAdmin
)
func (r UserRight) isValid() error {
if r != UserRightAdmin && r != UserRightRead && r != UserRightWrite {
return ErrInvalidUserRight{r}
}
return nil
}
// CanCreate checks if the user can create a new user <-> list relation
func (lu *ListUser) CanCreate(doer *User) bool {
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanCreate for ListUser: %s", err)
return false
}
return l.CanWrite(doer)
}
// CanDelete checks if the user can delete a user <-> list relation
func (lu *ListUser) CanDelete(doer *User) bool {
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanDelete for ListUser: %s", err)
return false
}
return l.CanWrite(doer)
}
// CanUpdate checks if the user can update a user <-> list relation
func (lu *ListUser) CanUpdate(doer *User) bool {
// Get the list and check if the user has write access on it
l := List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanUpdate for ListUser: %s", err)
return false
}
return l.CanWrite(doer)
}

View File

@ -1,20 +0,0 @@
package models
// NamespaceUser represents a namespace <-> user relation
type NamespaceUser struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
UserID int64 `xorm:"int(11) not null INDEX" json:"user_id" param:"user"`
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"namespace_id" param:"namespace"`
Right UserRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
}
// TableName is the table name for NamespaceUser
func (NamespaceUser) TableName() string {
return "users_namespace"
}

View File

@ -1,34 +0,0 @@
package models
// CanCreate checks if the user can create a new user <-> namespace relation
func (nu *NamespaceUser) CanCreate(doer *User) bool {
// Get the namespace and check if the user has write access on it
n, err := GetNamespaceByID(nu.NamespaceID)
if err != nil {
Log.Error("Error occurred during CanCreate for NamespaceUser: %s", err)
return false
}
return n.CanWrite(doer)
}
// CanDelete checks if the user can delete a user <-> namespace relation
func (nu *NamespaceUser) CanDelete(doer *User) bool {
// Get the namespace and check if the user has write access on it
n, err := GetNamespaceByID(nu.NamespaceID)
if err != nil {
Log.Error("Error occurred during CanCreate for NamespaceUser: %s", err)
return false
}
return n.CanWrite(doer)
}
// CanUpdate checks if the user can update a user <-> namespace relation
func (nu *NamespaceUser) CanUpdate(doer *User) bool {
// Get the namespace and check if the user has write access on it
n, err := GetNamespaceByID(nu.NamespaceID)
if err != nil {
Log.Error("Error occurred during CanCreate for NamespaceUser: %s", err)
return false
}
return n.CanWrite(doer)
}

View File

@ -1,11 +0,0 @@
package models
// Rights defines rights methods
type Rights interface {
IsAdmin(*User) bool
CanWrite(*User) bool
CanRead(*User) bool
CanDelete(*User) bool
CanUpdate(*User) bool
CanCreate(*User) bool
}

View File

@ -1,25 +0,0 @@
package models
// TeamList defines the relation between a team and a list
type TeamList struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
TeamID int64 `xorm:"int(11) not null INDEX" json:"team_id" param:"team"`
ListID int64 `xorm:"int(11) not null INDEX" json:"list_id" param:"list"`
Right TeamRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
func (TeamList) TableName() string {
return "team_list"
}
type teamWithRight struct {
Team `xorm:"extends"`
Right TeamRight `json:"right"`
}

View File

@ -1,31 +0,0 @@
package models
// CanCreate checks if the user can create a team <-> list relation
func (tl *TeamList) CanCreate(user *User) bool {
l := List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanCreate for TeamList: %s", err)
return false
}
return l.IsAdmin(user)
}
// CanDelete checks if the user can delete a team <-> list relation
func (tl *TeamList) CanDelete(user *User) bool {
l := List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanDelete for TeamList: %s", err)
return false
}
return l.IsAdmin(user)
}
// CanUpdate checks if the user can update a team <-> list relation
func (tl *TeamList) CanUpdate(user *User) bool {
l := List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanUpdate for TeamList: %s", err)
return false
}
return l.IsAdmin(user)
}

View File

@ -1,23 +0,0 @@
package models
// CanCreate checks if the user can add a new tem member
func (tm *TeamMember) CanCreate(user *User) bool {
return tm.IsAdmin(user)
}
// CanDelete checks if the user can delete a new team member
func (tm *TeamMember) CanDelete(user *User) bool {
return tm.IsAdmin(user)
}
// IsAdmin checks if the user is team admin
func (tm *TeamMember) IsAdmin(user *User) bool {
// A user can add a member to a team if he is admin of that team
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", user.ID, tm.TeamID, true).
Get(&TeamMember{})
if err != nil {
Log.Error("Error occurred during IsAdmin for TeamMember: %s", err)
return false
}
return exists
}

View File

@ -1,20 +0,0 @@
package models
// TeamNamespace defines the relationship between a Team and a Namespace
type TeamNamespace struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
TeamID int64 `xorm:"int(11) not null INDEX" json:"team_id" param:"team"`
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"namespace_id" param:"namespace"`
Right TeamRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
func (TeamNamespace) TableName() string {
return "team_namespaces"
}

View File

@ -1,31 +0,0 @@
package models
// CanCreate checks if one can create a new team <-> namespace relation
func (tn *TeamNamespace) CanCreate(user *User) bool {
n, err := GetNamespaceByID(tn.NamespaceID)
if err != nil {
Log.Error("Error occurred during CanCreate for TeamNamespace: %s", err)
return false
}
return n.IsAdmin(user)
}
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
func (tn *TeamNamespace) CanDelete(user *User) bool {
n, err := GetNamespaceByID(tn.NamespaceID)
if err != nil {
Log.Error("Error occurred during CanDelete for TeamNamespace: %s", err)
return false
}
return n.IsAdmin(user)
}
// CanUpdate checks if a user can update a team from a namespace. Only namespace admins can do that.
func (tn *TeamNamespace) CanUpdate(user *User) bool {
n, err := GetNamespaceByID(tn.NamespaceID)
if err != nil {
Log.Error("Error occurred during CanUpdate for TeamNamespace: %s", err)
return false
}
return n.IsAdmin(user)
}

View File

@ -1,80 +0,0 @@
package models
// TeamRight defines the rights teams can have for lists/namespaces
type TeamRight int
// define unknown team right
const (
TeamRightUnknown = -1
)
// Enumerate all the team rights
const (
// Can read lists in a Team
TeamRightRead TeamRight = iota
// Can write tasks in a Team like lists and todo tasks. Cannot create new lists.
TeamRightWrite
// Can manage a list/namespace, can do everything
TeamRightAdmin
)
func (r TeamRight) isValid() error {
if r != TeamRightAdmin && r != TeamRightRead && r != TeamRightWrite {
return ErrInvalidTeamRight{r}
}
return nil
}
// CanCreate checks if the user can create a new team
func (t *Team) CanCreate(user *User) bool {
// This is currently a dummy function, later on we could imagine global limits etc.
return true
}
// CanUpdate checks if the user can update a team
func (t *Team) CanUpdate(user *User) bool {
// Check if the current user is in the team and has admin rights in it
exists, err := x.Where("team_id = ?", t.ID).
And("user_id = ?", user.ID).
And("admin = ?", true).
Get(&TeamMember{})
if err != nil {
Log.Error("Error occurred during CanUpdate for Team: %s", err)
return false
}
return exists
}
// CanDelete checks if a user can delete a team
func (t *Team) CanDelete(user *User) bool {
return t.IsAdmin(user)
}
// IsAdmin returns true when the user is admin of a team
func (t *Team) IsAdmin(user *User) bool {
exists, err := x.Where("team_id = ?", t.ID).
And("user_id = ?", user.ID).
And("admin = ?", true).
Get(&TeamMember{})
if err != nil {
Log.Error("Error occurred during CanUpdate for Team: %s", err)
return false
}
return exists
}
// CanRead returns true if the user has read access to the team
func (t *Team) CanRead(user *User) bool {
// Check if the user is in the team
exists, err := x.Where("team_id = ?", t.ID).
And("user_id = ?", user.ID).
Get(&TeamMember{})
if err != nil {
Log.Error("Error occurred during CanUpdate for Team: %s", err)
return false
}
return exists
}

View File

@ -1,4 +1,4 @@
package models
package config
import (
"crypto/rand"

View File

@ -1,4 +1,4 @@
package models
package errs
import (
"fmt"

12
pkg/list/init.go Normal file
View File

@ -0,0 +1,12 @@
package list
import (
"code.vikunja.io/api/pkg/models"
"github.com/go-xorm/xorm"
)
var x *xorm.Engine
func init() {
x = models.GetEngine()
}

View File

@ -1,4 +1,10 @@
package models
package list
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
)
// List represents a list of tasks
type List struct {
@ -8,14 +14,14 @@ type List struct {
OwnerID int64 `xorm:"int(11) INDEX" json:"-"`
NamespaceID int64 `xorm:"int(11) INDEX" json:"-" param:"namespace"`
Owner User `xorm:"-" json:"owner"`
Owner user.User `xorm:"-" json:"owner"`
Tasks []*ListTask `xorm:"-" json:"tasks"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
models.CRUDable `xorm:"-" json:"-"`
models.Rights `xorm:"-" json:"-"`
}
// GetListsByNamespaceID gets all lists in a namespace
@ -25,9 +31,9 @@ func GetListsByNamespaceID(nID int64) (lists []*List, err error) {
}
// ReadAll gets all lists a user has access to
func (l *List) ReadAll(user *User) (interface{}, error) {
func (l *List) ReadAll(u *user.User) (interface{}, error) {
lists := []*List{}
fullUser, err := GetUserByID(user.ID)
fullUser, err := user.GetUserByID(u.ID)
if err != nil {
return lists, err
}
@ -53,7 +59,7 @@ func (l *List) ReadAll(user *User) (interface{}, error) {
Find(&lists)
// Add more list details
addListDetails(lists)
AddListDetails(lists)
return lists, err
}
@ -72,14 +78,14 @@ func (l *List) ReadOne() (err error) {
}
// Get list owner
l.Owner, err = GetUserByID(l.OwnerID)
l.Owner, err = user.GetUserByID(l.OwnerID)
return
}
// GetSimpleByID gets a list with only the basic items, aka no tasks or user objects. Returns an error if the list does not exist.
func (l *List) GetSimpleByID() (err error) {
if l.ID < 1 {
return ErrListDoesNotExist{ID: l.ID}
return errs.ErrListDoesNotExist{ID: l.ID}
}
// We need to re-init our list object, because otherwise xorm creates a "where for every item in that list object,
@ -92,14 +98,14 @@ func (l *List) GetSimpleByID() (err error) {
}
if !exists {
return ErrListDoesNotExist{ID: l.ID}
return errs.ErrListDoesNotExist{ID: l.ID}
}
return
}
// Adds owner user objects and list tasks to all lists in the slice
func addListDetails(lists []*List) (err error) {
func AddListDetails(lists []*List) (err error) {
var listIDs []int64
var ownerIDs []int64
for _, l := range lists {
@ -108,14 +114,14 @@ func addListDetails(lists []*List) (err error) {
}
// Get all tasks
tasks := []*ListTask{}
err = x.In("list_id", listIDs).Find(&tasks)
ts := []*ListTask{}
err = x.In("list_id", listIDs).Find(&ts)
if err != nil {
return
}
// Get all list owners
owners := []*User{}
owners := []*user.User{}
err = x.In("id", ownerIDs).Find(&owners)
if err != nil {
return
@ -132,7 +138,7 @@ func addListDetails(lists []*List) (err error) {
}
// Tasks
for _, task := range tasks {
for _, task := range ts {
if task.ListID == list.ID {
lists[in].Tasks = append(lists[in].Tasks, task)
}

View File

@ -1,6 +1,8 @@
package models
package list
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"testing"
)
@ -10,7 +12,7 @@ func TestList_Create(t *testing.T) {
//assert.NoError(t, PrepareTestDatabase())
// Get our doer
doer, err := GetUserByID(1)
doer, err := user.GetUserByID(1)
assert.NoError(t, err)
// Dummy list for testing
@ -53,18 +55,18 @@ func TestList_Create(t *testing.T) {
// Try updating a nonexistant list
err = dummylist.Update()
assert.Error(t, err)
assert.True(t, IsErrListDoesNotExist(err))
assert.True(t, errs.IsErrListDoesNotExist(err))
// Delete a nonexistant list
err = dummylist.Delete()
assert.Error(t, err)
assert.True(t, IsErrListDoesNotExist(err))
assert.True(t, errs.IsErrListDoesNotExist(err))
// Check failing with no title
list2 := List{}
err = list2.Create(&doer)
assert.Error(t, err)
assert.True(t, IsErrListTitleCannotBeEmpty(err))
assert.True(t, errs.IsErrListTitleCannotBeEmpty(err))
// Check creation with a nonexistant namespace
list3 := List{
@ -75,11 +77,11 @@ func TestList_Create(t *testing.T) {
err = list3.Create(&doer)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
assert.True(t, errs.IsErrNamespaceDoesNotExist(err))
// Try creating with a nonexistant owner
nUser := &User{ID: 9482385}
nUser := &user.User{ID: 9482385}
err = dummylist.Create(nUser)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, errs.IsErrUserDoesNotExist(err))
}

View File

@ -1,16 +1,22 @@
package models
package list
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/user"
)
// CreateOrUpdateList updates a list or creates it if it doesn't exist
func CreateOrUpdateList(list *List) (err error) {
// Check we have at least a title
if list.Title == "" {
return ErrListTitleCannotBeEmpty{}
return errs.ErrListTitleCannotBeEmpty{}
}
// Check if the namespace exists
if list.NamespaceID != 0 {
_, err = GetNamespaceByID(list.NamespaceID)
_, err = namespace.GetNamespaceByID(list.NamespaceID)
if err != nil {
return err
}
@ -42,15 +48,15 @@ func (l *List) Update() (err error) {
}
// Create implements the create method of CRUDable
func (l *List) Create(doer *User) (err error) {
func (l *List) Create(doer *user.User) (err error) {
// Check rights
user, err := GetUserByID(doer.ID)
u, err := user.GetUserByID(doer.ID)
if err != nil {
return
}
l.OwnerID = user.ID
l.Owner.ID = user.ID
l.OwnerID = u.ID
l.Owner.ID = u.ID
l.ID = 0 // Otherwise only the first time a new list would be created
return CreateOrUpdateList(l)

View File

@ -1,4 +1,4 @@
package models
package list
// Delete implements the delete method of CRUDable
func (l *List) Delete() (err error) {

View File

@ -1,76 +1,83 @@
package models
package list
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/user"
)
// IsAdmin returns whether the user has admin rights on the list or not
func (l *List) IsAdmin(user *User) bool {
func (l *List) IsAdmin(u *user.User) bool {
// Owners are always admins
if l.Owner.ID == user.ID {
if l.Owner.ID == u.ID {
return true
}
// Check individual rights
if l.checkListUserRight(user, UserRightAdmin) {
if l.checkListUserRight(u, models.UserRightAdmin) {
return true
}
return l.checkListTeamRight(user, TeamRightAdmin)
return l.checkListTeamRight(u, models.TeamRightAdmin)
}
// CanWrite return whether the user can write on that list or not
func (l *List) CanWrite(user *User) bool {
func (l *List) CanWrite(user *user.User) bool {
// Admins always have write access
if l.IsAdmin(user) {
return true
}
// Check individual rights
if l.checkListUserRight(user, UserRightWrite) {
if l.checkListUserRight(user, models.UserRightWrite) {
return true
}
return l.checkListTeamRight(user, TeamRightWrite)
return l.checkListTeamRight(user, models.TeamRightWrite)
}
// CanRead checks if a user has read access to a list
func (l *List) CanRead(user *User) bool {
func (l *List) CanRead(user *user.User) bool {
// Admins always have read access
if l.IsAdmin(user) {
return true
}
// Check individual rights
if l.checkListUserRight(user, UserRightRead) {
if l.checkListUserRight(user, models.UserRightRead) {
return true
}
return l.checkListTeamRight(user, TeamRightRead)
return l.checkListTeamRight(user, models.TeamRightRead)
}
// CanDelete checks if the user can delete a list
func (l *List) CanDelete(doer *User) bool {
func (l *List) CanDelete(doer *user.User) bool {
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanDelete for List: %s", err)
log.Log.Error("Error occurred during CanDelete for List: %s", err)
return false
}
return l.IsAdmin(doer)
}
// CanUpdate checks if the user can update a list
func (l *List) CanUpdate(doer *User) bool {
func (l *List) CanUpdate(doer *user.User) bool {
if err := l.GetSimpleByID(); err != nil {
Log.Error("Error occurred during CanUpdate for List: %s", err)
log.Log.Error("Error occurred during CanUpdate for List: %s", err)
return false
}
return l.CanWrite(doer)
}
// CanCreate checks if the user can update a list
func (l *List) CanCreate(doer *User) bool {
func (l *List) CanCreate(doer *user.User) bool {
// A user can create a list if he has write access to the namespace
n, _ := GetNamespaceByID(l.NamespaceID)
n, _ := namespace.GetNamespaceByID(l.NamespaceID)
return n.CanWrite(doer)
}
func (l *List) checkListTeamRight(user *User, r TeamRight) bool {
func (l *List) checkListTeamRight(user *user.User, r models.TeamRight) bool {
exists, err := x.Select("l.*").
Table("list").
Alias("l").
@ -82,14 +89,14 @@ func (l *List) checkListTeamRight(user *User, r TeamRight) bool {
user.ID, r, user.ID, r, l.ID).
Exist(&List{})
if err != nil {
Log.Error("Error occurred during checkListTeamRight for List: %s", err)
log.Log.Error("Error occurred during checkListTeamRight for List: %s", err)
return false
}
return exists
}
func (l *List) checkListUserRight(user *User, r UserRight) bool {
func (l *List) checkListUserRight(user *user.User, r models.UserRight) bool {
exists, err := x.Select("l.*").
Table("list").
Alias("l").
@ -103,7 +110,7 @@ func (l *List) checkListUserRight(user *User, r UserRight) bool {
user.ID, r, user.ID, r, user.ID, l.ID).
Exist(&List{})
if err != nil {
Log.Error("Error occurred during checkListUserRight for List: %s", err)
log.Log.Error("Error occurred during checkListUserRight for List: %s", err)
return false
}

View File

@ -1,4 +1,10 @@
package models
package list
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
)
// ListTask represents an task in a todolist
type ListTask struct {
@ -13,10 +19,10 @@ type ListTask struct {
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CreatedBy User `xorm:"-" json:"createdBy"`
CreatedBy user.User `xorm:"-" json:"createdBy"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
models.CRUDable `xorm:"-" json:"-"`
models.Rights `xorm:"-" json:"-"`
}
// TableName returns the table name for listtasks
@ -52,16 +58,16 @@ func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
}
}
var users []User
var users []user.User
err = x.In("id", userIDs).Find(&users)
if err != nil {
return
}
for in, task := range tasks {
for _, user := range users {
if task.CreatedByID == user.ID {
tasks[in].CreatedBy = user
for _, u := range users {
if task.CreatedByID == u.ID {
tasks[in].CreatedBy = u
break
}
}
@ -76,7 +82,7 @@ func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
// GetListTaskByID returns all tasks a list has
func GetListTaskByID(listTaskID int64) (listTask ListTask, err error) {
if listTaskID < 1 {
return ListTask{}, ErrListTaskDoesNotExist{listTaskID}
return ListTask{}, errs.ErrListTaskDoesNotExist{listTaskID}
}
exists, err := x.ID(listTaskID).Get(&listTask)
@ -85,14 +91,14 @@ func GetListTaskByID(listTaskID int64) (listTask ListTask, err error) {
}
if !exists {
return ListTask{}, ErrListTaskDoesNotExist{listTaskID}
return ListTask{}, errs.ErrListTaskDoesNotExist{listTaskID}
}
user, err := GetUserByID(listTask.CreatedByID)
u, err := user.GetUserByID(listTask.CreatedByID)
if err != nil {
return
}
listTask.CreatedBy = user
listTask.CreatedBy = u
return
}

View File

@ -1,16 +1,18 @@
package models
package list
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
"github.com/imdario/mergo"
)
// Create is the implementation to create a list task
func (i *ListTask) Create(doer *User) (err error) {
func (i *ListTask) Create(doer *user.User) (err error) {
i.ID = 0
// Check if we have at least a text
if i.Text == "" {
return ErrListTaskCannotBeEmpty{}
return errs.ErrListTaskCannotBeEmpty{}
}
// Check if the list exists
@ -19,13 +21,13 @@ func (i *ListTask) Create(doer *User) (err error) {
return
}
user, err := GetUserByID(doer.ID)
u, err := user.GetUserByID(doer.ID)
if err != nil {
return err
}
i.CreatedByID = user.ID
i.CreatedBy = user
i.CreatedByID = u.ID
i.CreatedBy = u
_, err = x.Cols("text", "description", "done", "due_date_unix", "reminder_unix", "created_by_id", "list_id", "created", "updated").Insert(i)
return err
}

View File

@ -1,4 +1,4 @@
package models
package list
// Delete implements the delete method for listTask
func (i *ListTask) Delete() (err error) {

View File

@ -0,0 +1,44 @@
package list
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user"
)
// CanDelete checks if the user can delete an task
func (i *ListTask) CanDelete(doer *user.User) bool {
// Get the task
lI, err := GetListTaskByID(i.ID)
if err != nil {
log.Log.Error("Error occurred during CanDelete for ListTask: %s", err)
return false
}
// A user can delete an task if he has write acces to its list
l := &List{ID: lI.ListID}
l.ReadOne()
return l.CanWrite(doer)
}
// CanUpdate determines if a user has the right to update a list task
func (i *ListTask) CanUpdate(doer *user.User) bool {
// Get the task
lI, err := GetListTaskByID(i.ID)
if err != nil {
log.Log.Error("Error occurred during CanDelete for ListTask: %s", err)
return false
}
// A user can update an task if he has write acces to its list
l := &List{ID: lI.ListID}
l.ReadOne()
return l.CanWrite(doer)
}
// CanCreate determines if a user has the right to create a list task
func (i *ListTask) CanCreate(doer *user.User) bool {
// A user can create an task if he has write acces to its list
l := &List{ID: i.ListID}
l.ReadOne()
return l.CanWrite(doer)
}

View File

@ -1,6 +1,8 @@
package models
package list
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"testing"
)
@ -16,7 +18,7 @@ func TestListTask_Create(t *testing.T) {
}
// Add one point to a list
doer, err := GetUserByID(1)
doer, err := user.GetUserByID(1)
assert.NoError(t, err)
assert.True(t, listtask.CanCreate(&doer))
@ -44,31 +46,31 @@ func TestListTask_Create(t *testing.T) {
listtask.ID = 0
err = listtask.Delete()
assert.Error(t, err)
assert.True(t, IsErrListTaskDoesNotExist(err))
assert.True(t, errs.IsErrListTaskDoesNotExist(err))
// Try adding a list task with an empty text
listtask.Text = ""
err = listtask.Create(&doer)
assert.Error(t, err)
assert.True(t, IsErrListTaskCannotBeEmpty(err))
assert.True(t, errs.IsErrListTaskCannotBeEmpty(err))
// Try adding one to a nonexistant list
listtask.ListID = 99993939
listtask.Text = "Lorem Ipsum"
err = listtask.Create(&doer)
assert.Error(t, err)
assert.True(t, IsErrListDoesNotExist(err))
assert.True(t, errs.IsErrListDoesNotExist(err))
// Try updating a nonexistant task
listtask.ID = 94829352
err = listtask.Update()
assert.Error(t, err)
assert.True(t, IsErrListTaskDoesNotExist(err))
assert.True(t, errs.IsErrListTaskDoesNotExist(err))
// Try inserting an task with a nonexistant user
nUser := &User{ID: 9482385}
nUser := &user.User{ID: 9482385}
listtask.ListID = 1
err = listtask.Create(nUser)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, errs.IsErrUserDoesNotExist(err))
}

View File

@ -1,6 +1,8 @@
package models
package list
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
@ -16,18 +18,18 @@ func TestList_ReadAll(t *testing.T) {
assert.Equal(t, len(lists), 2)
// Get all lists our user has access to
user, err := GetUserByID(1)
u, err := user.GetUserByID(1)
assert.NoError(t, err)
lists2 := List{}
lists3, err := lists2.ReadAll(&user)
lists3, err := lists2.ReadAll(&u)
assert.NoError(t, err)
assert.Equal(t, reflect.TypeOf(lists3).Kind(), reflect.Slice)
s := reflect.ValueOf(lists3)
assert.Equal(t, s.Len(), 1)
// Try getting lists for a nonexistant user
_, err = lists2.ReadAll(&User{ID: 984234})
_, err = lists2.ReadAll(&user.User{ID: 984234})
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, errs.IsErrUserDoesNotExist(err))
}

View File

@ -1,4 +1,4 @@
package models
package log
import (
"github.com/op/go-logging"

View File

@ -1,8 +1,8 @@
package mail
import (
"code.vikunja.io/api/pkg/log"
"crypto/tls"
"fmt"
"github.com/spf13/viper"
"gopkg.in/gomail.v2"
"time"
@ -16,8 +16,7 @@ func StartMailDaemon() {
Queue = make(chan *gomail.Message, viper.GetInt("mailer.queuelength"))
if viper.GetString("mailer.host") == "" {
//models.Log.Warning("Mailer seems to be not configured! Please see the config docs for more details.")
fmt.Println("Mailer seems to be not configured! Please see the config docs for more details.")
log.Log.Warning("Mailer seems to be not configured! Please see the config docs for more details.")
return
}
@ -36,23 +35,21 @@ func StartMailDaemon() {
}
if !open {
if s, err = d.Dial(); err != nil {
// models.Log.Error("Error during connect to smtp server: %s", err)
fmt.Printf("Error during connect to smtp server: %s \n", err)
log.Log.Error("Error during connect to smtp server: %s", err)
}
open = true
}
if err := gomail.Send(s, m); err != nil {
// models.Log.Error("Error when sending mail: %s", err)
fmt.Printf("Error when sending mail: %s \n", err)
log.Log.Error("Error when sending mail: %s", err)
}
// Close the connection to the SMTP server if no email was sent in
// the last 30 seconds.
case <-time.After(viper.GetDuration("mailer.queuetimeout") * time.Second):
if open {
if err := s.Close(); err != nil {
fmt.Printf("Error closing the mail server connection: %s\n", err)
log.Log.Error("Error closing the mail server connection: %s\n", err)
}
fmt.Println("Closed connection to mailserver")
log.Log.Infof("Closed connection to mailserver")
open = false
}
}

View File

@ -2,7 +2,7 @@ package mail
import (
"bytes"
"code.vikunja.io/api/models/utils"
"code.vikunja.io/api/pkg/utils"
"github.com/labstack/gommon/log"
"github.com/spf13/viper"
"gopkg.in/gomail.v2"

View File

@ -2,9 +2,9 @@ package models
// CRUDable defines the crud methods
type CRUDable interface {
Create(*User) error
Create(*user.User) error
ReadOne() error
ReadAll(*User) (interface{}, error)
ReadAll(*user.User) (interface{}, error)
Update() error
Delete() error
}

View File

@ -40,6 +40,11 @@ func getEngine() (*xorm.Engine, error) {
return xorm.NewEngine("sqlite3", path)
}
// GetEngine returns the databse engine
func GetEngine() *xorm.Engine {
return x
}
func init() {
tables = append(tables,
new(User),

11
pkg/models/rights.go Normal file
View File

@ -0,0 +1,11 @@
package models
// Rights defines rights methods
type Rights interface {
IsAdmin(*user.User) bool
CanWrite(*user.User) bool
CanRead(*user.User) bool
CanDelete(*user.User) bool
CanUpdate(*user.User) bool
CanCreate(*user.User) bool
}

29
pkg/models/team_right.go Normal file
View File

@ -0,0 +1,29 @@
package models
import "code.vikunja.io/api/pkg/errs"
// TeamRight defines the rights teams can have for lists/namespaces
type TeamRight int
// define unknown team right
const (
TeamRightUnknown = -1
)
// Enumerate all the team rights
const (
// Can read lists in a Team
TeamRightRead TeamRight = iota
// Can write tasks in a Team like lists and todo tasks. Cannot create new lists.
TeamRightWrite
// Can manage a list/namespace, can do everything
TeamRightAdmin
)
func (r TeamRight) IsValid() error {
if r != TeamRightAdmin && r != TeamRightRead && r != TeamRightWrite {
return errs.ErrInvalidTeamRight{r}
}
return nil
}

View File

@ -1,7 +1,7 @@
package models
import (
"code.vikunja.io/api/models/mail"
"code.vikunja.io/api/pkg/mail"
"fmt"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"

29
pkg/models/user_right.go Normal file
View File

@ -0,0 +1,29 @@
package models
import "code.vikunja.io/api/pkg/errs"
// UserRight defines the rights users can have for lists/namespaces
type UserRight int
// define unknown user right
const (
UserRightUnknown = -1
)
// Enumerate all the user rights
const (
// Can read lists in a User
UserRightRead UserRight = iota
// Can write tasks in a User like lists and todo tasks. Cannot create new lists.
UserRightWrite
// Can manage a list/namespace, can do everything
UserRightAdmin
)
func (r UserRight) IsValid() error {
if r != UserRightAdmin && r != UserRightRead && r != UserRightWrite {
return errs.ErrInvalidUserRight{r}
}
return nil
}

12
pkg/namespace/init.go Normal file
View File

@ -0,0 +1,12 @@
package namespace
import (
"code.vikunja.io/api/pkg/models"
"github.com/go-xorm/xorm"
)
var x *xorm.Engine
func init() {
x = models.GetEngine()
}

View File

@ -1,4 +1,11 @@
package models
package namespace
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
)
// Namespace holds informations about a namespace
type Namespace struct {
@ -7,13 +14,13 @@ type Namespace struct {
Description string `xorm:"varchar(1000)" json:"description"`
OwnerID int64 `xorm:"int(11) not null INDEX" json:"-"`
Owner User `xorm:"-" json:"owner"`
Owner user.User `xorm:"-" json:"owner"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
CRUDable `xorm:"-" json:"-"`
Rights `xorm:"-" json:"-"`
models.CRUDable `xorm:"-" json:"-"`
models.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
@ -24,7 +31,7 @@ func (Namespace) TableName() string {
// GetNamespaceByID returns a namespace object by its ID
func GetNamespaceByID(id int64) (namespace Namespace, err error) {
if id < 1 {
return namespace, ErrNamespaceDoesNotExist{ID: id}
return namespace, errs.ErrNamespaceDoesNotExist{ID: id}
}
namespace.ID = id
@ -34,11 +41,11 @@ func GetNamespaceByID(id int64) (namespace Namespace, err error) {
}
if !exists {
return namespace, ErrNamespaceDoesNotExist{ID: id}
return namespace, errs.ErrNamespaceDoesNotExist{ID: id}
}
// Get the namespace Owner
namespace.Owner, err = GetUserByID(namespace.OwnerID)
namespace.Owner, err = user.GetUserByID(namespace.OwnerID)
if err != nil {
return namespace, err
}
@ -53,11 +60,11 @@ func (n *Namespace) ReadOne() (err error) {
}
// ReadAll gets all namespaces a user has access to
func (n *Namespace) ReadAll(doer *User) (interface{}, error) {
func (n *Namespace) ReadAll(doer *user.User) (interface{}, error) {
type namespaceWithLists struct {
Namespace `xorm:"extends"`
Lists []*List `xorm:"-" json:"lists"`
Lists []*list.List `xorm:"-" json:"lists"`
}
all := []*namespaceWithLists{}
@ -78,7 +85,7 @@ func (n *Namespace) ReadAll(doer *User) (interface{}, error) {
}
// Get all users
users := []*User{}
users := []*user.User{}
err = x.Select("users.*").
Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
@ -100,7 +107,7 @@ func (n *Namespace) ReadAll(doer *User) (interface{}, error) {
}
// Get all lists
lists := []*List{}
lists := []*list.List{}
err = x.Table(&lists).
In("namespace_id", namespaceids).
Find(&lists)
@ -109,7 +116,7 @@ func (n *Namespace) ReadAll(doer *User) (interface{}, error) {
}
// More details for the lists
addListDetails(lists)
list.AddListDetails(lists)
// Put objects in our namespace list
for i, n := range all {

View File

@ -1,15 +1,20 @@
package models
package namespace
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
)
// Create implements the creation method via the interface
func (n *Namespace) Create(doer *User) (err error) {
func (n *Namespace) Create(doer *user.User) (err error) {
// Check if we have at least a name
if n.Name == "" {
return ErrNamespaceNameCannotBeEmpty{NamespaceID: 0, UserID: doer.ID}
return errs.ErrNamespaceNameCannotBeEmpty{NamespaceID: 0, UserID: doer.ID}
}
n.ID = 0 // This would otherwise prevent the creation of new lists after one was created
// Check if the User exists
n.Owner, err = GetUserByID(doer.ID)
n.Owner, err = user.GetUserByID(doer.ID)
if err != nil {
return
}

View File

@ -1,4 +1,8 @@
package models
package namespace
import (
"code.vikunja.io/api/pkg/list"
)
// Delete deletes a namespace
func (n *Namespace) Delete() (err error) {
@ -16,23 +20,23 @@ func (n *Namespace) Delete() (err error) {
}
// Delete all lists with their tasks
lists, err := GetListsByNamespaceID(n.ID)
lists, err := list.GetListsByNamespaceID(n.ID)
var listIDs []int64
// We need to do that for here because we need the list ids to delete two times:
// 1) to delete the lists itself
// 2) to delete the list tasks
for _, list := range lists {
listIDs = append(listIDs, list.ID)
for _, l := range lists {
listIDs = append(listIDs, l.ID)
}
// Delete tasks
_, err = x.In("list_id", listIDs).Delete(&ListTask{})
_, err = x.In("list_id", listIDs).Delete(&list.ListTask{})
if err != nil {
return
}
// Delete the lists
_, err = x.In("id", listIDs).Delete(&List{})
_, err = x.In("id", listIDs).Delete(&list.List{})
if err != nil {
return
}

View File

@ -1,106 +1,112 @@
package models
package namespace
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
)
// IsAdmin returns true or false if the user is admin on that namespace or not
func (n *Namespace) IsAdmin(user *User) bool {
func (n *Namespace) IsAdmin(u *user.User) bool {
// Owners always have admin rights
if user.ID == n.Owner.ID {
if u.ID == n.Owner.ID {
return true
}
// Check user rights
if n.checkUserRights(user, UserRightAdmin) {
if n.checkUserRights(u, models.UserRightAdmin) {
return true
}
// Check if that user is in a team which has admin rights to that namespace
return n.checkTeamRights(user, TeamRightAdmin)
return n.checkTeamRights(u, models.TeamRightAdmin)
}
// CanWrite checks if a user has write access to a namespace
func (n *Namespace) CanWrite(user *User) bool {
func (n *Namespace) CanWrite(u *user.User) bool {
// Admins always have write access
if n.IsAdmin(user) {
if n.IsAdmin(u) {
return true
}
// Check user rights
if n.checkUserRights(user, UserRightWrite) {
if n.checkUserRights(u, models.UserRightWrite) {
return true
}
// Check if that user is in a team which has write rights to that namespace
return n.checkTeamRights(user, TeamRightWrite)
return n.checkTeamRights(u, models.TeamRightWrite)
}
// CanRead checks if a user has read access to that namespace
func (n *Namespace) CanRead(user *User) bool {
func (n *Namespace) CanRead(u *user.User) bool {
// Admins always have read access
if n.IsAdmin(user) {
if n.IsAdmin(u) {
return true
}
// Check user rights
if n.checkUserRights(user, UserRightRead) {
if n.checkUserRights(u, models.UserRightRead) {
return true
}
// Check if the user is in a team which has access to the namespace
return n.checkTeamRights(user, TeamRightRead)
return n.checkTeamRights(u, models.TeamRightRead)
}
// CanUpdate checks if the user can update the namespace
func (n *Namespace) CanUpdate(user *User) bool {
func (n *Namespace) CanUpdate(u *user.User) bool {
nn, err := GetNamespaceByID(n.ID)
if err != nil {
Log.Error("Error occurred during CanUpdate for Namespace: %s", err)
log.Log.Error("Error occurred during CanUpdate for Namespace: %s", err)
return false
}
return nn.IsAdmin(user)
return nn.IsAdmin(u)
}
// CanDelete checks if the user can delete a namespace
func (n *Namespace) CanDelete(user *User) bool {
func (n *Namespace) CanDelete(u *user.User) bool {
nn, err := GetNamespaceByID(n.ID)
if err != nil {
Log.Error("Error occurred during CanDelete for Namespace: %s", err)
log.Log.Error("Error occurred during CanDelete for Namespace: %s", err)
return false
}
return nn.IsAdmin(user)
return nn.IsAdmin(u)
}
// CanCreate checks if the user can create a new namespace
func (n *Namespace) CanCreate(user *User) bool {
func (n *Namespace) CanCreate(u *user.User) bool {
// This is currently a dummy function, later on we could imagine global limits etc.
return true
}
func (n *Namespace) checkTeamRights(user *User, r TeamRight) bool {
func (n *Namespace) checkTeamRights(u *user.User, r models.TeamRight) bool {
exists, err := x.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").
Where("namespaces.id = ? "+
"AND (team_members.user_id = ? AND team_namespaces.right = ?) "+
"OR namespaces.owner_id = ? ", n.ID, user.ID, r, user.ID).
"OR namespaces.owner_id = ? ", n.ID, u.ID, r, u.ID).
Get(&Namespace{})
if err != nil {
Log.Error("Error occurred during checkTeamRights for Namespace: %s, TeamRight: %d", err, r)
log.Log.Error("Error occurred during checkTeamRights for Namespace: %s, TeamRight: %d", err, r)
return false
}
return exists
}
func (n *Namespace) checkUserRights(user *User, r UserRight) bool {
func (n *Namespace) checkUserRights(u *user.User, r models.UserRight) bool {
exists, err := x.Select("namespaces.*").
Table("namespaces").
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
Where("namespaces.id = ? AND ("+
"namespaces.owner_id = ? "+
"OR (users_namespace.user_id = ? AND users_namespace.right = ?))", n.ID, user.ID, user.ID, r).
"OR (users_namespace.user_id = ? AND users_namespace.right = ?))", n.ID, u.ID, u.ID, r).
Get(&Namespace{})
if err != nil {
Log.Error("Error occurred during checkUserRights for Namespace: %s, UserRight: %d", err, r)
log.Log.Error("Error occurred during checkUserRights for Namespace: %s, UserRight: %d", err, r)
return false
}

View File

@ -1,6 +1,8 @@
package models
package namespace
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
@ -17,7 +19,7 @@ func TestNamespace_Create(t *testing.T) {
}
// Doer
doer, err := GetUserByID(1)
doer, err := user.GetUserByID(1)
assert.NoError(t, err)
// Try creating it
@ -36,14 +38,14 @@ func TestNamespace_Create(t *testing.T) {
n2 := Namespace{}
err = n2.Create(&doer)
assert.Error(t, err)
assert.True(t, IsErrNamespaceNameCannotBeEmpty(err))
assert.True(t, errs.IsErrNamespaceNameCannotBeEmpty(err))
// Try inserting one with a nonexistant user
nUser := &User{ID: 9482385}
nUser := &user.User{ID: 9482385}
dnsp2 := dummynamespace
err = dnsp2.Create(nUser)
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, errs.IsErrUserDoesNotExist(err))
// Update it
assert.True(t, dummynamespace.CanUpdate(&doer))
@ -55,19 +57,19 @@ func TestNamespace_Create(t *testing.T) {
dummynamespace.Owner.ID = 94829838572
err = dummynamespace.Update()
assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err))
assert.True(t, errs.IsErrUserDoesNotExist(err))
// Try updating without a name
dummynamespace.Name = ""
err = dummynamespace.Update()
assert.Error(t, err)
assert.True(t, IsErrNamespaceNameCannotBeEmpty(err))
assert.True(t, errs.IsErrNamespaceNameCannotBeEmpty(err))
// Try updating a nonexistant one
n := Namespace{ID: 284729, Name: "Lorem"}
err = n.Update()
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
assert.True(t, errs.IsErrNamespaceDoesNotExist(err))
// Delete it
assert.True(t, dummynamespace.CanDelete(&doer))
@ -77,12 +79,12 @@ func TestNamespace_Create(t *testing.T) {
// Try deleting a nonexistant one
err = n.Delete()
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
assert.True(t, errs.IsErrNamespaceDoesNotExist(err))
// Check if it was successfully deleted
err = dummynamespace.ReadOne()
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
assert.True(t, errs.IsErrNamespaceDoesNotExist(err))
// Get all namespaces of a user
nsps, err := n.ReadAll(&doer)

View File

@ -1,10 +1,15 @@
package models
package namespace
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
)
// Update implements the update method via the interface
func (n *Namespace) Update() (err error) {
// Check if we have at least a name
if n.Name == "" {
return ErrNamespaceNameCannotBeEmpty{NamespaceID: n.ID}
return errs.ErrNamespaceNameCannotBeEmpty{NamespaceID: n.ID}
}
// Check if the namespace exists
@ -16,7 +21,7 @@ func (n *Namespace) Update() (err error) {
// Check if the (new) owner exists
n.OwnerID = n.Owner.ID
if currentNamespace.OwnerID != n.OwnerID {
n.Owner, err = GetUserByID(n.OwnerID)
n.Owner, err = user.GetUserByID(n.OwnerID)
if err != nil {
return
}

View File

@ -1,7 +1,7 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
"strconv"
@ -33,10 +33,10 @@ func GetListsByNamespaceID(c echo.Context) error {
// Get our namespace
namespace, err := getNamespace(c)
if err != nil {
if models.IsErrNamespaceDoesNotExist(err) {
if models.errs.IsErrNamespaceDoesNotExist(err) {
return c.JSON(http.StatusNotFound, models.Message{"Namespace not found."})
}
if models.IsErrUserDoesNotHaveAccessToNamespace(err) {
if models.errs.IsErrUserDoesNotHaveAccessToNamespace(err) {
return c.JSON(http.StatusForbidden, models.Message{"You don't have access to this namespace."})
}
return c.JSON(http.StatusInternalServerError, models.Message{"An error occurred."})
@ -45,7 +45,7 @@ func GetListsByNamespaceID(c echo.Context) error {
// Get the lists
lists, err := models.GetListsByNamespaceID(namespace.ID)
if err != nil {
if models.IsErrNamespaceDoesNotExist(err) {
if models.errs.IsErrNamespaceDoesNotExist(err) {
return c.JSON(http.StatusNotFound, models.Message{"Namespace not found."})
}
return c.JSON(http.StatusInternalServerError, models.Message{"An error occurred."})

View File

@ -1,7 +1,7 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/models"
"crypto/md5"
"encoding/hex"
"github.com/dgrijalva/jwt-go"

View File

@ -0,0 +1,58 @@
package swagger
import (
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/sharing"
"code.vikunja.io/api/pkg/team"
"code.vikunja.io/api/pkg/user"
)
// not actually a response, just a hack to get go-swagger to include definitions
// of the various XYZOption structs
// parameterBodies
// swagger:response parameterBodies
type swaggerParameterBodies struct {
// in:body
UserLogin user.UserLogin
// in:body
APIUserPassword user.APIUserPassword
// in:body
List list.List
// in:body
ListTask list.ListTask
// in:body
Namespace namespace.Namespace
// in:body
Team team.Team
// in:body
TeamMember team.TeamMember
// in:body
TeamList sharing.TeamList
// in:body
TeamNamespace sharing.TeamNamespace
// in:body
ListUser sharing.ListUser
// in:body
NamespaceUser sharing.NamespaceUser
// in:body
PasswordReset user.PasswordReset
// in:body
PasswordTokenRequest user.PasswordTokenRequest
// in:body
EmailConfirm user.EmailConfirm
}

View File

@ -1,6 +1,13 @@
package swagger
import "code.vikunja.io/api/models"
import (
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/sharing"
"code.vikunja.io/api/pkg/team"
"code.vikunja.io/api/pkg/user"
)
// Message
// swagger:response Message
@ -17,7 +24,7 @@ type swaggerResponseMessage struct {
// swagger:response User
type swaggerResponseUser struct {
// in:body
Body models.User `json:"body"`
Body user.User `json:"body"`
}
// Token
@ -41,14 +48,14 @@ type swaggerResponseToken struct {
// swagger:response List
type swaggerResponseLIst struct {
// in:body
Body models.List `json:"body"`
Body list.List `json:"body"`
}
// ListTask
// swagger:response ListTask
type swaggerResponseLIstTask struct {
// in:body
Body models.ListTask `json:"body"`
Body list.ListTask `json:"body"`
}
// ================
@ -59,7 +66,7 @@ type swaggerResponseLIstTask struct {
// swagger:response Namespace
type swaggerResponseNamespace struct {
// in:body
Body models.Namespace `json:"body"`
Body namespace.Namespace `json:"body"`
}
// ================
@ -70,40 +77,40 @@ type swaggerResponseNamespace struct {
// swagger:response Team
type swaggerResponseTeam struct {
// in:body
Body models.Team `json:"body"`
Body team.Team `json:"body"`
}
// TeamMember
// swagger:response TeamMember
type swaggerResponseTeamMember struct {
// in:body
Body models.TeamMember `json:"body"`
Body team.TeamMember `json:"body"`
}
// TeamList
// swagger:response TeamList
type swaggerResponseTeamList struct {
// in:body
Body models.TeamList `json:"body"`
Body sharing.TeamList `json:"body"`
}
// TeamNamespace
// swagger:response TeamNamespace
type swaggerResponseTeamNamespace struct {
// in:body
Body models.TeamNamespace `json:"body"`
Body sharing.TeamNamespace `json:"body"`
}
// UserList
// swagger:response UserList
type swaggerResponseUserList struct {
// in:body
Body models.ListUser `json:"body"`
Body sharing.ListUser `json:"body"`
}
// UserNamespace
// swagger:response UserNamespace
type swaggerResponseUserNamespace struct {
// in:body
Body models.NamespaceUser `json:"body"`
Body sharing.NamespaceUser `json:"body"`
}

View File

@ -1,7 +1,7 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/models"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"

View File

@ -1,8 +1,8 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/routes/crud"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"github.com/labstack/echo"
"net/http"
"strconv"
@ -62,7 +62,7 @@ func userAddOrUpdate(c echo.Context) error {
var exists = true
_, err := models.GetUserByID(datUser.ID)
if err != nil {
if models.IsErrUserDoesNotExist(err) {
if models.errs.IsErrUserDoesNotExist(err) {
exists = false
} else {
return c.JSON(http.StatusInternalServerError, models.Message{"Could not check if the user exists."})

View File

@ -1,8 +1,8 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/routes/crud"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"github.com/labstack/echo"
"net/http"
)

View File

@ -1,8 +1,8 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/routes/crud"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"github.com/labstack/echo"
"net/http"
"strconv"
@ -26,7 +26,7 @@ func UserDelete(c echo.Context) error {
_, err = models.GetUserByID(userID)
if err != nil {
if models.IsErrUserDoesNotExist(err) {
if models.errs.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."})

View File

@ -1,8 +1,8 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/routes/crud"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"github.com/labstack/echo"
"net/http"
)

View File

@ -1,8 +1,8 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/routes/crud"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"github.com/labstack/echo"
"net/http"
)

View File

@ -1,8 +1,8 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/routes/crud"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"github.com/labstack/echo"
"net/http"
)

View File

@ -1,8 +1,8 @@
package v1
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/routes/crud"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/routes/crud"
"github.com/labstack/echo"
"net/http"
)

View File

@ -1,7 +1,8 @@
package crud
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo"
"net/http"
)
@ -17,14 +18,14 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error {
}
// Get the user to pass for later checks
currentUser, err := models.GetCurrentUser(ctx)
currentUser, err := user.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}
// Check rights
if !currentStruct.CanCreate(&currentUser) {
models.Log.Noticef("%s [ID: %d] tried to create while not having the rights for it", currentUser.Username, currentUser.ID)
log.Log.Noticef("%s [ID: %d] tried to create while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden)
}

View File

@ -1,7 +1,9 @@
package crud
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo"
"net/http"
)
@ -18,12 +20,12 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
}
// Check if the user has the right to delete
user, err := models.GetCurrentUser(ctx)
currentUser, err := user.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError)
}
if !currentStruct.CanDelete(&user) {
models.Log.Noticef("%s [ID: %d] tried to delete while not having the rights for it", user.Username, user.ID)
if !currentStruct.CanDelete(&currentUser) {
log.Log.Noticef("%s [ID: %d] tried to delete while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden)
}

View File

@ -1,7 +1,9 @@
package crud
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"github.com/labstack/echo"
"net/http"
)
@ -20,10 +22,10 @@ type CObject interface {
// HandleHTTPError does what it says
func HandleHTTPError(err error) *echo.HTTPError {
if a, has := err.(models.HTTPErrorProcessor); has {
if a, has := err.(errs.HTTPErrorProcessor); has {
errDetails := a.HTTPError()
return echo.NewHTTPError(errDetails.HTTPCode, errDetails)
}
models.Log.Error(err.Error())
log.Log.Error(err.Error())
return echo.NewHTTPError(http.StatusInternalServerError)
}

View File

@ -1,7 +1,7 @@
package crud
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo"
"net/http"
)
@ -11,7 +11,7 @@ func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
// Get our model
currentStruct := c.EmptyStruct()
currentUser, err := models.GetCurrentUser(ctx)
currentUser, err := user.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}

View File

@ -1,7 +1,8 @@
package crud
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo"
"net/http"
)
@ -24,12 +25,12 @@ func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
// Check rights
// We can only check the rights on a full object, which is why we need to check it afterwards
currentUser, err := models.GetCurrentUser(ctx)
currentUser, err := user.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}
if !currentStruct.CanRead(&currentUser) {
models.Log.Noticef("%s [ID: %d] tried to read while not having the rights for it", currentUser.Username, currentUser.ID)
log.Log.Noticef("%s [ID: %d] tried to read while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this")
}

View File

@ -1,7 +1,8 @@
package crud
import (
"code.vikunja.io/api/models"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user"
"github.com/labstack/echo"
"net/http"
)
@ -18,12 +19,12 @@ func (c *WebHandler) UpdateWeb(ctx echo.Context) error {
}
// Check if the user has the right to do that
currentUser, err := models.GetCurrentUser(ctx)
currentUser, err := user.GetCurrentUser(ctx)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
}
if !currentStruct.CanUpdate(&currentUser) {
models.Log.Noticef("%s [ID: %d] tried to update while not having the rights for it", currentUser.Username, currentUser.ID)
log.Log.Noticef("%s [ID: %d] tried to update while not having the rights for it", currentUser.Username, currentUser.ID)
return echo.NewHTTPError(http.StatusForbidden)
}

View File

@ -27,13 +27,16 @@
package routes
import (
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/sharing"
"code.vikunja.io/api/pkg/team"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"code.vikunja.io/api/models"
apiv1 "code.vikunja.io/api/routes/api/v1"
_ "code.vikunja.io/api/routes/api/v1/swagger" // for docs generation
"code.vikunja.io/api/routes/crud"
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
_ "code.vikunja.io/api/pkg/routes/api/v1/swagger" // for docs generation
"code.vikunja.io/api/pkg/routes/crud"
"github.com/spf13/viper"
)
@ -81,7 +84,7 @@ func RegisterRoutes(e *echo.Echo) {
listHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.List{}
return &list.List{}
},
}
a.GET("/lists", listHandler.ReadAllWeb)
@ -92,7 +95,7 @@ func RegisterRoutes(e *echo.Echo) {
taskHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.ListTask{}
return &list.ListTask{}
},
}
a.PUT("/lists/:list", taskHandler.CreateWeb)
@ -101,7 +104,7 @@ func RegisterRoutes(e *echo.Echo) {
listTeamHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.TeamList{}
return &sharing.TeamList{}
},
}
a.GET("/lists/:list/teams", listTeamHandler.ReadAllWeb)
@ -111,7 +114,7 @@ func RegisterRoutes(e *echo.Echo) {
listUserHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.ListUser{}
return &sharing.ListUser{}
},
}
a.GET("/lists/:list/users", listUserHandler.ReadAllWeb)
@ -121,7 +124,7 @@ func RegisterRoutes(e *echo.Echo) {
namespaceHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.Namespace{}
return &namespace.Namespace{}
},
}
a.GET("/namespaces", namespaceHandler.ReadAllWeb)
@ -133,7 +136,7 @@ func RegisterRoutes(e *echo.Echo) {
namespaceTeamHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.TeamNamespace{}
return &sharing.TeamNamespace{}
},
}
a.GET("/namespaces/:namespace/teams", namespaceTeamHandler.ReadAllWeb)
@ -143,7 +146,7 @@ func RegisterRoutes(e *echo.Echo) {
namespaceUserHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.NamespaceUser{}
return &sharing.NamespaceUser{}
},
}
a.GET("/namespaces/:namespace/users", namespaceUserHandler.ReadAllWeb)
@ -153,7 +156,7 @@ func RegisterRoutes(e *echo.Echo) {
teamHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.Team{}
return &team.Team{}
},
}
a.GET("/teams", teamHandler.ReadAllWeb)
@ -164,7 +167,7 @@ func RegisterRoutes(e *echo.Echo) {
teamMemberHandler := &crud.WebHandler{
EmptyStruct: func() crud.CObject {
return &models.TeamMember{}
return &team.TeamMember{}
},
}
a.PUT("/teams/:team/members", teamMemberHandler.CreateWeb)

12
pkg/sharing/init.go Normal file
View File

@ -0,0 +1,12 @@
package sharing
import (
"code.vikunja.io/api/pkg/models"
"github.com/go-xorm/xorm"
)
var x *xorm.Engine
func init() {
x = models.GetEngine()
}

30
pkg/sharing/list_users.go Normal file
View File

@ -0,0 +1,30 @@
package sharing
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
)
// ListUser represents a list <-> user relation
type ListUser struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
UserID int64 `xorm:"int(11) not null INDEX" json:"user_id" param:"user"`
ListID int64 `xorm:"int(11) not null INDEX" json:"list_id" param:"list"`
Right models.UserRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
models.CRUDable `xorm:"-" json:"-"`
models.Rights `xorm:"-" json:"-"`
}
// TableName is the table name for ListUser
func (ListUser) TableName() string {
return "users_list"
}
type userWithRight struct {
user.User `xorm:"extends"`
Right models.UserRight `json:"right"`
}

View File

@ -1,28 +1,34 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/user"
)
// Create creates a new list <-> user relation
func (ul *ListUser) Create(user *User) (err error) {
func (ul *ListUser) Create(u *user.User) (err error) {
// Check if the right is valid
if err := ul.Right.isValid(); err != nil {
if err := ul.Right.IsValid(); err != nil {
return err
}
// Check if the list exists
l := &List{ID: ul.ListID}
l := &list.List{ID: ul.ListID}
if err = l.GetSimpleByID(); err != nil {
return
}
// Check if the user exists
if _, err = GetUserByID(ul.UserID); err != nil {
if _, err = user.GetUserByID(ul.UserID); err != nil {
return err
}
// Check if the user already has access or is owner of that list
// We explicitly DONT check for teams here
if l.OwnerID == ul.UserID {
return ErrUserAlreadyHasAccess{UserID: ul.UserID, ListID: ul.ListID}
return errs.ErrUserAlreadyHasAccess{UserID: ul.UserID, ListID: ul.ListID}
}
exist, err := x.Where("list_id = ? AND user_id = ?", ul.ListID, ul.UserID).Get(&ListUser{})
@ -30,7 +36,7 @@ func (ul *ListUser) Create(user *User) (err error) {
return
}
if exist {
return ErrUserAlreadyHasAccess{UserID: ul.UserID, ListID: ul.ListID}
return errs.ErrUserAlreadyHasAccess{UserID: ul.UserID, ListID: ul.ListID}
}
// Insert user <-> list relation

View File

@ -1,10 +1,15 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
)
// Delete deletes a list <-> user relation
func (lu *ListUser) Delete() (err error) {
// Check if the user exists
_, err = GetUserByID(lu.UserID)
_, err = user.GetUserByID(lu.UserID)
if err != nil {
return
}
@ -16,7 +21,7 @@ func (lu *ListUser) Delete() (err error) {
return
}
if !has {
return ErrUserDoesNotHaveAccessToList{ListID: lu.ListID, UserID: lu.UserID}
return errs.ErrUserDoesNotHaveAccessToList{ListID: lu.ListID, UserID: lu.UserID}
}
_, err = x.Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).

View File

@ -1,14 +1,20 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/user"
)
// ReadAll gets all users who have access to a list
func (ul *ListUser) ReadAll(user *User) (interface{}, error) {
func (ul *ListUser) ReadAll(u *user.User) (interface{}, error) {
// Check if the user has access to the list
l := &List{ID: ul.ListID}
l := &list.List{ID: ul.ListID}
if err := l.GetSimpleByID(); err != nil {
return nil, err
}
if !l.CanRead(user) {
return nil, ErrNeedToHaveListReadAccess{}
if !l.CanRead(u) {
return nil, errs.ErrNeedToHaveListReadAccess{}
}
// Get all users

View File

@ -0,0 +1,40 @@
package sharing
import (
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user"
)
// CanCreate checks if the user can create a new user <-> list relation
func (lu *ListUser) CanCreate(doer *user.User) bool {
// Get the list and check if the user has write access on it
l := list.List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanCreate for ListUser: %s", err)
return false
}
return l.CanWrite(doer)
}
// CanDelete checks if the user can delete a user <-> list relation
func (lu *ListUser) CanDelete(doer *user.User) bool {
// Get the list and check if the user has write access on it
l := list.List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanDelete for ListUser: %s", err)
return false
}
return l.CanWrite(doer)
}
// CanUpdate checks if the user can update a user <-> list relation
func (lu *ListUser) CanUpdate(doer *user.User) bool {
// Get the list and check if the user has write access on it
l := list.List{ID: lu.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanUpdate for ListUser: %s", err)
return false
}
return l.CanWrite(doer)
}

View File

@ -1,10 +1,10 @@
package models
package sharing
// Update updates a user <-> list relation
func (lu *ListUser) Update() (err error) {
// Check if the right is valid
if err := lu.Right.isValid(); err != nil {
if err := lu.Right.IsValid(); err != nil {
return err
}

View File

@ -0,0 +1,22 @@
package sharing
import "code.vikunja.io/api/pkg/models"
// NamespaceUser represents a namespace <-> user relation
type NamespaceUser struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
UserID int64 `xorm:"int(11) not null INDEX" json:"user_id" param:"user"`
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"namespace_id" param:"namespace"`
Right models.UserRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
models.CRUDable `xorm:"-" json:"-"`
models.Rights `xorm:"-" json:"-"`
}
// TableName is the table name for NamespaceUser
func (NamespaceUser) TableName() string {
return "users_namespace"
}

View File

@ -1,31 +1,37 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/user"
)
// Create creates a new namespace <-> user relation
func (un *NamespaceUser) Create(user *User) (err error) {
func (un *NamespaceUser) Create(u *user.User) (err error) {
// Reset the id
un.ID = 0
// Check if the right is valid
if err := un.Right.isValid(); err != nil {
if err := un.Right.IsValid(); err != nil {
return err
}
// Check if the namespace exists
l, err := GetNamespaceByID(un.NamespaceID)
l, err := namespace.GetNamespaceByID(un.NamespaceID)
if err != nil {
return
}
// Check if the user exists
if _, err = GetUserByID(un.UserID); err != nil {
if _, err = user.GetUserByID(un.UserID); err != nil {
return err
}
// Check if the user already has access or is owner of that namespace
// We explicitly DO NOT check for teams here
if l.OwnerID == un.UserID {
return ErrUserAlreadyHasNamespaceAccess{UserID: un.UserID, NamespaceID: un.NamespaceID}
return errs.ErrUserAlreadyHasNamespaceAccess{UserID: un.UserID, NamespaceID: un.NamespaceID}
}
exist, err := x.Where("namespace_id = ? AND user_id = ?", un.NamespaceID, un.UserID).Get(&NamespaceUser{})
@ -33,7 +39,7 @@ func (un *NamespaceUser) Create(user *User) (err error) {
return
}
if exist {
return ErrUserAlreadyHasNamespaceAccess{UserID: un.UserID, NamespaceID: un.NamespaceID}
return errs.ErrUserAlreadyHasNamespaceAccess{UserID: un.UserID, NamespaceID: un.NamespaceID}
}
// Insert user <-> namespace relation

View File

@ -1,10 +1,15 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/user"
)
// Delete deletes a namespace <-> user relation
func (nu *NamespaceUser) Delete() (err error) {
// Check if the user exists
_, err = GetUserByID(nu.UserID)
_, err = user.GetUserByID(nu.UserID)
if err != nil {
return
}
@ -16,7 +21,7 @@ func (nu *NamespaceUser) Delete() (err error) {
return
}
if !has {
return ErrUserDoesNotHaveAccessToNamespace{NamespaceID: nu.NamespaceID, UserID: nu.UserID}
return errs.ErrUserDoesNotHaveAccessToNamespace{NamespaceID: nu.NamespaceID, UserID: nu.UserID}
}
_, err = x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).

View File

@ -1,14 +1,20 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/user"
)
// ReadAll gets all users who have access to a namespace
func (un *NamespaceUser) ReadAll(user *User) (interface{}, error) {
func (un *NamespaceUser) ReadAll(u *user.User) (interface{}, error) {
// Check if the user has access to the namespace
l, err := GetNamespaceByID(un.NamespaceID)
l, err := namespace.GetNamespaceByID(un.NamespaceID)
if err != nil {
return nil, err
}
if !l.CanRead(user) {
return nil, ErrNeedToHaveNamespaceReadAccess{}
if !l.CanRead(u) {
return nil, errs.ErrNeedToHaveNamespaceReadAccess{}
}
// Get all users

View File

@ -0,0 +1,40 @@
package sharing
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/user"
)
// CanCreate checks if the user can create a new user <-> namespace relation
func (nu *NamespaceUser) CanCreate(doer *user.User) bool {
// Get the namespace and check if the user has write access on it
n, err := namespace.GetNamespaceByID(nu.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanCreate for NamespaceUser: %s", err)
return false
}
return n.CanWrite(doer)
}
// CanDelete checks if the user can delete a user <-> namespace relation
func (nu *NamespaceUser) CanDelete(doer *user.User) bool {
// Get the namespace and check if the user has write access on it
n, err := namespace.GetNamespaceByID(nu.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanDelete for NamespaceUser: %s", err)
return false
}
return n.CanWrite(doer)
}
// CanUpdate checks if the user can update a user <-> namespace relation
func (nu *NamespaceUser) CanUpdate(doer *user.User) bool {
// Get the namespace and check if the user has write access on it
n, err := namespace.GetNamespaceByID(nu.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanUpdate for NamespaceUser: %s", err)
return false
}
return n.CanWrite(doer)
}

View File

@ -1,10 +1,10 @@
package models
package sharing
// Update updates a user <-> namespace relation
func (nu *NamespaceUser) Update() (err error) {
// Check if the right is valid
if err := nu.Right.isValid(); err != nil {
if err := nu.Right.IsValid(); err != nil {
return err
}

30
pkg/sharing/team_list.go Normal file
View File

@ -0,0 +1,30 @@
package sharing
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/team"
)
// TeamList defines the relation between a team and a list
type TeamList struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
TeamID int64 `xorm:"int(11) not null INDEX" json:"team_id" param:"team"`
ListID int64 `xorm:"int(11) not null INDEX" json:"list_id" param:"list"`
Right models.TeamRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
models.CRUDable `xorm:"-" json:"-"`
models.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
func (TeamList) TableName() string {
return "team_list"
}
type teamWithRight struct {
team.Team `xorm:"extends"`
Right models.TeamRight `json:"right"`
}

View File

@ -1,21 +1,28 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/team"
"code.vikunja.io/api/pkg/user"
)
// Create creates a new team <-> list relation
func (tl *TeamList) Create(doer *User) (err error) {
func (tl *TeamList) Create(doer *user.User) (err error) {
// Check if the rights are valid
if err = tl.Right.isValid(); err != nil {
if err = tl.Right.IsValid(); err != nil {
return
}
// Check if the team exists
_, err = GetTeamByID(tl.TeamID)
_, err = team.GetTeamByID(tl.TeamID)
if err != nil {
return
}
// Check if the list exists
l := &List{ID: tl.ListID}
l := &list.List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
return err
}
@ -28,7 +35,7 @@ func (tl *TeamList) Create(doer *User) (err error) {
return
}
if exists {
return ErrTeamAlreadyHasAccess{tl.TeamID, tl.ListID}
return errs.ErrTeamAlreadyHasAccess{tl.TeamID, tl.ListID}
}
// Insert the new team

View File

@ -1,10 +1,15 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/team"
)
// Delete deletes a team <-> list relation based on the list & team id
func (tl *TeamList) Delete() (err error) {
// Check if the team exists
_, err = GetTeamByID(tl.TeamID)
_, err = team.GetTeamByID(tl.TeamID)
if err != nil {
return
}
@ -16,7 +21,7 @@ func (tl *TeamList) Delete() (err error) {
return
}
if !has {
return ErrTeamDoesNotHaveAccessToList{TeamID: tl.TeamID, ListID: tl.ListID}
return errs.ErrTeamDoesNotHaveAccessToList{TeamID: tl.TeamID, ListID: tl.ListID}
}
// Delete the relation

View File

@ -1,14 +1,20 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/user"
)
// ReadAll implements the method to read all teams of a list
func (tl *TeamList) ReadAll(user *User) (interface{}, error) {
func (tl *TeamList) ReadAll(u *user.User) (interface{}, error) {
// Check if the user can read the namespace
l := &List{ID: tl.ListID}
l := &list.List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
return nil, err
}
if !l.CanRead(user) {
return nil, ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: user.ID}
if !l.CanRead(u) {
return nil, errs.ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: u.ID}
}
// Get the teams

View File

@ -0,0 +1,37 @@
package sharing
import (
"code.vikunja.io/api/pkg/list"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user"
)
// CanCreate checks if the user can create a team <-> list relation
func (tl *TeamList) CanCreate(u *user.User) bool {
l := list.List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanCreate for TeamList: %s", err)
return false
}
return l.IsAdmin(u)
}
// CanDelete checks if the user can delete a team <-> list relation
func (tl *TeamList) CanDelete(user *user.User) bool {
l := list.List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanDelete for TeamList: %s", err)
return false
}
return l.IsAdmin(user)
}
// CanUpdate checks if the user can update a team <-> list relation
func (tl *TeamList) CanUpdate(user *user.User) bool {
l := list.List{ID: tl.ListID}
if err := l.GetSimpleByID(); err != nil {
log.Log.Error("Error occurred during CanUpdate for TeamList: %s", err)
return false
}
return l.IsAdmin(user)
}

View File

@ -1,6 +1,9 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
@ -11,82 +14,82 @@ func TestTeamList(t *testing.T) {
tl := TeamList{
TeamID: 1,
ListID: 1,
Right: TeamRightAdmin,
Right: models.TeamRightAdmin,
}
// Dummyuser
user, err := GetUserByID(1)
u, err := user.GetUserByID(1)
assert.NoError(t, err)
// Check normal creation
assert.True(t, tl.CanCreate(&user))
err = tl.Create(&user)
assert.True(t, tl.CanCreate(&u))
err = tl.Create(&u)
assert.NoError(t, err)
// Check again
err = tl.Create(&user)
err = tl.Create(&u)
assert.Error(t, err)
assert.True(t, IsErrTeamAlreadyHasAccess(err))
assert.True(t, errs.IsErrTeamAlreadyHasAccess(err))
// Check with wrong rights
tl2 := tl
tl2.Right = TeamRightUnknown
err = tl2.Create(&user)
tl2.Right = models.TeamRightUnknown
err = tl2.Create(&u)
assert.Error(t, err)
assert.True(t, IsErrInvalidTeamRight(err))
assert.True(t, errs.IsErrInvalidTeamRight(err))
// Check with inexistant team
tl3 := tl
tl3.TeamID = 3253
err = tl3.Create(&user)
err = tl3.Create(&u)
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotExist(err))
assert.True(t, errs.IsErrTeamDoesNotExist(err))
// Check with inexistant list
tl4 := tl
tl4.ListID = 3252
err = tl4.Create(&user)
err = tl4.Create(&u)
assert.Error(t, err)
assert.True(t, IsErrListDoesNotExist(err))
assert.True(t, errs.IsErrListDoesNotExist(err))
// Test Read all
teams, err := tl.ReadAll(&user)
teams, err := tl.ReadAll(&u)
assert.NoError(t, err)
assert.Equal(t, reflect.TypeOf(teams).Kind(), reflect.Slice)
s := reflect.ValueOf(teams)
assert.Equal(t, s.Len(), 1)
// Test Read all for nonexistant list
_, err = tl4.ReadAll(&user)
_, err = tl4.ReadAll(&u)
assert.Error(t, err)
assert.True(t, IsErrListDoesNotExist(err))
assert.True(t, errs.IsErrListDoesNotExist(err))
// Test Read all for a list where the user is owner of the namespace this list belongs to
tl5 := tl
tl5.ListID = 2
_, err = tl5.ReadAll(&user)
_, err = tl5.ReadAll(&u)
assert.NoError(t, err)
// Test read all for a list where the user not has access
tl6 := tl
tl6.ListID = 3
_, err = tl6.ReadAll(&user)
_, err = tl6.ReadAll(&u)
assert.Error(t, err)
assert.True(t, IsErrNeedToHaveListReadAccess(err))
assert.True(t, errs.IsErrNeedToHaveListReadAccess(err))
// Delete
assert.True(t, tl.CanDelete(&user))
assert.True(t, tl.CanDelete(&u))
err = tl.Delete()
assert.NoError(t, err)
// Delete a nonexistant team
err = tl3.Delete()
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotExist(err))
assert.True(t, errs.IsErrTeamDoesNotExist(err))
// Delete with a nonexistant list
err = tl4.Delete()
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotHaveAccessToList(err))
assert.True(t, errs.IsErrTeamDoesNotHaveAccessToList(err))
}

View File

@ -1,10 +1,10 @@
package models
package sharing
// Update updates a team <-> list relation
func (tl *TeamList) Update() (err error) {
// Check if the right is valid
if err := tl.Right.isValid(); err != nil {
if err := tl.Right.IsValid(); err != nil {
return err
}

View File

@ -0,0 +1,22 @@
package sharing
import "code.vikunja.io/api/pkg/models"
// TeamNamespace defines the relationship between a Team and a Namespace
type TeamNamespace struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
TeamID int64 `xorm:"int(11) not null INDEX" json:"team_id" param:"team"`
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"namespace_id" param:"namespace"`
Right models.TeamRight `xorm:"int(11) INDEX" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
models.CRUDable `xorm:"-" json:"-"`
models.Rights `xorm:"-" json:"-"`
}
// TableName makes beautiful table names
func (TeamNamespace) TableName() string {
return "team_namespaces"
}

View File

@ -1,21 +1,28 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/team"
"code.vikunja.io/api/pkg/user"
)
// Create creates a new team <-> namespace relation
func (tn *TeamNamespace) Create(doer *User) (err error) {
func (tn *TeamNamespace) Create(doer *user.User) (err error) {
// Check if the rights are valid
if err = tn.Right.isValid(); err != nil {
if err = tn.Right.IsValid(); err != nil {
return
}
// Check if the team exists
_, err = GetTeamByID(tn.TeamID)
_, err = team.GetTeamByID(tn.TeamID)
if err != nil {
return
}
// Check if the namespace exists
_, err = GetNamespaceByID(tn.NamespaceID)
_, err = namespace.GetNamespaceByID(tn.NamespaceID)
if err != nil {
return
}
@ -28,7 +35,7 @@ func (tn *TeamNamespace) Create(doer *User) (err error) {
return
}
if exists {
return ErrTeamAlreadyHasAccess{tn.TeamID, tn.NamespaceID}
return errs.ErrTeamAlreadyHasAccess{tn.TeamID, tn.NamespaceID}
}
// Insert the new team

View File

@ -1,10 +1,15 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/team"
)
// Delete deletes a team <-> namespace relation based on the namespace & team id
func (tn *TeamNamespace) Delete() (err error) {
// Check if the team exists
_, err = GetTeamByID(tn.TeamID)
_, err = team.GetTeamByID(tn.TeamID)
if err != nil {
return
}
@ -16,7 +21,7 @@ func (tn *TeamNamespace) Delete() (err error) {
return
}
if !has {
return ErrTeamDoesNotHaveAccessToNamespace{TeamID: tn.TeamID, NamespaceID: tn.NamespaceID}
return errs.ErrTeamDoesNotHaveAccessToNamespace{TeamID: tn.TeamID, NamespaceID: tn.NamespaceID}
}
// Delete the relation

View File

@ -1,14 +1,20 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/user"
)
// ReadAll implements the method to read all teams of a namespace
func (tn *TeamNamespace) ReadAll(user *User) (interface{}, error) {
func (tn *TeamNamespace) ReadAll(user *user.User) (interface{}, error) {
// Check if the user can read the namespace
n, err := GetNamespaceByID(tn.NamespaceID)
n, err := namespace.GetNamespaceByID(tn.NamespaceID)
if err != nil {
return nil, err
}
if !n.CanRead(user) {
return nil, ErrNeedToHaveNamespaceReadAccess{NamespaceID: tn.NamespaceID, UserID: user.ID}
return nil, errs.ErrNeedToHaveNamespaceReadAccess{NamespaceID: tn.NamespaceID, UserID: user.ID}
}
// Get the teams

View File

@ -0,0 +1,37 @@
package sharing
import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/namespace"
"code.vikunja.io/api/pkg/user"
)
// CanCreate checks if one can create a new team <-> namespace relation
func (tn *TeamNamespace) CanCreate(user *user.User) bool {
n, err := namespace.GetNamespaceByID(tn.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanCreate for TeamNamespace: %s", err)
return false
}
return n.IsAdmin(user)
}
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
func (tn *TeamNamespace) CanDelete(user *user.User) bool {
n, err := namespace.GetNamespaceByID(tn.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanDelete for TeamNamespace: %s", err)
return false
}
return n.IsAdmin(user)
}
// CanUpdate checks if a user can update a team from a namespace. Only namespace admins can do that.
func (tn *TeamNamespace) CanUpdate(user *user.User) bool {
n, err := namespace.GetNamespaceByID(tn.NamespaceID)
if err != nil {
log.Log.Error("Error occurred during CanUpdate for TeamNamespace: %s", err)
return false
}
return n.IsAdmin(user)
}

View File

@ -1,6 +1,9 @@
package models
package sharing
import (
"code.vikunja.io/api/pkg/errs"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"reflect"
"testing"
@ -11,10 +14,10 @@ func TestTeamNamespace(t *testing.T) {
tn := TeamNamespace{
TeamID: 1,
NamespaceID: 1,
Right: TeamRightAdmin,
Right: models.TeamRightAdmin,
}
dummyuser, err := GetUserByID(1)
dummyuser, err := user.GetUserByID(1)
assert.NoError(t, err)
// Test normal creation
@ -25,28 +28,28 @@ func TestTeamNamespace(t *testing.T) {
// Test again (should fail)
err = tn.Create(&dummyuser)
assert.Error(t, err)
assert.True(t, IsErrTeamAlreadyHasAccess(err))
assert.True(t, errs.IsErrTeamAlreadyHasAccess(err))
// Test with invalid team right
tn2 := tn
tn2.Right = TeamRightUnknown
tn2.Right = models.TeamRightUnknown
err = tn2.Create(&dummyuser)
assert.Error(t, err)
assert.True(t, IsErrInvalidTeamRight(err))
assert.True(t, errs.IsErrInvalidTeamRight(err))
// Check with inexistant team
tn3 := tn
tn3.TeamID = 324
err = tn3.Create(&dummyuser)
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotExist(err))
assert.True(t, errs.IsErrTeamDoesNotExist(err))
// Check with a namespace which does not exist
tn4 := tn
tn4.NamespaceID = 423
err = tn4.Create(&dummyuser)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
assert.True(t, errs.IsErrNamespaceDoesNotExist(err))
// Check readall
teams, err := tn.ReadAll(&dummyuser)
@ -58,13 +61,13 @@ func TestTeamNamespace(t *testing.T) {
// Check readall for a nonexistant namespace
_, err = tn4.ReadAll(&dummyuser)
assert.Error(t, err)
assert.True(t, IsErrNamespaceDoesNotExist(err))
assert.True(t, errs.IsErrNamespaceDoesNotExist(err))
// Check with no right to read the namespace
nouser := &User{ID: 393}
nouser := &user.User{ID: 393}
_, err = tn.ReadAll(nouser)
assert.Error(t, err)
assert.True(t, IsErrNeedToHaveNamespaceReadAccess(err))
assert.True(t, errs.IsErrNeedToHaveNamespaceReadAccess(err))
// Delete it
assert.True(t, tn.CanDelete(&dummyuser))
@ -74,11 +77,11 @@ func TestTeamNamespace(t *testing.T) {
// Try deleting with a nonexisting team
err = tn3.Delete()
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotExist(err))
assert.True(t, errs.IsErrTeamDoesNotExist(err))
// Try deleting with a nonexistant namespace
err = tn4.Delete()
assert.Error(t, err)
assert.True(t, IsErrTeamDoesNotHaveAccessToNamespace(err))
assert.True(t, errs.IsErrTeamDoesNotHaveAccessToNamespace(err))
}

View File

@ -1,10 +1,10 @@
package models
package sharing
// Update updates a team <-> namespace relation
func (tl *TeamNamespace) Update() (err error) {
// Check if the right is valid
if err := tl.Right.isValid(); err != nil {
if err := tl.Right.IsValid(); err != nil {
return err
}

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