Rights performance improvements for lists and namespaces (#54)
This commit is contained in:
@ -699,29 +699,6 @@ func (err ErrTeamDoesNotExist) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."}
|
||||
}
|
||||
|
||||
// ErrInvalidTeamRight represents an error where a team right is invalid
|
||||
type ErrInvalidTeamRight struct {
|
||||
Right TeamRight
|
||||
}
|
||||
|
||||
// IsErrInvalidTeamRight checks if an error is ErrInvalidTeamRight.
|
||||
func IsErrInvalidTeamRight(err error) bool {
|
||||
_, ok := err.(ErrInvalidTeamRight)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidTeamRight) Error() string {
|
||||
return fmt.Sprintf("Team right invalid [Right: %d]", err.Right)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidTeamRight holds the unique world-error code of this error
|
||||
const ErrCodeInvalidTeamRight = 6003
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidTeamRight) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidTeamRight, Message: "The team right is invalid."}
|
||||
}
|
||||
|
||||
// ErrTeamAlreadyHasAccess represents an error where a team already has access to a list/namespace
|
||||
type ErrTeamAlreadyHasAccess struct {
|
||||
TeamID int64
|
||||
@ -822,29 +799,6 @@ func (err ErrTeamDoesNotHaveAccessToList) HTTPError() web.HTTPError {
|
||||
// User <-> List errors
|
||||
// ====================
|
||||
|
||||
// ErrInvalidUserRight represents an error where a user right is invalid
|
||||
type ErrInvalidUserRight struct {
|
||||
Right UserRight
|
||||
}
|
||||
|
||||
// IsErrInvalidUserRight checks if an error is ErrInvalidUserRight.
|
||||
func IsErrInvalidUserRight(err error) bool {
|
||||
_, ok := err.(ErrInvalidUserRight)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidUserRight) Error() string {
|
||||
return fmt.Sprintf("User right is invalid [Right: %d]", err.Right)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidUserRight holds the unique world-error code of this error
|
||||
const ErrCodeInvalidUserRight = 7001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidUserRight) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidUserRight, Message: "The user right is invalid."}
|
||||
}
|
||||
|
||||
// ErrUserAlreadyHasAccess represents an error where a user already has access to a list/namespace
|
||||
type ErrUserAlreadyHasAccess struct {
|
||||
UserID int64
|
||||
@ -979,3 +933,34 @@ func (err ErrUserHasNoAccessToLabel) HTTPError() web.HTTPError {
|
||||
Message: "You don't have access to this label.",
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Rights
|
||||
// ========
|
||||
|
||||
// ErrInvalidRight represents an error where a right is invalid
|
||||
type ErrInvalidRight struct {
|
||||
Right Right
|
||||
}
|
||||
|
||||
// IsErrInvalidRight checks if an error is ErrInvalidRight.
|
||||
func IsErrInvalidRight(err error) bool {
|
||||
_, ok := err.(ErrInvalidRight)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidRight) Error() string {
|
||||
return fmt.Sprintf(" right invalid [Right: %d]", err.Right)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidRight holds the unique world-error code of this error
|
||||
const ErrCodeInvalidRight = 9001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidRight) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
Code: ErrCodeInvalidRight,
|
||||
Message: "The right is invalid.",
|
||||
}
|
||||
}
|
||||
|
@ -19,74 +19,35 @@ package models
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/web"
|
||||
"github.com/go-xorm/builder"
|
||||
)
|
||||
|
||||
// IsAdmin returns whether the user has admin rights on the list or not
|
||||
func (l *List) IsAdmin(a web.Auth) bool {
|
||||
u := getUserForRights(a)
|
||||
|
||||
// Owners are always admins
|
||||
if l.OwnerID == u.ID {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check individual rights
|
||||
if l.checkListUserRight(u, UserRightAdmin) {
|
||||
return true
|
||||
}
|
||||
|
||||
return l.checkListTeamRight(u, TeamRightAdmin)
|
||||
}
|
||||
|
||||
// CanWrite return whether the user can write on that list or not
|
||||
func (l *List) CanWrite(a web.Auth) bool {
|
||||
user := getUserForRights(a)
|
||||
|
||||
// Admins always have write access
|
||||
if l.IsAdmin(user) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check individual rights
|
||||
if l.checkListUserRight(user, UserRightWrite) {
|
||||
return true
|
||||
}
|
||||
|
||||
return l.checkListTeamRight(user, TeamRightWrite)
|
||||
// Check all the things
|
||||
// Check if the user is either owner or can write to the list
|
||||
return l.isOwner(user) || l.checkRight(user, RightWrite, RightAdmin)
|
||||
}
|
||||
|
||||
// CanRead checks if a user has read access to a list
|
||||
func (l *List) CanRead(a web.Auth) bool {
|
||||
user := getUserForRights(a)
|
||||
|
||||
// Admins always have read access
|
||||
if l.IsAdmin(user) {
|
||||
return true
|
||||
}
|
||||
// Check all the things
|
||||
// Check if the user is either owner or can read
|
||||
return l.isOwner(user) || l.checkRight(user, RightRead, RightWrite, RightAdmin)
|
||||
}
|
||||
|
||||
// Check individual rights
|
||||
if l.checkListUserRight(user, UserRightRead) {
|
||||
return true
|
||||
}
|
||||
|
||||
if l.checkListTeamRight(user, TeamRightRead) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Users who are able to write should also be able to read
|
||||
// CanUpdate checks if the user can update a list
|
||||
func (l *List) CanUpdate(a web.Auth) bool {
|
||||
return l.CanWrite(a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a list
|
||||
func (l *List) CanDelete(a web.Auth) bool {
|
||||
doer := getUserForRights(a)
|
||||
return l.IsAdmin(doer)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update a list
|
||||
func (l *List) CanUpdate(a web.Auth) bool {
|
||||
doer := getUserForRights(a)
|
||||
return l.CanWrite(doer)
|
||||
return l.IsAdmin(a)
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can update a list
|
||||
@ -96,40 +57,83 @@ func (l *List) CanCreate(a web.Auth) bool {
|
||||
return n.CanWrite(a)
|
||||
}
|
||||
|
||||
func (l *List) checkListTeamRight(user *User, r TeamRight) bool {
|
||||
// IsAdmin returns whether the user has admin rights on the list or not
|
||||
func (l *List) IsAdmin(a web.Auth) bool {
|
||||
user := getUserForRights(a)
|
||||
|
||||
// Check all the things
|
||||
// Check if the user is either owner or can write to the list
|
||||
// Owners are always admins
|
||||
return l.isOwner(user) || l.checkRight(user, RightAdmin)
|
||||
}
|
||||
|
||||
// Little helper function to check if a user is list owner
|
||||
func (l *List) isOwner(u *User) bool {
|
||||
return l.OwnerID == u.ID
|
||||
}
|
||||
|
||||
// Checks n different rights for any given user
|
||||
func (l *List) checkRight(user *User, rights ...Right) bool {
|
||||
|
||||
/*
|
||||
The following loop creates an sql condition like this one:
|
||||
|
||||
(ul.user_id = 1 AND ul.right = 1) OR (un.user_id = 1 AND un.right = 1) OR
|
||||
(tm.user_id = 1 AND tn.right = 1) OR (tm2.user_id = 1 AND tl.right = 1) OR
|
||||
|
||||
for each passed right. That way, we can check with a single sql query (instead if 8)
|
||||
if the user has the right to see the list or not.
|
||||
*/
|
||||
|
||||
var conds []builder.Cond
|
||||
for _, r := range rights {
|
||||
// User conditions
|
||||
// If the list was shared directly with the user and the user has the right
|
||||
conds = append(conds, builder.And(
|
||||
builder.Eq{"ul.user_id": user.ID},
|
||||
builder.Eq{"ul.right": r},
|
||||
))
|
||||
// If the namespace this list belongs to was shared directly with the user and the user has the right
|
||||
conds = append(conds, builder.And(
|
||||
builder.Eq{"un.user_id": user.ID},
|
||||
builder.Eq{"un.right": r},
|
||||
))
|
||||
|
||||
// Team rights
|
||||
// If the list was shared directly with the team and the team has the right
|
||||
conds = append(conds, builder.And(
|
||||
builder.Eq{"tm2.user_id": user.ID},
|
||||
builder.Eq{"tl.right": r},
|
||||
))
|
||||
// If the namespace this list belongs to was shared directly with the team and the team has the right
|
||||
conds = append(conds, builder.And(
|
||||
builder.Eq{"tm.user_id": user.ID},
|
||||
builder.Eq{"tn.right": r},
|
||||
))
|
||||
}
|
||||
|
||||
exists, err := x.Select("l.*").
|
||||
Table("list").
|
||||
Alias("l").
|
||||
// User stuff
|
||||
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
||||
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
||||
Join("LEFT", []string{"namespaces", "n"}, "n.id = l.namespace_id").
|
||||
// Team stuff
|
||||
Join("LEFT", []string{"team_namespaces", "tn"}, " l.namespace_id = tn.namespace_id").
|
||||
Join("LEFT", []string{"team_members", "tm"}, "tm.team_id = tn.team_id").
|
||||
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
|
||||
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
||||
Where("((tm.user_id = ? AND tn.right = ?) OR (tm2.user_id = ? AND tl.right = ?)) AND l.id = ?",
|
||||
user.ID, r, user.ID, r, l.ID).
|
||||
// The actual condition
|
||||
Where(builder.And(
|
||||
builder.Or(
|
||||
conds...,
|
||||
),
|
||||
builder.Eq{"l.id": l.ID},
|
||||
)).
|
||||
Exist(&List{})
|
||||
if err != nil {
|
||||
log.Log.Error("Error occurred during checkListTeamRight for List: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
func (l *List) checkListUserRight(user *User, r UserRight) bool {
|
||||
exists, err := x.Select("l.*").
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
||||
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
||||
Join("LEFT", []string{"namespaces", "n"}, "n.id = l.namespace_id").
|
||||
Where("((ul.user_id = ? AND ul.right = ?) "+
|
||||
"OR (un.user_id = ? AND un.right = ?) "+
|
||||
"OR n.owner_id = ?)"+
|
||||
"AND l.id = ?",
|
||||
user.ID, r, user.ID, r, user.ID, l.ID).
|
||||
Exist(&List{})
|
||||
if err != nil {
|
||||
log.Log.Error("Error occurred during checkListUserRight for List: %s", err)
|
||||
log.Log.Error("Error occurred during checkRight for list: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ type ListUser struct {
|
||||
// The list id.
|
||||
ListID int64 `xorm:"int(11) not null INDEX" json:"-" param:"list"`
|
||||
// The right this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||
Right UserRight `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
Right Right `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
|
||||
// A unix timestamp when this relation was created. You cannot change this value.
|
||||
Created int64 `xorm:"created" json:"created"`
|
||||
@ -46,5 +46,5 @@ func (ListUser) TableName() string {
|
||||
// UserWithRight represents a user in combination with the right it can have on a list/namespace
|
||||
type UserWithRight struct {
|
||||
User `xorm:"extends"`
|
||||
Right UserRight `json:"right"`
|
||||
Right Right `json:"right"`
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func TestListUser_CanDoSomething(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
ListID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
|
@ -29,7 +29,7 @@ func TestListUser_Create(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
ListID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -69,7 +69,7 @@ func TestListUser_Create(t *testing.T) {
|
||||
Right: 500,
|
||||
},
|
||||
wantErr: true,
|
||||
errType: IsErrInvalidUserRight,
|
||||
errType: IsErrInvalidRight,
|
||||
},
|
||||
{
|
||||
name: "ListUsers Create with inexisting list",
|
||||
@ -127,7 +127,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
ListID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -162,7 +162,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||
Password: "1234",
|
||||
Email: "user1@example.com",
|
||||
},
|
||||
Right: UserRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
{
|
||||
User: User{
|
||||
@ -171,7 +171,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||
Password: "1234",
|
||||
Email: "user2@example.com",
|
||||
},
|
||||
Right: UserRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -218,7 +218,7 @@ func TestListUser_Update(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
ListID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -235,7 +235,7 @@ func TestListUser_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
ListID: 3,
|
||||
UserID: 1,
|
||||
Right: UserRightAdmin,
|
||||
Right: RightAdmin,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -243,7 +243,7 @@ func TestListUser_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
ListID: 3,
|
||||
UserID: 1,
|
||||
Right: UserRightWrite,
|
||||
Right: RightWrite,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -251,7 +251,7 @@ func TestListUser_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
ListID: 3,
|
||||
UserID: 1,
|
||||
Right: UserRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -262,7 +262,7 @@ func TestListUser_Update(t *testing.T) {
|
||||
Right: 500,
|
||||
},
|
||||
wantErr: true,
|
||||
errType: IsErrInvalidUserRight,
|
||||
errType: IsErrInvalidRight,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@ -293,7 +293,7 @@ func TestListUser_Delete(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
ListID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
|
@ -19,84 +19,29 @@ package models
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/web"
|
||||
"github.com/go-xorm/builder"
|
||||
)
|
||||
|
||||
// IsAdmin returns true or false if the user is admin on that namespace or not
|
||||
func (n *Namespace) IsAdmin(a web.Auth) bool {
|
||||
u := getUserForRights(a)
|
||||
|
||||
// Owners always have admin rights
|
||||
if u.ID == n.Owner.ID {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check user rights
|
||||
if n.checkUserRights(u, UserRightAdmin) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if that user is in a team which has admin rights to that namespace
|
||||
return n.checkTeamRights(u, TeamRightAdmin)
|
||||
}
|
||||
|
||||
// CanWrite checks if a user has write access to a namespace
|
||||
func (n *Namespace) CanWrite(a web.Auth) bool {
|
||||
u := getUserForRights(a)
|
||||
|
||||
// Admins always have write access
|
||||
if n.IsAdmin(u) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check user rights
|
||||
if n.checkUserRights(u, UserRightWrite) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if that user is in a team which has write rights to that namespace
|
||||
return n.checkTeamRights(u, TeamRightWrite)
|
||||
return n.isOwner(u) || n.checkRight(u, RightWrite, RightAdmin)
|
||||
}
|
||||
|
||||
// CanRead checks if a user has read access to that namespace
|
||||
func (n *Namespace) CanRead(a web.Auth) bool {
|
||||
u := getUserForRights(a)
|
||||
|
||||
// Admins always have read access
|
||||
if n.IsAdmin(u) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check user rights
|
||||
if n.checkUserRights(u, UserRightRead) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the user is in a team which has access to the namespace
|
||||
return n.checkTeamRights(u, TeamRightRead)
|
||||
return n.isOwner(u) || n.checkRight(u, RightRead, RightWrite, RightAdmin)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update the namespace
|
||||
func (n *Namespace) CanUpdate(a web.Auth) bool {
|
||||
u := getUserForRights(a)
|
||||
|
||||
nn, err := GetNamespaceByID(n.ID)
|
||||
if err != nil {
|
||||
log.Log.Error("Error occurred during CanUpdate for Namespace: %s", err)
|
||||
return false
|
||||
}
|
||||
return nn.IsAdmin(u)
|
||||
return n.IsAdmin(a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a namespace
|
||||
func (n *Namespace) CanDelete(a web.Auth) bool {
|
||||
u := getUserForRights(a)
|
||||
|
||||
nn, err := GetNamespaceByID(n.ID)
|
||||
if err != nil {
|
||||
log.Log.Error("Error occurred during CanDelete for Namespace: %s", err)
|
||||
return false
|
||||
}
|
||||
return nn.IsAdmin(u)
|
||||
return n.IsAdmin(a)
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can create a new namespace
|
||||
@ -105,33 +50,66 @@ func (n *Namespace) CanCreate(a web.Auth) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *Namespace) checkTeamRights(u *User, r TeamRight) bool {
|
||||
// IsAdmin returns true or false if the user is admin on that namespace or not
|
||||
func (n *Namespace) IsAdmin(a web.Auth) bool {
|
||||
u := getUserForRights(a)
|
||||
return n.isOwner(u) || n.checkRight(u, RightAdmin)
|
||||
}
|
||||
|
||||
// Small helper function to check if a user owns the namespace
|
||||
func (n *Namespace) isOwner(user *User) bool {
|
||||
return n.OwnerID == user.ID
|
||||
}
|
||||
|
||||
func (n *Namespace) checkRight(user *User, rights ...Right) bool {
|
||||
|
||||
/*
|
||||
The following loop creates an sql condition like this one:
|
||||
|
||||
namespaces.owner_id = 1 OR
|
||||
(users_namespace.user_id = 1 AND users_namespace.right = 1) OR
|
||||
(team_members.user_id = 1 AND team_namespaces.right = 1) OR
|
||||
|
||||
|
||||
for each passed right. That way, we can check with a single sql query (instead if 8)
|
||||
if the user has the right to see the list or not.
|
||||
*/
|
||||
|
||||
var conds []builder.Cond
|
||||
conds = append(conds, builder.Eq{"namespaces.owner_id": user.ID})
|
||||
for _, r := range rights {
|
||||
// User conditions
|
||||
// If the namespace was shared directly with the user and the user has the right
|
||||
conds = append(conds, builder.And(
|
||||
builder.Eq{"users_namespace.user_id": user.ID},
|
||||
builder.Eq{"users_namespace.right": r},
|
||||
))
|
||||
|
||||
// Team rights
|
||||
// If the namespace was shared directly with the team and the team has the right
|
||||
conds = append(conds, builder.And(
|
||||
builder.Eq{"team_members.user_id": user.ID},
|
||||
builder.Eq{"team_namespaces.right": r},
|
||||
))
|
||||
}
|
||||
|
||||
exists, err := x.Select("namespaces.*").
|
||||
Table("namespaces").
|
||||
// User stuff
|
||||
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
|
||||
// Teams stuff
|
||||
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, u.ID, r, u.ID).
|
||||
Get(&Namespace{})
|
||||
// The actual condition
|
||||
Where(builder.And(
|
||||
builder.Or(
|
||||
conds...,
|
||||
),
|
||||
builder.Eq{"namespaces.id": n.ID},
|
||||
)).
|
||||
Exist(&List{})
|
||||
if err != nil {
|
||||
log.Log.Error("Error occurred during checkTeamRights for Namespace: %s, TeamRight: %d", err, r)
|
||||
return false
|
||||
}
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
func (n *Namespace) checkUserRights(u *User, r UserRight) bool {
|
||||
exists, err := x.Select("namespaces.*").
|
||||
Table("namespaces").
|
||||
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
|
||||
Where("namespaces.id = ? AND ("+
|
||||
"(users_namespace.user_id = ? AND users_namespace.right = ?) "+
|
||||
"OR namespaces.owner_id = ?)", n.ID, u.ID, r, u.ID).
|
||||
Get(&Namespace{})
|
||||
if err != nil {
|
||||
log.Log.Error("Error occurred during checkUserRights for Namespace: %s, UserRight: %d", err, r)
|
||||
log.Log.Error("Error occurred during checkRight for namespace: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ type NamespaceUser struct {
|
||||
// The namespace id
|
||||
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"-" param:"namespace"`
|
||||
// The right this user has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||
Right UserRight `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
Right Right `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
|
||||
// A unix timestamp when this relation was created. You cannot change this value.
|
||||
Created int64 `xorm:"created" json:"created"`
|
||||
|
@ -27,7 +27,7 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
NamespaceID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
|
@ -30,7 +30,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
NamespaceID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -70,7 +70,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
||||
Right: 500,
|
||||
},
|
||||
wantErr: true,
|
||||
errType: IsErrInvalidUserRight,
|
||||
errType: IsErrInvalidRight,
|
||||
},
|
||||
{
|
||||
name: "NamespaceUsers Create with inexisting list",
|
||||
@ -128,7 +128,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
NamespaceID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -163,7 +163,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||
Password: "1234",
|
||||
Email: "user1@example.com",
|
||||
},
|
||||
Right: UserRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
{
|
||||
User: User{
|
||||
@ -172,7 +172,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||
Password: "1234",
|
||||
Email: "user2@example.com",
|
||||
},
|
||||
Right: UserRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -220,7 +220,7 @@ func TestNamespaceUser_Update(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
NamespaceID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -237,7 +237,7 @@ func TestNamespaceUser_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
NamespaceID: 3,
|
||||
UserID: 1,
|
||||
Right: UserRightAdmin,
|
||||
Right: RightAdmin,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -245,7 +245,7 @@ func TestNamespaceUser_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
NamespaceID: 3,
|
||||
UserID: 1,
|
||||
Right: UserRightWrite,
|
||||
Right: RightWrite,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -253,7 +253,7 @@ func TestNamespaceUser_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
NamespaceID: 3,
|
||||
UserID: 1,
|
||||
Right: UserRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -264,7 +264,7 @@ func TestNamespaceUser_Update(t *testing.T) {
|
||||
Right: 500,
|
||||
},
|
||||
wantErr: true,
|
||||
errType: IsErrInvalidUserRight,
|
||||
errType: IsErrInvalidRight,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@ -295,7 +295,7 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
||||
ID int64
|
||||
UserID int64
|
||||
NamespaceID int64
|
||||
Right UserRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Vikunja is a todo-list application to facilitate your life.
|
||||
// Copyright 2018 Vikunja and contributors. All rights reserved.
|
||||
// Copyright 2019 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@ -16,27 +16,27 @@
|
||||
|
||||
package models
|
||||
|
||||
// TeamRight defines the rights teams can have for lists/namespaces
|
||||
type TeamRight int
|
||||
// Right defines the rights users/teams can have for lists/namespaces
|
||||
type Right int
|
||||
|
||||
// define unknown team right
|
||||
// define unknown right
|
||||
const (
|
||||
TeamRightUnknown = -1
|
||||
RightUnknown = -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 read lists in a
|
||||
RightRead Right = iota
|
||||
// Can write in a like lists and todo tasks. Cannot create new lists.
|
||||
RightWrite
|
||||
// Can manage a list/namespace, can do everything
|
||||
TeamRightAdmin
|
||||
RightAdmin
|
||||
)
|
||||
|
||||
func (r TeamRight) isValid() error {
|
||||
if r != TeamRightAdmin && r != TeamRightRead && r != TeamRightWrite {
|
||||
return ErrInvalidTeamRight{r}
|
||||
func (r Right) isValid() error {
|
||||
if r != RightAdmin && r != RightRead && r != RightWrite {
|
||||
return ErrInvalidRight{r}
|
||||
}
|
||||
|
||||
return nil
|
@ -27,7 +27,7 @@ type TeamList struct {
|
||||
// The list id.
|
||||
ListID int64 `xorm:"int(11) not null INDEX" json:"-" param:"list"`
|
||||
// The right this team has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||
Right TeamRight `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
Right Right `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
|
||||
// A unix timestamp when this relation was created. You cannot change this value.
|
||||
Created int64 `xorm:"created" json:"created"`
|
||||
@ -46,5 +46,5 @@ func (TeamList) TableName() string {
|
||||
// TeamWithRight represents a team, combined with rights.
|
||||
type TeamWithRight struct {
|
||||
Team `xorm:"extends"`
|
||||
Right TeamRight `json:"right"`
|
||||
Right Right `json:"right"`
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ func TestTeamList(t *testing.T) {
|
||||
tl := TeamList{
|
||||
TeamID: 1,
|
||||
ListID: 1,
|
||||
Right: TeamRightAdmin,
|
||||
Right: RightAdmin,
|
||||
}
|
||||
|
||||
// Dummyuser
|
||||
@ -48,10 +48,10 @@ func TestTeamList(t *testing.T) {
|
||||
|
||||
// Check with wrong rights
|
||||
tl2 := tl
|
||||
tl2.Right = TeamRightUnknown
|
||||
tl2.Right = RightUnknown
|
||||
err = tl2.Create(&u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrInvalidTeamRight(err))
|
||||
assert.True(t, IsErrInvalidRight(err))
|
||||
|
||||
// Check with inexistant team
|
||||
tl3 := tl
|
||||
@ -113,7 +113,7 @@ func TestTeamList_Update(t *testing.T) {
|
||||
ID int64
|
||||
TeamID int64
|
||||
ListID int64
|
||||
Right TeamRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -130,7 +130,7 @@ func TestTeamList_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
ListID: 3,
|
||||
TeamID: 1,
|
||||
Right: TeamRightAdmin,
|
||||
Right: RightAdmin,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -138,7 +138,7 @@ func TestTeamList_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
ListID: 3,
|
||||
TeamID: 1,
|
||||
Right: TeamRightWrite,
|
||||
Right: RightWrite,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -146,7 +146,7 @@ func TestTeamList_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
ListID: 3,
|
||||
TeamID: 1,
|
||||
Right: TeamRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -157,7 +157,7 @@ func TestTeamList_Update(t *testing.T) {
|
||||
Right: 500,
|
||||
},
|
||||
wantErr: true,
|
||||
errType: IsErrInvalidTeamRight,
|
||||
errType: IsErrInvalidRight,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
@ -27,7 +27,7 @@ type TeamNamespace struct {
|
||||
// The namespace id.
|
||||
NamespaceID int64 `xorm:"int(11) not null INDEX" json:"-" param:"namespace"`
|
||||
// The right this team has. 0 = Read only, 1 = Read & Write, 2 = Admin. See the docs for more details.
|
||||
Right TeamRight `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
Right Right `xorm:"int(11) INDEX" json:"right" valid:"length(0|2)" maximum:"2" default:"0"`
|
||||
|
||||
// A unix timestamp when this relation was created. You cannot change this value.
|
||||
Created int64 `xorm:"created" json:"created"`
|
||||
|
@ -27,7 +27,7 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
|
||||
ID int64
|
||||
TeamID int64
|
||||
NamespaceID int64
|
||||
Right TeamRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
|
@ -29,7 +29,7 @@ func TestTeamNamespace(t *testing.T) {
|
||||
tn := TeamNamespace{
|
||||
TeamID: 1,
|
||||
NamespaceID: 1,
|
||||
Right: TeamRightAdmin,
|
||||
Right: RightAdmin,
|
||||
}
|
||||
|
||||
dummyuser, err := GetUserByID(1)
|
||||
@ -47,10 +47,10 @@ func TestTeamNamespace(t *testing.T) {
|
||||
|
||||
// Test with invalid team right
|
||||
tn2 := tn
|
||||
tn2.Right = TeamRightUnknown
|
||||
tn2.Right = RightUnknown
|
||||
err = tn2.Create(&dummyuser)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrInvalidTeamRight(err))
|
||||
assert.True(t, IsErrInvalidRight(err))
|
||||
|
||||
// Check with inexistant team
|
||||
tn3 := tn
|
||||
@ -105,7 +105,7 @@ func TestTeamNamespace_Update(t *testing.T) {
|
||||
ID int64
|
||||
TeamID int64
|
||||
NamespaceID int64
|
||||
Right TeamRight
|
||||
Right Right
|
||||
Created int64
|
||||
Updated int64
|
||||
CRUDable web.CRUDable
|
||||
@ -122,7 +122,7 @@ func TestTeamNamespace_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
NamespaceID: 3,
|
||||
TeamID: 1,
|
||||
Right: TeamRightAdmin,
|
||||
Right: RightAdmin,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -130,7 +130,7 @@ func TestTeamNamespace_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
NamespaceID: 3,
|
||||
TeamID: 1,
|
||||
Right: TeamRightWrite,
|
||||
Right: RightWrite,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -138,7 +138,7 @@ func TestTeamNamespace_Update(t *testing.T) {
|
||||
fields: fields{
|
||||
NamespaceID: 3,
|
||||
TeamID: 1,
|
||||
Right: TeamRightRead,
|
||||
Right: RightRead,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -149,7 +149,7 @@ func TestTeamNamespace_Update(t *testing.T) {
|
||||
Right: 500,
|
||||
},
|
||||
wantErr: true,
|
||||
errType: IsErrInvalidTeamRight,
|
||||
errType: IsErrInvalidRight,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
@ -92,15 +92,15 @@ func TestTeam_Create(t *testing.T) {
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
}
|
||||
|
||||
func TestIsErrInvalidTeamRight(t *testing.T) {
|
||||
assert.NoError(t, TeamRightAdmin.isValid())
|
||||
assert.NoError(t, TeamRightRead.isValid())
|
||||
assert.NoError(t, TeamRightWrite.isValid())
|
||||
func TestIsErrInvalidRight(t *testing.T) {
|
||||
assert.NoError(t, RightAdmin.isValid())
|
||||
assert.NoError(t, RightRead.isValid())
|
||||
assert.NoError(t, RightWrite.isValid())
|
||||
|
||||
// Check invalid
|
||||
var tr TeamRight
|
||||
var tr Right
|
||||
tr = 938
|
||||
err := tr.isValid()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrInvalidTeamRight(err))
|
||||
assert.True(t, IsErrInvalidRight(err))
|
||||
}
|
||||
|
@ -1,43 +0,0 @@
|
||||
// Vikunja is a todo-list application to facilitate your life.
|
||||
// Copyright 2018 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user