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 all namespaces
|
||||||
GET http://localhost:8080/api/v1/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}}
|
||||||
|
|
||||||
|
###
|
|
@ -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)
|
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
|
// Team errors
|
||||||
// ============
|
// ============
|
||||||
|
|
|
@ -5,7 +5,7 @@ type ListUser struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
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"`
|
UserID int64 `xorm:"int(11) not null" json:"user_id" param:"user"`
|
||||||
ListID int64 `xorm:"int(11) not null" json:"list_id" param:"list"`
|
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"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
|
@ -41,6 +41,7 @@ func init() {
|
||||||
new(TeamNamespace),
|
new(TeamNamespace),
|
||||||
new(Namespace),
|
new(Namespace),
|
||||||
new(ListUser),
|
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) {
|
if models.IsErrUserAlreadyHasAccess(err) {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "This user already has access to this list.")
|
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) {
|
if models.IsErrInvalidUserRight(err) {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "The right is invalid.")
|
return echo.NewHTTPError(http.StatusBadRequest, "The right is invalid.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package crud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/models"
|
"code.vikunja.io/api/models"
|
||||||
"fmt"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -25,7 +24,6 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
|
||||||
|
|
||||||
err = c.CObject.Delete()
|
err = c.CObject.Delete()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
|
||||||
if models.IsErrNeedToBeListAdmin(err) {
|
if models.IsErrNeedToBeListAdmin(err) {
|
||||||
return echo.NewHTTPError(http.StatusForbidden, "You need to be the list admin to delete a list.")
|
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.")
|
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)
|
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,13 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.PUT("/namespaces/:namespace/teams", namespaceTeamHandler.CreateWeb)
|
a.PUT("/namespaces/:namespace/teams", namespaceTeamHandler.CreateWeb)
|
||||||
a.DELETE("/namespaces/:namespace/teams/:team", namespaceTeamHandler.DeleteWeb)
|
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{
|
teamHandler := &crud.WebHandler{
|
||||||
CObject: &models.Team{},
|
CObject: &models.Team{},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue