Added namespace user rights
This commit is contained in:
parent
422662e3e1
commit
f2758e239c
12 changed files with 179 additions and 4 deletions
|
@ -1,3 +1,26 @@
|
|||
# Get all namespaces
|
||||
GET http://localhost:8080/api/v1/namespaces
|
||||
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}}
|
||||
|
||||
###
|
|
@ -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
|
||||
// ============
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -41,6 +41,7 @@ func init() {
|
|||
new(TeamNamespace),
|
||||
new(Namespace),
|
||||
new(ListUser),
|
||||
new(NamespaceUser),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
20
models/namespace_users.go
Normal file
20
models/namespace_users.go
Normal 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"
|
||||
}
|
40
models/namespace_users_create.go
Normal file
40
models/namespace_users_create.go
Normal 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
|
||||
}
|
25
models/namespace_users_delete.go
Normal file
25
models/namespace_users_delete.go
Normal 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
|
||||
}
|
23
models/namespace_users_readall.go
Normal file
23
models/namespace_users_readall.go
Normal 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
|
||||
}
|
15
models/namespace_users_rights.go
Normal file
15
models/namespace_users_rights.go
Normal 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)
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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{},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue