Added namespace user rights

This commit is contained in:
konrad 2018-09-04 20:15:24 +02:00 committed by kolaente
parent 422662e3e1
commit f2758e239c
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
12 changed files with 179 additions and 4 deletions

View File

@ -1,3 +1,26 @@
# Get all namespaces
GET http://localhost:8080/api/v1/namespaces
Authorization: Bearer {{auth_token}}
Authorization: Bearer {{auth_token}}
###
# Get all users who have access to that namespace
GET http://localhost:8080/api/v1/namespaces/1/users
Authorization: Bearer {{auth_token}}
###
# Give a user access to that namespace
PUT http://localhost:8080/api/v1/namespaces/1/users
Authorization: Bearer {{auth_token}}
Content-Type: application/json
{"user_id":2, "right": 0}
###
# Delete a user from a namespace
DELETE http://localhost:8080/api/v1/namespaces/1/users/2
Authorization: Bearer {{auth_token}}
###

View File

@ -415,6 +415,22 @@ func (err ErrTeamDoesNotHaveAccessToNamespace) Error() string {
return fmt.Sprintf("You need to have access to this namespace to do that [NamespaceID: %d, TeamID: %d]", err.NamespaceID, err.TeamID)
}
// ErrUserAlreadyHasNamespaceAccess represents an error where a user already has access to a namespace
type ErrUserAlreadyHasNamespaceAccess struct {
UserID int64
NamespaceID int64
}
// IsErrUserAlreadyHasNamespaceAccess checks if an error is ErrUserAlreadyHasNamespaceAccess.
func IsErrUserAlreadyHasNamespaceAccess(err error) bool {
_, ok := err.(ErrUserAlreadyHasNamespaceAccess)
return ok
}
func (err ErrUserAlreadyHasNamespaceAccess) Error() string {
return fmt.Sprintf("This user already has access to that namespace. [User ID: %d, Namespace ID: %d]", err.UserID, err.NamespaceID)
}
// ============
// Team errors
// ============

View File

@ -5,7 +5,7 @@ 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"`
Right UserRight `xorm:"int(11)" json:"right"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`

View File

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

20
models/namespace_users.go Normal file
View File

@ -0,0 +1,20 @@
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" json:"user_id" param:"user"`
NamespaceID int64 `xorm:"int(11) not null" json:"namespace_id" param:"namespace"`
Right UserRight `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 NamespaceUser
func (NamespaceUser) TableName() string {
return "users_namespace"
}

View File

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

View File

@ -0,0 +1,25 @@
package models
// Delete deletes a namespace <-> user relation
func (nu *NamespaceUser) Delete() (err error) {
// Check if the user exists
_, err = GetUserByID(nu.UserID)
if err != nil {
return
}
// Check if the user has access to the namespace
has, err := x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
Get(&NamespaceUser{})
if err != nil {
return
}
if !has {
return ErrUserDoesNotHaveAccessToNamespace{NamespaceID: nu.NamespaceID, UserID: nu.UserID}
}
_, err = x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
Delete(&NamespaceUser{})
return
}

View File

@ -0,0 +1,23 @@
package models
// ReadAll gets all users who have access to a namespace
func (un *NamespaceUser) ReadAll(user *User) (interface{}, error) {
// Check if the user has access to the namespace
l, err := GetNamespaceByID(un.NamespaceID)
if err != nil {
return nil, err
}
if !l.CanRead(user) {
return nil, ErrNeedToHaveNamespaceReadAccess{}
}
// Get all users
all := []*User{}
err = x.
Select("users.*").
Join("INNER", "users_namespace", "user_id = users.id").
Where("users_namespace.namespace_id = ?", un.NamespaceID).
Find(&all)
return all, err
}

View File

@ -0,0 +1,15 @@
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, _ := GetNamespaceByID(nu.NamespaceID)
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, _ := GetNamespaceByID(nu.NamespaceID)
return n.CanWrite(doer)
}

View File

@ -66,6 +66,9 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error {
if models.IsErrUserAlreadyHasAccess(err) {
return echo.NewHTTPError(http.StatusBadRequest, "This user already has access to this list.")
}
if models.IsErrUserAlreadyHasNamespaceAccess(err) {
return echo.NewHTTPError(http.StatusBadRequest, "This user already has access to this namespace.")
}
if models.IsErrInvalidUserRight(err) {
return echo.NewHTTPError(http.StatusBadRequest, "The right is invalid.")
}

View File

@ -2,7 +2,6 @@ package crud
import (
"code.vikunja.io/api/models"
"fmt"
"github.com/labstack/echo"
"net/http"
)
@ -25,7 +24,6 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
err = c.CObject.Delete()
if err != nil {
fmt.Println(err)
if models.IsErrNeedToBeListAdmin(err) {
return echo.NewHTTPError(http.StatusForbidden, "You need to be the list admin to delete a list.")
}
@ -49,6 +47,10 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "This user does not have access to the list.")
}
if models.IsErrUserDoesNotHaveAccessToNamespace(err) {
return echo.NewHTTPError(http.StatusBadRequest, "This user does not have access to the namespace.")
}
return echo.NewHTTPError(http.StatusInternalServerError)
}

View File

@ -134,6 +134,13 @@ func RegisterRoutes(e *echo.Echo) {
a.PUT("/namespaces/:namespace/teams", namespaceTeamHandler.CreateWeb)
a.DELETE("/namespaces/:namespace/teams/:team", namespaceTeamHandler.DeleteWeb)
namespaceUserHandler := &crud.WebHandler{
CObject: &models.NamespaceUser{},
}
a.GET("/namespaces/:namespace/users", namespaceUserHandler.ReadAllWeb)
a.PUT("/namespaces/:namespace/users", namespaceUserHandler.CreateWeb)
a.DELETE("/namespaces/:namespace/users/:user", namespaceUserHandler.DeleteWeb)
teamHandler := &crud.WebHandler{
CObject: &models.Team{},
}