Rights performance improvements for lists and namespaces #54
|
@ -4,7 +4,7 @@ Content-Type: application/json
|
|||
|
||||
{
|
||||
"username": "user",
|
||||
"password": "1234"
|
||||
"password": "jup2000"
|
||||
}
|
||||
|
||||
> {% client.global.set("auth_token", response.body.token); %}
|
||||
|
|
|
@ -5,7 +5,7 @@ Authorization: Bearer {{auth_token}}
|
|||
###
|
||||
|
||||
# Get one list
|
||||
GET http://localhost:8080/api/v1/lists/1172
|
||||
GET http://localhost:8080/api/v1/lists/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
|
|
@ -5,7 +5,7 @@ Authorization: Bearer {{auth_token}}
|
|||
###
|
||||
|
||||
# Get one namespaces
|
||||
GET http://localhost:8080/api/v1/namespaces/125476
|
||||
GET http://localhost:8080/api/v1/namespaces/-1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
|
|
@ -24,6 +24,7 @@ This document describes the different errors Vikunja can return.
|
|||
| 4003 | 403 | All bulk editing tasks must belong to the same list. |
|
||||
| 4004 | 403 | Need at least one task when bulk editing tasks. |
|
||||
| 4005 | 403 | The user does not have the right to see the task. |
|
||||
| 4006 | 403 | The list right is invalid. |
|
||||
| 5001 | 404 | The namspace does not exist. |
|
||||
| 5003 | 403 | The user does not have access to the specified namespace. |
|
||||
| 5006 | 400 | The namespace name cannot be empty. |
|
||||
|
|
|
@ -502,6 +502,29 @@ func (err ErrNoRightToSeeTask) HTTPError() web.HTTPError {
|
|||
}
|
||||
}
|
||||
|
||||
// ErrInvalidListRight represents an error where a list right is invalid
|
||||
type ErrInvalidListRight struct {
|
||||
Right ListRight
|
||||
}
|
||||
|
||||
// IsErrInvalidListRight checks if an error is ErrInvalidListRight.
|
||||
func IsErrInvalidListRight(err error) bool {
|
||||
_, ok := err.(ErrInvalidListRight)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidListRight) Error() string {
|
||||
return fmt.Sprintf("List right is invalid [Right: %d]", err.Right)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidListRight holds the unique world-error code of this error
|
||||
const ErrCodeInvalidListRight = 4006
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidListRight) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidListRight, Message: "The list right is invalid."}
|
||||
}
|
||||
|
||||
// =================
|
||||
// Namespace errors
|
||||
// =================
|
||||
|
|
|
@ -19,6 +19,7 @@ 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
|
||||
|
@ -135,3 +136,95 @@ func (l *List) checkListUserRight(user *User, r UserRight) bool {
|
|||
|
||||
return exists
|
||||
}
|
||||
|
||||
func (l *List) checkListRight(user *User, rights ...UserRight) 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
|
||||
conds = append(conds, builder.Or(
|
||||
builder.And(
|
||||
builder.Eq{"ul.user_id": user.ID},
|
||||
builder.Eq{"ul.right": r},
|
||||
),
|
||||
builder.And(
|
||||
builder.Eq{"un.user_id": user.ID},
|
||||
builder.Eq{"un.right": r},
|
||||
),
|
||||
))
|
||||
// Team rights
|
||||
conds = append(conds, builder.Or(
|
||||
builder.And(
|
||||
builder.Eq{"tm.user_id": user.ID},
|
||||
builder.Eq{"tn.right": r},
|
||||
),
|
||||
builder.And(
|
||||
builder.Eq{"tm2.user_id": user.ID},
|
||||
builder.Eq{"tl.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").
|
||||
// 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 checkListUserRight for List: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// ListRight defines the rights users/teams can have for lists
|
||||
type ListRight int
|
||||
|
||||
// define unknown list right
|
||||
const (
|
||||
ListRightUnknown = -1
|
||||
)
|
||||
|
||||
// Enumerate all the list rights
|
||||
const (
|
||||
// Can read lists
|
||||
ListRightRead ListRight = iota
|
||||
// Can write/edit tasks in a list
|
||||
ListRightWrite
|
||||
// Can manage a list, can do everything
|
||||
ListRightAdmin
|
||||
)
|
||||
|
||||
func (r ListRight) isValid() error {
|
||||
if r != ListRightAdmin && r != ListRightRead && r != ListRightWrite {
|
||||
return ErrInvalidListRight{r}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue