Let rights methods return errors #64

Merged
konrad merged 5 commits from enhancements/rights-errors into master 2019-03-24 12:35:51 +00:00
27 changed files with 206 additions and 176 deletions
Showing only changes of commit dbc2c002db - Show all commits

View File

@ -17,7 +17,6 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/imdario/mergo" "github.com/imdario/mergo"
) )
@ -53,12 +52,11 @@ func (bt *BulkTask) checkIfTasksAreOnTheSameList() (err error) {
} }
// CanUpdate checks if a user is allowed to update a task // CanUpdate checks if a user is allowed to update a task
func (bt *BulkTask) CanUpdate(a web.Auth) bool { func (bt *BulkTask) CanUpdate(a web.Auth) (bool, error) {
err := bt.checkIfTasksAreOnTheSameList() err := bt.checkIfTasksAreOnTheSameList()
if err != nil { if err != nil {
log.Log.Error("Error occurred during CanUpdate for BulkTask: %s", err) return false, err
return false
} }
// A user can update an task if he has write acces to its list // A user can update an task if he has write acces to its list

View File

@ -17,51 +17,48 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/go-xorm/builder" "github.com/go-xorm/builder"
) )
// CanUpdate checks if a user can update a label // CanUpdate checks if a user can update a label
func (l *Label) CanUpdate(a web.Auth) bool { func (l *Label) CanUpdate(a web.Auth) (bool, error) {
return l.isLabelOwner(a) // Only owners should be allowed to update a label return l.isLabelOwner(a) // Only owners should be allowed to update a label
} }
// CanDelete checks if a user can delete a label // CanDelete checks if a user can delete a label
func (l *Label) CanDelete(a web.Auth) bool { func (l *Label) CanDelete(a web.Auth) (bool, error) {
return l.isLabelOwner(a) // Only owners should be allowed to delete a label return l.isLabelOwner(a) // Only owners should be allowed to delete a label
} }
// CanRead checks if a user can read a label // CanRead checks if a user can read a label
func (l *Label) CanRead(a web.Auth) bool { func (l *Label) CanRead(a web.Auth) (bool, error) {
return l.hasAccessToLabel(a) return l.hasAccessToLabel(a)
} }
// CanCreate checks if the user can create a label // CanCreate checks if the user can create a label
// Currently a dummy. // Currently a dummy.
func (l *Label) CanCreate(a web.Auth) bool { func (l *Label) CanCreate(a web.Auth) (bool, error) {
return true return true, nil
} }
func (l *Label) isLabelOwner(a web.Auth) bool { func (l *Label) isLabelOwner(a web.Auth) (bool, error) {
u := getUserForRights(a) u := getUserForRights(a)
lorig, err := getLabelByIDSimple(l.ID) lorig, err := getLabelByIDSimple(l.ID)
if err != nil { if err != nil {
log.Log.Errorf("Error occurred during isLabelOwner for Label: %v", err) return false, err
return false
} }
return lorig.CreatedByID == u.ID return lorig.CreatedByID == u.ID, nil
} }
// Helper method to check if a user can see a specific label // Helper method to check if a user can see a specific label
func (l *Label) hasAccessToLabel(a web.Auth) bool { func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
u := getUserForRights(a) u := getUserForRights(a)
// Get all tasks // Get all tasks
taskIDs, err := getUserTaskIDs(u) taskIDs, err := getUserTaskIDs(u)
if err != nil { if err != nil {
log.Log.Errorf("Error occurred during hasAccessToLabel for Label: %v", err) return false, err
return false
} }
// Get all labels associated with these tasks // Get all labels associated with these tasks
@ -74,10 +71,5 @@ func (l *Label) hasAccessToLabel(a web.Auth) bool {
And("labels.id = ?", l.ID). And("labels.id = ?", l.ID).
GroupBy("labels.id"). GroupBy("labels.id").
Exist(&labels) Exist(&labels)
if err != nil { return has, err
log.Log.Errorf("Error occurred during hasAccessToLabel for Label: %v", err)
return false
}
return has
} }

View File

@ -114,7 +114,11 @@ func (lt *LabelTask) ReadAll(search string, a web.Auth, page int) (labels interf
return nil, err return nil, err
} }
if !task.CanRead(a) { canRead, err := task.CanRead(a)
if err != nil {
return nil, err
}
if !canRead {
return nil, ErrNoRightToSeeTask{lt.TaskID, u.ID} return nil, ErrNoRightToSeeTask{lt.TaskID, u.ID}
} }
@ -256,7 +260,11 @@ func (t *ListTask) updateTaskLabels(creator web.Auth, labels []*Label) (err erro
} }
// Check if the user has the rights to see the label he is about to add // Check if the user has the rights to see the label he is about to add
if !label.hasAccessToLabel(creator) { hasAccessToLabel, err := label.hasAccessToLabel(creator)
if err != nil {
return err
}
if !hasAccessToLabel {
user, _ := creator.(*User) user, _ := creator.(*User)
return ErrUserHasNoAccessToLabel{LabelID: l.ID, UserID: user.ID} return ErrUserHasNoAccessToLabel{LabelID: l.ID, UserID: user.ID}
} }

View File

@ -17,51 +17,61 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
) )
// CanCreate checks if a user can add a label to a task // CanCreate checks if a user can add a label to a task
func (lt *LabelTask) CanCreate(a web.Auth) bool { func (lt *LabelTask) CanCreate(a web.Auth) (bool, error) {
label, err := getLabelByIDSimple(lt.LabelID) label, err := getLabelByIDSimple(lt.LabelID)
if err != nil { if err != nil {
log.Log.Errorf("Error during CanCreate for LabelTask: %v", err) return false, err
return false
} }
return label.hasAccessToLabel(a) && canDoLabelTask(lt.TaskID, a) hasAccessTolabel, err := label.hasAccessToLabel(a)
if err != nil || !hasAccessTolabel { // If the user doesn't have access to the label, we can error out here
return false, err
}
canDoLabelTask, err := canDoLabelTask(lt.TaskID, a)
if err != nil {
return false, err
}
return hasAccessTolabel && canDoLabelTask, nil
} }
// CanDelete checks if a user can delete a label from a task // CanDelete checks if a user can delete a label from a task
func (lt *LabelTask) CanDelete(a web.Auth) bool { func (lt *LabelTask) CanDelete(a web.Auth) (bool, error) {
if !canDoLabelTask(lt.TaskID, a) { canDoLabelTask, err := canDoLabelTask(lt.TaskID, a)
return false if err != nil {
return false, err
}
if !canDoLabelTask {
return false, nil
} }
// We don't care here if the label exists or not. The only relevant thing here is if the relation already exists, // We don't care here if the label exists or not. The only relevant thing here is if the relation already exists,
// throw an error. // throw an error.
exists, err := x.Exist(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID}) exists, err := x.Exist(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID})
if err != nil { if err != nil {
log.Log.Errorf("Error during CanDelete for LabelTask: %v", err) return false, err
return false
} }
return exists return exists, err
} }
// CanCreate determines if a user can update a labeltask // CanCreate determines if a user can update a labeltask
func (ltb *LabelTaskBulk) CanCreate(a web.Auth) bool { func (ltb *LabelTaskBulk) CanCreate(a web.Auth) (bool, error) {
return canDoLabelTask(ltb.TaskID, a) return canDoLabelTask(ltb.TaskID, a)
} }
// Helper function to check if a user can write to a task // Helper function to check if a user can write to a task
// + is able to see the label // + is able to see the label
// always the same check for either deleting or adding a label to a task // always the same check for either deleting or adding a label to a task
func canDoLabelTask(taskID int64, a web.Auth) bool { func canDoLabelTask(taskID int64, a web.Auth) (bool, error) {
// A user can add a label to a task if he can write to the task // A user can add a label to a task if he can write to the task
task, err := getTaskByIDSimple(taskID) task, err := getTaskByIDSimple(taskID)
if err != nil { if err != nil {
log.Log.Error("Error occurred during canDoLabelTask for LabelTask: %v", err) return false, err
return false
} }
return task.CanUpdate(a) return task.CanUpdate(a)
} }

View File

@ -17,71 +17,76 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/go-xorm/builder" "github.com/go-xorm/builder"
) )
// CanWrite return whether the user can write on that list or not // CanWrite return whether the user can write on that list or not
func (l *List) CanWrite(a web.Auth) bool { func (l *List) CanWrite(a web.Auth) (bool, error) {
// Get the list and check the right // Get the list and check the right
originalList := &List{ID: l.ID} originalList := &List{ID: l.ID}
err := originalList.GetSimpleByID() err := originalList.GetSimpleByID()
if err != nil { if err != nil {
log.Log.Error("Error occurred during CanWrite for List: %s", err) return false, err
return false
} }
user := getUserForRights(a) user := getUserForRights(a)
// Check all the things
// Check if the user is either owner or can write to the list // Check if the user is either owner or can write to the list
return originalList.isOwner(user) || originalList.checkRight(user, RightWrite, RightAdmin) if originalList.isOwner(user) {
return true, nil
}
return originalList.checkRight(user, RightWrite, RightAdmin)
} }
// CanRead checks if a user has read access to a list // CanRead checks if a user has read access to a list
func (l *List) CanRead(a web.Auth) bool { func (l *List) CanRead(a web.Auth) (bool, error) {
user := getUserForRights(a) user := getUserForRights(a)
// Check all the things
// Check if the user is either owner or can read // Check if the user is either owner or can read
// We can do this without first looking up the list because CanRead() is called after ReadOne() // We can do this without first looking up the list because CanRead() is called after ReadOne()
// So are sure the list exists // So are sure the list exists
return l.isOwner(user) || l.checkRight(user, RightRead, RightWrite, RightAdmin) if l.isOwner(user) {
return true, nil
}
return l.checkRight(user, RightRead, RightWrite, RightAdmin)
} }
// CanUpdate checks if the user can update a list // CanUpdate checks if the user can update a list
func (l *List) CanUpdate(a web.Auth) bool { func (l *List) CanUpdate(a web.Auth) (bool, error) {
return l.CanWrite(a) return l.CanWrite(a)
} }
// CanDelete checks if the user can delete a list // CanDelete checks if the user can delete a list
func (l *List) CanDelete(a web.Auth) bool { func (l *List) CanDelete(a web.Auth) (bool, error) {
return l.IsAdmin(a) return l.IsAdmin(a)
} }
// CanCreate checks if the user can update a list // CanCreate checks if the user can update a list
func (l *List) CanCreate(a web.Auth) bool { func (l *List) CanCreate(a web.Auth) (bool, error) {
// A user can create a list if he has write access to the namespace // A user can create a list if he has write access to the namespace
n := &Namespace{ID: l.NamespaceID} n := &Namespace{ID: l.NamespaceID}
return n.CanWrite(a) return n.CanWrite(a)
} }
// IsAdmin returns whether the user has admin rights on the list or not // IsAdmin returns whether the user has admin rights on the list or not
func (l *List) IsAdmin(a web.Auth) bool { func (l *List) IsAdmin(a web.Auth) (bool, error) {
user := getUserForRights(a) user := getUserForRights(a)
originalList := &List{ID: l.ID} originalList := &List{ID: l.ID}
err := originalList.GetSimpleByID() err := originalList.GetSimpleByID()
if err != nil { if err != nil {
log.Log.Error("Error occurred during IsAdmin for List: %s", err) return false, err
return false
} }
// Check all the things // Check all the things
// Check if the user is either owner or can write to the list // Check if the user is either owner or can write to the list
// Owners are always admins // Owners are always admins
return originalList.isOwner(user) || originalList.checkRight(user, RightAdmin) if originalList.isOwner(user) {
return true, nil
}
return originalList.checkRight(user, RightAdmin)
} }
// Little helper function to check if a user is list owner // Little helper function to check if a user is list owner
@ -90,7 +95,7 @@ func (l *List) isOwner(u *User) bool {
} }
// Checks n different rights for any given user // Checks n different rights for any given user
func (l *List) checkRight(user *User, rights ...Right) bool { func (l *List) checkRight(user *User, rights ...Right) (bool, error) {
/* /*
The following loop creates an sql condition like this one: The following loop creates an sql condition like this one:
@ -149,10 +154,5 @@ func (l *List) checkRight(user *User, rights ...Right) bool {
builder.Eq{"l.id": l.ID}, builder.Eq{"l.id": l.ID},
)). )).
Exist(&List{}) Exist(&List{})
if err != nil { return exists, err
log.Log.Error("Error occurred during checkRight for list: %s", err)
return false
}
return exists
} }

View File

@ -175,7 +175,8 @@ func (t *ListTask) addNewAssigneeByID(newAssigneeID int64, list *List) (err erro
if err != nil { if err != nil {
return err return err
} }
if !list.CanRead(&newAssignee) { canRead, err := list.CanRead(&newAssignee)
if !canRead {
return ErrUserDoesNotHaveAccessToList{list.ID, newAssigneeID} return ErrUserDoesNotHaveAccessToList{list.ID, newAssigneeID}
} }

View File

@ -17,31 +17,29 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
) )
// CanCreate checks if a user can add a new assignee // CanCreate checks if a user can add a new assignee
func (la *ListTaskAssginee) CanCreate(a web.Auth) bool { func (la *ListTaskAssginee) CanCreate(a web.Auth) (bool, error) {
return canDoListTaskAssingee(la.TaskID, a) return canDoListTaskAssingee(la.TaskID, a)
} }
// CanCreate checks if a user can add a new assignee // CanCreate checks if a user can add a new assignee
func (ba *BulkAssignees) CanCreate(a web.Auth) bool { func (ba *BulkAssignees) CanCreate(a web.Auth) (bool, error) {
return canDoListTaskAssingee(ba.TaskID, a) return canDoListTaskAssingee(ba.TaskID, a)
} }
// CanDelete checks if a user can delete an assignee // CanDelete checks if a user can delete an assignee
func (la *ListTaskAssginee) CanDelete(a web.Auth) bool { func (la *ListTaskAssginee) CanDelete(a web.Auth) (bool, error) {
return canDoListTaskAssingee(la.TaskID, a) return canDoListTaskAssingee(la.TaskID, a)
} }
func canDoListTaskAssingee(taskID int64, a web.Auth) bool { func canDoListTaskAssingee(taskID int64, a web.Auth) (bool, error) {
// Check if the current user can edit the list // Check if the current user can edit the list
list, err := GetListSimplByTaskID(taskID) list, err := GetListSimplByTaskID(taskID)
if err != nil { if err != nil {
log.Log.Errorf("Error during canDoListTaskAssingee for ListTaskAssginee: %v", err) return false, err
return false
} }
return list.CanCreate(a) return list.CanCreate(a)
} }

View File

@ -17,22 +17,21 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
) )
// CanDelete checks if the user can delete an task // CanDelete checks if the user can delete an task
func (t *ListTask) CanDelete(a web.Auth) bool { func (t *ListTask) CanDelete(a web.Auth) (bool, error) {
return t.canDoListTask(a) return t.canDoListTask(a)
} }
// CanUpdate determines if a user has the right to update a list task // CanUpdate determines if a user has the right to update a list task
func (t *ListTask) CanUpdate(a web.Auth) bool { func (t *ListTask) CanUpdate(a web.Auth) (bool, error) {
return t.canDoListTask(a) return t.canDoListTask(a)
} }
// CanCreate determines if a user has the right to create a list task // CanCreate determines if a user has the right to create a list task
func (t *ListTask) CanCreate(a web.Auth) bool { func (t *ListTask) CanCreate(a web.Auth) (bool, error) {
doer := getUserForRights(a) doer := getUserForRights(a)
// A user can do a task if he has write acces to its list // A user can do a task if he has write acces to its list
@ -41,26 +40,24 @@ func (t *ListTask) CanCreate(a web.Auth) bool {
} }
// CanRead determines if a user can read a task // CanRead determines if a user can read a task
func (t *ListTask) CanRead(a web.Auth) bool { func (t *ListTask) CanRead(a web.Auth) (bool, error) {
// A user can read a task if it has access to the list // A user can read a task if it has access to the list
list := &List{ID: t.ListID} list := &List{ID: t.ListID}
err := list.GetSimpleByID() err := list.GetSimpleByID()
if err != nil { if err != nil {
log.Log.Error("Error occurred during CanRead for ListTask: %s", err) return false, err
return false
} }
return list.CanRead(a) return list.CanRead(a)
} }
// Helper function to check if a user can do stuff on a list task // Helper function to check if a user can do stuff on a list task
func (t *ListTask) canDoListTask(a web.Auth) bool { func (t *ListTask) canDoListTask(a web.Auth) (bool, error) {
doer := getUserForRights(a) doer := getUserForRights(a)
// Get the task // Get the task
lI, err := getTaskByIDSimple(t.ID) lI, err := getTaskByIDSimple(t.ID)
if err != nil { if err != nil {
log.Log.Error("Error occurred during canDoListTask (getTaskByIDSimple) for ListTask: %s", err) return false, err
return false
} }
// A user can do a task if he has write acces to its list // A user can do a task if he has write acces to its list

View File

@ -43,7 +43,11 @@ func (lu *ListUser) ReadAll(search string, a web.Auth, page int) (interface{}, e
if err := l.GetSimpleByID(); err != nil { if err := l.GetSimpleByID(); err != nil {
return nil, err return nil, err
} }
if !l.CanRead(u) { canRead, err := l.CanRead(u)
if err != nil {
return nil, err
}
if !canRead {
return nil, ErrNeedToHaveListReadAccess{UserID: u.ID, ListID: lu.ListID} return nil, ErrNeedToHaveListReadAccess{UserID: u.ID, ListID: lu.ListID}
} }

View File

@ -21,21 +21,21 @@ import (
) )
// CanCreate checks if the user can create a new user <-> list relation // CanCreate checks if the user can create a new user <-> list relation
func (lu *ListUser) CanCreate(a web.Auth) bool { func (lu *ListUser) CanCreate(a web.Auth) (bool, error) {
// Get the list and check if the user has write access on it // Get the list and check if the user has write access on it
l := List{ID: lu.ListID} l := List{ID: lu.ListID}
return l.CanWrite(a) return l.CanWrite(a)
} }
// CanDelete checks if the user can delete a user <-> list relation // CanDelete checks if the user can delete a user <-> list relation
func (lu *ListUser) CanDelete(a web.Auth) bool { func (lu *ListUser) CanDelete(a web.Auth) (bool, error) {
// Get the list and check if the user has write access on it // Get the list and check if the user has write access on it
l := List{ID: lu.ListID} l := List{ID: lu.ListID}
return l.CanWrite(a) return l.CanWrite(a)
} }
// CanUpdate checks if the user can update a user <-> list relation // CanUpdate checks if the user can update a user <-> list relation
func (lu *ListUser) CanUpdate(a web.Auth) bool { func (lu *ListUser) CanUpdate(a web.Auth) (bool, error) {
// Get the list and check if the user has write access on it // Get the list and check if the user has write access on it
l := List{ID: lu.ListID} l := List{ID: lu.ListID}
return l.CanWrite(a) return l.CanWrite(a)

View File

@ -17,59 +17,71 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
"github.com/go-xorm/builder" "github.com/go-xorm/builder"
) )
// CanWrite checks if a user has write access to a namespace // CanWrite checks if a user has write access to a namespace
func (n *Namespace) CanWrite(a web.Auth) bool { func (n *Namespace) CanWrite(a web.Auth) (bool, error) {
// Get the namespace and check the right // Get the namespace and check the right
originalNamespace := &Namespace{ID: n.ID} originalNamespace := &Namespace{ID: n.ID}
err := originalNamespace.GetSimpleByID() err := originalNamespace.GetSimpleByID()
if err != nil { if err != nil {
log.Log.Error("Error occurred during CanWrite for Namespace: %s", err) return false, err
return false
} }
u := getUserForRights(a) u := getUserForRights(a)
return originalNamespace.isOwner(u) || originalNamespace.checkRight(u, RightWrite, RightAdmin) if originalNamespace.isOwner(u) {
return true, nil
}
return originalNamespace.checkRight(u, RightWrite, RightAdmin)
} }
// IsAdmin returns true or false if the user is admin on that namespace or not // IsAdmin returns true or false if the user is admin on that namespace or not
func (n *Namespace) IsAdmin(a web.Auth) bool { func (n *Namespace) IsAdmin(a web.Auth) (bool, error) {
originalNamespace := &Namespace{ID: n.ID} originalNamespace := &Namespace{ID: n.ID}
err := originalNamespace.GetSimpleByID() err := originalNamespace.GetSimpleByID()
if err != nil { if err != nil {
log.Log.Error("Error occurred during IsAdmin for Namespace: %s", err) return false, err
return false
} }
u := getUserForRights(a) u := getUserForRights(a)
return originalNamespace.isOwner(u) || originalNamespace.checkRight(u, RightAdmin) if originalNamespace.isOwner(u) {
return true, nil
}
return originalNamespace.checkRight(u, RightAdmin)
} }
// CanRead checks if a user has read access to that namespace // CanRead checks if a user has read access to that namespace
func (n *Namespace) CanRead(a web.Auth) bool { func (n *Namespace) CanRead(a web.Auth) (bool, error) {
originalNamespace := &Namespace{ID: n.ID}
err := originalNamespace.GetSimpleByID()
if err != nil {
return false, err
}
u := getUserForRights(a) u := getUserForRights(a)
return n.isOwner(u) || n.checkRight(u, RightRead, RightWrite, RightAdmin) if originalNamespace.isOwner(u) {
return true, nil
}
return n.checkRight(u, RightRead, RightWrite, RightAdmin)
} }
// CanUpdate checks if the user can update the namespace // CanUpdate checks if the user can update the namespace
func (n *Namespace) CanUpdate(a web.Auth) bool { func (n *Namespace) CanUpdate(a web.Auth) (bool, error) {
return n.IsAdmin(a) return n.IsAdmin(a)
} }
// CanDelete checks if the user can delete a namespace // CanDelete checks if the user can delete a namespace
func (n *Namespace) CanDelete(a web.Auth) bool { func (n *Namespace) CanDelete(a web.Auth) (bool, error) {
return n.IsAdmin(a) return n.IsAdmin(a)
} }
// CanCreate checks if the user can create a new namespace // CanCreate checks if the user can create a new namespace
func (n *Namespace) CanCreate(a web.Auth) bool { func (n *Namespace) CanCreate(a web.Auth) (bool, error) {
// This is currently a dummy function, later on we could imagine global limits etc. // This is currently a dummy function, later on we could imagine global limits etc.
return true return true, nil
} }
// Small helper function to check if a user owns the namespace // Small helper function to check if a user owns the namespace
@ -77,7 +89,7 @@ func (n *Namespace) isOwner(user *User) bool {
return n.OwnerID == user.ID return n.OwnerID == user.ID
} }
func (n *Namespace) checkRight(user *User, rights ...Right) bool { func (n *Namespace) checkRight(user *User, rights ...Right) (bool, error) {
/* /*
The following loop creates an sql condition like this one: The following loop creates an sql condition like this one:
@ -124,10 +136,5 @@ func (n *Namespace) checkRight(user *User, rights ...Right) bool {
builder.Eq{"namespaces.id": n.ID}, builder.Eq{"namespaces.id": n.ID},
)). )).
Exist(&List{}) Exist(&List{})
if err != nil { return exists, err
log.Log.Error("Error occurred during checkRight for namespace: %s", err)
return false
}
return exists
} }

View File

@ -43,7 +43,11 @@ func (nu *NamespaceUser) ReadAll(search string, a web.Auth, page int) (interface
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !l.CanRead(u) { canRead, err := l.CanRead(u)
if err != nil {
return nil, err
}
if !canRead {
return nil, ErrNeedToHaveNamespaceReadAccess{} return nil, ErrNeedToHaveNamespaceReadAccess{}
} }

View File

@ -21,19 +21,19 @@ import (
) )
// CanCreate checks if the user can create a new user <-> namespace relation // CanCreate checks if the user can create a new user <-> namespace relation
func (nu *NamespaceUser) CanCreate(a web.Auth) bool { func (nu *NamespaceUser) CanCreate(a web.Auth) (bool, error) {
n := &Namespace{ID: nu.NamespaceID} n := &Namespace{ID: nu.NamespaceID}
return n.CanWrite(a) return n.CanWrite(a)
} }
// CanDelete checks if the user can delete a user <-> namespace relation // CanDelete checks if the user can delete a user <-> namespace relation
func (nu *NamespaceUser) CanDelete(a web.Auth) bool { func (nu *NamespaceUser) CanDelete(a web.Auth) (bool, error) {
n := &Namespace{ID: nu.NamespaceID} n := &Namespace{ID: nu.NamespaceID}
return n.CanWrite(a) return n.CanWrite(a)
} }
// CanUpdate checks if the user can update a user <-> namespace relation // CanUpdate checks if the user can update a user <-> namespace relation
func (nu *NamespaceUser) CanUpdate(a web.Auth) bool { func (nu *NamespaceUser) CanUpdate(a web.Auth) (bool, error) {
n := &Namespace{ID: nu.NamespaceID} n := &Namespace{ID: nu.NamespaceID}
return n.CanWrite(a) return n.CanWrite(a)
} }

View File

@ -43,7 +43,11 @@ func (tl *TeamList) ReadAll(search string, a web.Auth, page int) (interface{}, e
if err := l.GetSimpleByID(); err != nil { if err := l.GetSimpleByID(); err != nil {
return nil, err return nil, err
} }
if !l.CanRead(u) { canRead, err := l.CanRead(u)
if err != nil {
return nil, err
}
if !canRead {
return nil, ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: u.ID} return nil, ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: u.ID}
} }

View File

@ -21,7 +21,7 @@ import (
) )
// CanCreate checks if the user can create a team <-> list relation // CanCreate checks if the user can create a team <-> list relation
func (tl *TeamList) CanCreate(a web.Auth) bool { func (tl *TeamList) CanCreate(a web.Auth) (bool, error) {
u := getUserForRights(a) u := getUserForRights(a)
l := List{ID: tl.ListID} l := List{ID: tl.ListID}
@ -29,7 +29,7 @@ func (tl *TeamList) CanCreate(a web.Auth) bool {
} }
// CanDelete checks if the user can delete a team <-> list relation // CanDelete checks if the user can delete a team <-> list relation
func (tl *TeamList) CanDelete(a web.Auth) bool { func (tl *TeamList) CanDelete(a web.Auth) (bool, error) {
user := getUserForRights(a) user := getUserForRights(a)
l := List{ID: tl.ListID} l := List{ID: tl.ListID}
@ -37,7 +37,7 @@ func (tl *TeamList) CanDelete(a web.Auth) bool {
} }
// CanUpdate checks if the user can update a team <-> list relation // CanUpdate checks if the user can update a team <-> list relation
func (tl *TeamList) CanUpdate(a web.Auth) bool { func (tl *TeamList) CanUpdate(a web.Auth) (bool, error) {
user := getUserForRights(a) user := getUserForRights(a)
l := List{ID: tl.ListID} l := List{ID: tl.ListID}

View File

@ -17,30 +17,25 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
) )
// CanCreate checks if the user can add a new tem member // CanCreate checks if the user can add a new tem member
func (tm *TeamMember) CanCreate(a web.Auth) bool { func (tm *TeamMember) CanCreate(a web.Auth) (bool, error) {
return tm.IsAdmin(a) return tm.IsAdmin(a)
} }
// CanDelete checks if the user can delete a new team member // CanDelete checks if the user can delete a new team member
func (tm *TeamMember) CanDelete(a web.Auth) bool { func (tm *TeamMember) CanDelete(a web.Auth) (bool, error) {
return tm.IsAdmin(a) return tm.IsAdmin(a)
} }
// IsAdmin checks if the user is team admin // IsAdmin checks if the user is team admin
func (tm *TeamMember) IsAdmin(a web.Auth) bool { func (tm *TeamMember) IsAdmin(a web.Auth) (bool, error) {
u := getUserForRights(a) u := getUserForRights(a)
// A user can add a member to a team if he is admin of that team // 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 = ?", u.ID, tm.TeamID, true). exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", u.ID, tm.TeamID, true).
Get(&TeamMember{}) Get(&TeamMember{})
if err != nil { return exists, err
log.Log.Error("Error occurred during IsAdmin for TeamMember: %s", err)
return false
}
return exists
} }

View File

@ -43,7 +43,11 @@ func (tn *TeamNamespace) ReadAll(search string, a web.Auth, page int) (interface
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !n.CanRead(user) { canRead, err := n.CanRead(user)
if err != nil {
return nil, err
}
if !canRead {
return nil, ErrNeedToHaveNamespaceReadAccess{NamespaceID: tn.NamespaceID, UserID: user.ID} return nil, ErrNeedToHaveNamespaceReadAccess{NamespaceID: tn.NamespaceID, UserID: user.ID}
} }

View File

@ -21,19 +21,19 @@ import (
) )
// CanCreate checks if one can create a new team <-> namespace relation // CanCreate checks if one can create a new team <-> namespace relation
func (tn *TeamNamespace) CanCreate(a web.Auth) bool { func (tn *TeamNamespace) CanCreate(a web.Auth) (bool, error) {
n := &Namespace{ID: tn.NamespaceID} n := &Namespace{ID: tn.NamespaceID}
return n.IsAdmin(a) return n.IsAdmin(a)
} }
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that. // CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
func (tn *TeamNamespace) CanDelete(a web.Auth) bool { func (tn *TeamNamespace) CanDelete(a web.Auth) (bool, error) {
n := &Namespace{ID: tn.NamespaceID} n := &Namespace{ID: tn.NamespaceID}
return n.IsAdmin(a) return n.IsAdmin(a)
} }
// CanUpdate checks if a user can update a team from a Only namespace admins can do that. // CanUpdate checks if a user can update a team from a Only namespace admins can do that.
func (tn *TeamNamespace) CanUpdate(a web.Auth) bool { func (tn *TeamNamespace) CanUpdate(a web.Auth) (bool, error) {
n := &Namespace{ID: tn.NamespaceID} n := &Namespace{ID: tn.NamespaceID}
return n.IsAdmin(a) return n.IsAdmin(a)
} }

View File

@ -17,64 +17,47 @@
package models package models
import ( import (
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/web" "code.vikunja.io/web"
) )
// CanCreate checks if the user can create a new team // CanCreate checks if the user can create a new team
func (t *Team) CanCreate(a web.Auth) bool { func (t *Team) CanCreate(a web.Auth) (bool, error) {
// This is currently a dummy function, later on we could imagine global limits etc. // This is currently a dummy function, later on we could imagine global limits etc.
return true return true, nil
} }
// CanUpdate checks if the user can update a team // CanUpdate checks if the user can update a team
func (t *Team) CanUpdate(a web.Auth) bool { func (t *Team) CanUpdate(a web.Auth) (bool, error) {
u := getUserForRights(a) u := getUserForRights(a)
// Check if the current user is in the team and has admin rights in it // Check if the current user is in the team and has admin rights in it
exists, err := x.Where("team_id = ?", t.ID). return x.Where("team_id = ?", t.ID).
And("user_id = ?", u.ID). And("user_id = ?", u.ID).
And("admin = ?", true). And("admin = ?", true).
Get(&TeamMember{}) Get(&TeamMember{})
if err != nil {
log.Log.Error("Error occurred during CanUpdate for Team: %s", err)
return false
}
return exists
} }
// CanDelete checks if a user can delete a team // CanDelete checks if a user can delete a team
func (t *Team) CanDelete(a web.Auth) bool { func (t *Team) CanDelete(a web.Auth) (bool, error) {
return t.IsAdmin(a) return t.IsAdmin(a)
} }
// IsAdmin returns true when the user is admin of a team // IsAdmin returns true when the user is admin of a team
func (t *Team) IsAdmin(a web.Auth) bool { func (t *Team) IsAdmin(a web.Auth) (bool, error) {
u := getUserForRights(a) u := getUserForRights(a)
exists, err := x.Where("team_id = ?", t.ID). return x.Where("team_id = ?", t.ID).
And("user_id = ?", u.ID). And("user_id = ?", u.ID).
And("admin = ?", true). And("admin = ?", true).
Get(&TeamMember{}) Get(&TeamMember{})
if err != nil {
log.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 // CanRead returns true if the user has read access to the team
func (t *Team) CanRead(a web.Auth) bool { func (t *Team) CanRead(a web.Auth) (bool, error) {
user := getUserForRights(a) user := getUserForRights(a)
// Check if the user is in the team // Check if the user is in the team
exists, err := x.Where("team_id = ?", t.ID). return x.Where("team_id = ?", t.ID).
And("user_id = ?", user.ID). And("user_id = ?", user.ID).
Get(&TeamMember{}) Get(&TeamMember{})
if err != nil {
log.Log.Error("Error occurred during CanUpdate for Team: %s", err)
return false
}
return exists
} }

View File

@ -91,7 +91,11 @@ func getNamespace(c echo.Context) (namespace models.Namespace, err error) {
if err != nil { if err != nil {
return return
} }
if !namespace.CanRead(user) { canRead, err := namespace.CanRead(user)
if err != nil {
return namespace, err
}
if !canRead {
return return
} }

25
vendor/code.vikunja.io/web/Readme.md generated vendored
View File

@ -38,10 +38,11 @@ other handler implementations, enabling a lot of flexibility while develeoping.
### TODOs ### TODOs
* [ ] Improve docs/Merge with the ones of Vikunja * [x] Improve docs/Merge with the ones of Vikunja
* [ ] Description of web.HTTPError * [x] Description of web.HTTPError
* [ ] Rights methods should return errors (I know, this will break a lot of existing stuff) * [x] Rights methods should return errors (I know, this will break a lot of existing stuff)
* [ ] optional Before- and after-{load|update|create} methods which do some preprocessing/after processing like making human-readable names from automatically up counting consts * [ ] optional Before- and after-{load|update|create} methods which do some preprocessing/after processing like making human-readable names from automatically up counting consts
* [ ] "Magic": Check if a passed struct implements Crudable methods and use a general (user defined) function if not
## Installation ## Installation
@ -49,6 +50,8 @@ Using the web handler in your application is pretty straight forward, simply run
In order to use the common web handler, the struct must implement the `web.CRUDable` and `web.Rights` interface. In order to use the common web handler, the struct must implement the `web.CRUDable` and `web.Rights` interface.
To learn how to use the handler, take a look at the [handler config](#handler-config) [defining routes](#defining-routes-using-the-standard-web-handler)
## CRUDable ## CRUDable
This interface defines methods to Create/Read/ReadAll/Update/Delete something. It is defined as followed: This interface defines methods to Create/Read/ReadAll/Update/Delete something. It is defined as followed:
@ -82,18 +85,20 @@ way to do this, don't hesitate to [drop me a message](https://vikunja.io/en/cont
## Rights ## Rights
This interface defines methods to check for rights on structs. They accept an `Auth`-element as parameter and return a `bool`. This interface defines methods to check for rights on structs. They accept an `Auth`-element as parameter and return a `bool` and `error`.
The `error` is handled [as usual](#errors).
The interface is defined as followed: The interface is defined as followed:
```go ```go
type Rights interface { type Rights interface {
IsAdmin(Auth) bool IsAdmin(Auth) (bool, error)
CanWrite(Auth) bool CanWrite(Auth) (bool, error)
CanRead(Auth) bool CanRead(Auth) (bool, error)
CanDelete(Auth) bool CanDelete(Auth) (bool, error)
CanUpdate(Auth) bool CanUpdate(Auth) (bool, error)
CanCreate(Auth) bool CanCreate(Auth) (bool, error)
} }
``` ```

View File

@ -42,7 +42,11 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error {
} }
// Check rights // Check rights
if !currentStruct.CanCreate(currentAuth) { canRead, err := currentStruct.CanCreate(currentAuth)
if err != nil {
return HandleHTTPError(err, ctx)
}
if canRead {
config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth) config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth)
return echo.NewHTTPError(http.StatusForbidden) return echo.NewHTTPError(http.StatusForbidden)
} }

View File

@ -40,7 +40,11 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError) return echo.NewHTTPError(http.StatusInternalServerError)
} }
if !currentStruct.CanDelete(currentAuth) { canDelete, err := currentStruct.CanDelete(currentAuth)
if err != nil {
return HandleHTTPError(err, ctx)
}
if canDelete {
config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth) config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth)
return echo.NewHTTPError(http.StatusForbidden) return echo.NewHTTPError(http.StatusForbidden)
} }

View File

@ -42,7 +42,11 @@ func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.") return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
} }
if !currentStruct.CanRead(currentAuth) { canRead, err := currentStruct.CanRead(currentAuth)
if err != nil {
return HandleHTTPError(err, ctx)
}
if canRead {
config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth) config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth)
return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this") return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this")
} }

View File

@ -41,7 +41,11 @@ func (c *WebHandler) UpdateWeb(ctx echo.Context) error {
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.") return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
} }
if !currentStruct.CanUpdate(currentAuth) { canUpdate, err := currentStruct.CanUpdate(currentAuth)
if err != nil {
return HandleHTTPError(err, ctx)
}
if canUpdate {
config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth) config.LoggingProvider.Noticef("Tried to create while not having the rights for it (User: %v)", currentAuth)
return echo.NewHTTPError(http.StatusForbidden) return echo.NewHTTPError(http.StatusForbidden)
} }

12
vendor/code.vikunja.io/web/web.go generated vendored
View File

@ -19,12 +19,12 @@ import "github.com/labstack/echo"
// Rights defines rights methods // Rights defines rights methods
type Rights interface { type Rights interface {
IsAdmin(Auth) bool IsAdmin(Auth) (bool, error)
CanWrite(Auth) bool CanWrite(Auth) (bool, error)
CanRead(Auth) bool CanRead(Auth) (bool, error)
CanDelete(Auth) bool CanDelete(Auth) (bool, error)
CanUpdate(Auth) bool CanUpdate(Auth) (bool, error)
CanCreate(Auth) bool CanCreate(Auth) (bool, error)
} }
// CRUDable defines the crud methods // CRUDable defines the crud methods

2
vendor/modules.txt vendored
View File

@ -1,4 +1,4 @@
# code.vikunja.io/web v0.0.0-20190324080741-7bd881d9892a # code.vikunja.io/web v0.0.0-20190324105229-0933ac082307
code.vikunja.io/web code.vikunja.io/web
code.vikunja.io/web/handler code.vikunja.io/web/handler
# github.com/BurntSushi/toml v0.3.1 # github.com/BurntSushi/toml v0.3.1