Added methods to give users access to a list

This commit is contained in:
konrad 2018-08-30 08:58:09 +02:00 committed by kolaente
parent d31f16aff1
commit b1c3e92f66
No known key found for this signature in database
GPG key ID: F40E70337AB24C9B
10 changed files with 185 additions and 0 deletions

View file

@ -25,3 +25,17 @@ Authorization: Bearer {{auth_token}}
### ###
# Get all users who have access to that list
GET http://localhost:8080/api/v1/lists/10/users
Authorization: Bearer {{auth_token}}
###
# Give a user access to that list
PUT http://localhost:8080/api/v1/lists/1/users
Authorization: Bearer {{auth_token}}
Content-Type: application/json
{"user_id":2, "right": 5}
###

View file

@ -495,3 +495,38 @@ func IsErrCannotDeleteLastTeamMember(err error) bool {
func (err ErrCannotDeleteLastTeamMember) Error() string { func (err ErrCannotDeleteLastTeamMember) Error() string {
return fmt.Sprintf("This user is already a member of that team. [Team ID: %d, User ID: %d]", err.TeamID, err.UserID) return fmt.Sprintf("This user is already a member of that team. [Team ID: %d, User ID: %d]", err.TeamID, err.UserID)
} }
// ====================
// 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("The right is invalid [Right: %d]", err.Right)
}
// ErrUserAlreadyHasAccess represents an error where a user already has access to a list/namespace
type ErrUserAlreadyHasAccess struct {
UserID int64
ListID int64
}
// IsErrUserAlreadyHasAccess checks if an error is ErrUserAlreadyHasAccess.
func IsErrUserAlreadyHasAccess(err error) bool {
_, ok := err.(ErrUserAlreadyHasAccess)
return ok
}
func (err ErrUserAlreadyHasAccess) Error() string {
return fmt.Sprintf("This user already has access to that list. [User ID: %d, List ID: %d]", err.UserID, err.ListID)
}

20
models/list_users.go Normal file
View file

@ -0,0 +1,20 @@
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" json:"user_id" param:"user"`
ListID int64 `xorm:"int(11) not null" json:"list_id" param:"list"`
Right TeamRight `xorm:"int(11)" 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"
}

View file

@ -0,0 +1,40 @@
package models
// Create creates a new list <-> user relation
func (ul *ListUser) Create(user *User) (err error) {
// Check if the right is valid
if err := ul.Right.isValid(); err != nil {
return err
}
// Check if the list exists
l, err := GetListByID(ul.ListID)
if err != nil {
return
}
// Check if the user exists
if _, _, err = 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}
}
exist, err := x.Where("list_id = ? AND user_id = ?", ul.ListID, ul.UserID).Get(&ListUser{})
if err != nil {
return
}
if exist {
return ErrUserAlreadyHasAccess{UserID: ul.UserID, ListID: ul.ListID}
}
// Insert user <-> list relation
_, err = x.Insert(ul)
return
}

View file

@ -0,0 +1,23 @@
package models
// ReadAll gets all users who have access to a list
func (ul *ListUser) ReadAll(user *User) (interface{}, error) {
// Check if the user has access to the list
l, err := GetListByID(ul.ListID)
if err != nil {
return nil, err
}
if !l.CanRead(user) {
return nil, ErrNeedToHaveListReadAccess{}
}
// Get all users
all := []*User{}
err = x.
Select("users.*").
Join("INNER", "users_list", "user_id = users.id").
Where("users_list.list_id = ?", ul.ListID).
Find(&all)
return all, err
}

View file

@ -0,0 +1,34 @@
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, _ := GetListByID(lu.ListID)
return l.CanWrite(doer)
}

View file

@ -40,6 +40,7 @@ func init() {
new(TeamList), new(TeamList),
new(TeamNamespace), new(TeamNamespace),
new(Namespace), new(Namespace),
new(ListUser),
) )
} }

View file

@ -63,6 +63,13 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "This user is already a member of that team.") return echo.NewHTTPError(http.StatusBadRequest, "This user is already a member of that team.")
} }
if models.IsErrUserAlreadyHasAccess(err) {
return echo.NewHTTPError(http.StatusBadRequest, "This user already has access to this list.")
}
if models.IsErrInvalidUserRight(err) {
return echo.NewHTTPError(http.StatusBadRequest, "The right is invalid.")
}
return echo.NewHTTPError(http.StatusInternalServerError) return echo.NewHTTPError(http.StatusInternalServerError)
} }

View file

@ -20,6 +20,10 @@ func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
lists, err := c.CObject.ReadAll(&currentUser) lists, err := c.CObject.ReadAll(&currentUser)
if err != nil { if err != nil {
if models.IsErrNeedToHaveListReadAccess(err) {
return echo.NewHTTPError(http.StatusForbidden, "You need to have read access to this list.")
}
return echo.NewHTTPError(http.StatusInternalServerError, "An error occured.") return echo.NewHTTPError(http.StatusInternalServerError, "An error occured.")
} }

View file

@ -110,6 +110,13 @@ func RegisterRoutes(e *echo.Echo) {
a.PUT("/lists/:list/teams", listTeamHandler.CreateWeb) a.PUT("/lists/:list/teams", listTeamHandler.CreateWeb)
a.DELETE("/lists/:list/teams/:team", listTeamHandler.DeleteWeb) a.DELETE("/lists/:list/teams/:team", listTeamHandler.DeleteWeb)
listUserHandler := &crud.WebHandler{
CObject: &models.ListUser{},
}
a.GET("/lists/:list/users", listUserHandler.ReadAllWeb)
a.PUT("/lists/:list/users", listUserHandler.CreateWeb)
a.DELETE("/lists/:list/users/:user", listUserHandler.DeleteWeb)
namespaceHandler := &crud.WebHandler{ namespaceHandler := &crud.WebHandler{
CObject: &models.Namespace{}, CObject: &models.Namespace{},
} }