implemented namespace update via interface method
This commit is contained in:
parent
0aa84e653f
commit
261aaba315
9 changed files with 103 additions and 224 deletions
|
@ -320,4 +320,36 @@ func IsErrNamespaceNameCannotBeEmpty(err error) bool {
|
||||||
|
|
||||||
func (err ErrNamespaceNameCannotBeEmpty) Error() string {
|
func (err ErrNamespaceNameCannotBeEmpty) Error() string {
|
||||||
return fmt.Sprintf("Namespace name cannot be emtpy [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID)
|
return fmt.Sprintf("Namespace name cannot be emtpy [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrNamespaceOwnerCannotBeEmpty represents an error, where a namespace owner is empty.
|
||||||
|
type ErrNamespaceOwnerCannotBeEmpty struct {
|
||||||
|
NamespaceID int64
|
||||||
|
UserID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrNamespaceOwnerCannotBeEmpty checks if an error is a ErrNamespaceDoesNotExist.
|
||||||
|
func IsErrNamespaceOwnerCannotBeEmpty(err error) bool {
|
||||||
|
_, ok := err.(ErrNamespaceOwnerCannotBeEmpty)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrNamespaceOwnerCannotBeEmpty) Error() string {
|
||||||
|
return fmt.Sprintf("Namespace owner cannot be emtpy [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNeedToBeNamespaceAdmin represents an error, where the user is not the admin of that namespace (used i.e. when deleting a namespace)
|
||||||
|
type ErrNeedToBeNamespaceAdmin struct {
|
||||||
|
NamespaceID int64
|
||||||
|
UserID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrNeedToBeNamespaceAdmin checks if an error is a ErrNamespaceDoesNotExist.
|
||||||
|
func IsErrNeedToBeNamespaceAdmin(err error) bool {
|
||||||
|
_, ok := err.(ErrNeedToBeNamespaceAdmin)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrNeedToBeNamespaceAdmin) Error() string {
|
||||||
|
return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID)
|
||||||
|
}
|
||||||
|
|
|
@ -51,6 +51,19 @@ func (user *User) IsNamespaceAdmin(namespace *Namespace) (err error) {
|
||||||
return ErrUserNeedsToBeNamespaceAdmin{UserID: user.ID, NamespaceID: namespace.ID}
|
return ErrUserNeedsToBeNamespaceAdmin{UserID: user.ID, NamespaceID: namespace.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Namespace) IsAdmin(user *User) bool {
|
||||||
|
|
||||||
|
// Owners always have admin rights
|
||||||
|
if user.ID == n.Owner.ID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if that user is in a team which has admin rights to that namespace
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// HasNamespaceAccess checks if the User has namespace read access
|
// HasNamespaceAccess checks if the User has namespace read access
|
||||||
func (user *User) HasNamespaceAccess(namespace *Namespace) (err error) {
|
func (user *User) HasNamespaceAccess(namespace *Namespace) (err error) {
|
||||||
// Owners always have access
|
// Owners always have access
|
||||||
|
@ -65,26 +78,14 @@ func (user *User) HasNamespaceAccess(namespace *Namespace) (err error) {
|
||||||
|
|
||||||
// CanWrite checks if a user has write access to a namespace
|
// CanWrite checks if a user has write access to a namespace
|
||||||
func (n *Namespace) CanWrite(user *User) bool {
|
func (n *Namespace) CanWrite(user *User) bool {
|
||||||
if err := user.HasNamespaceAccess(n); err != nil {
|
// Owners always have access
|
||||||
return false
|
if user.ID == n.Owner.ID {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasNamespaceWriteAccess checks if a user has write access to a namespace
|
|
||||||
func (user *User) HasNamespaceWriteAccess(namespace *Namespace) (err error) {
|
|
||||||
|
|
||||||
// Owners always have access
|
|
||||||
if user.ID == namespace.Owner.ID {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the user is in a team which has write access to the namespace
|
|
||||||
|
|
||||||
return ErrUserDoesNotHaveAccessToNamespace{UserID: user.ID, NamespaceID: namespace.ID}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNamespaceByID returns a namespace object by its ID
|
// GetNamespaceByID returns a namespace object by its ID
|
||||||
func GetNamespaceByID(id int64) (namespace Namespace, err error) {
|
func GetNamespaceByID(id int64) (namespace Namespace, err error) {
|
||||||
namespace.ID = id
|
namespace.ID = id
|
||||||
|
|
|
@ -1,36 +1,6 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
// CreateOrUpdateNamespace does what it says
|
// Create implements the creation method via the interface
|
||||||
func CreateOrUpdateNamespace(namespace *Namespace) (err error) {
|
|
||||||
// Check if the namespace exists
|
|
||||||
_, err = GetNamespaceByID(namespace.ID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the User exists
|
|
||||||
namespace.Owner, _, err = GetUserByID(namespace.Owner.ID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
namespace.OwnerID = namespace.Owner.ID
|
|
||||||
|
|
||||||
if namespace.ID == 0 {
|
|
||||||
_, err = x.Insert(namespace)
|
|
||||||
} else {
|
|
||||||
_, err = x.ID(namespace.ID).Update(namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the new one
|
|
||||||
*namespace, err = GetNamespaceByID(namespace.ID)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Namespace) Create(doer *User, _ int64) (err error) {
|
func (n *Namespace) Create(doer *User, _ int64) (err error) {
|
||||||
// Check if we have at least a name
|
// Check if we have at least a name
|
||||||
if n.Name == "" {
|
if n.Name == "" {
|
||||||
|
@ -48,3 +18,40 @@ func (n *Namespace) Create(doer *User, _ int64) (err error) {
|
||||||
_, err = x.Insert(n)
|
_, err = x.Insert(n)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update implements the update method via the interface
|
||||||
|
func (n *Namespace) Update(id int64, doer *User) (err error) {
|
||||||
|
// Check if we have at least a name
|
||||||
|
if n.Name == "" {
|
||||||
|
return ErrNamespaceNameCannotBeEmpty{NamespaceID:id, UserID:doer.ID}
|
||||||
|
}
|
||||||
|
n.ID = id
|
||||||
|
|
||||||
|
// Check if the namespace exists
|
||||||
|
currentNamespace, err := GetNamespaceByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the (new) owner exists
|
||||||
|
if currentNamespace.OwnerID != n.OwnerID {
|
||||||
|
n.Owner, _, err = GetUserByID(doer.ID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check rights
|
||||||
|
user, _, err := GetUserByID(doer.ID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !currentNamespace.IsAdmin(&user) {
|
||||||
|
return ErrNeedToBeNamespaceAdmin{NamespaceID:id, UserID:doer.ID}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the actual update
|
||||||
|
_, err = x.ID(currentNamespace.ID).Update(n)
|
||||||
|
return
|
||||||
|
}
|
|
@ -51,7 +51,8 @@ func CreateUser(user User) (newUser User, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the user's namespace
|
// Create the user's namespace
|
||||||
err = CreateOrUpdateNamespace(&Namespace{Name: newUserOut.Username, Description: newUserOut.Username + "'s namespace.", Owner: newUserOut})
|
newN := &Namespace{Name: newUserOut.Username, Description: newUserOut.Username + "'s namespace.", Owner: newUserOut}
|
||||||
|
err = newN.Create(&newUserOut, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return User{}, err
|
return User{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.kolaente.de/konrad/list/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -36,55 +35,7 @@ func AddList(c echo.Context) error {
|
||||||
// "500":
|
// "500":
|
||||||
// "$ref": "#/responses/Message"
|
// "$ref": "#/responses/Message"
|
||||||
|
|
||||||
// Get the list
|
return echo.NewHTTPError(http.StatusNotImplemented)
|
||||||
var list *models.List
|
|
||||||
|
|
||||||
if err := c.Bind(&list); err != nil {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"No list model provided."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the namespace ID
|
|
||||||
var err error
|
|
||||||
list.NamespaceID, err = models.GetIntURLParam("nID", c)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"Invalid namespace ID."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current user for later checks
|
|
||||||
user, err := models.GetCurrentUser(c)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
list.Owner = user
|
|
||||||
|
|
||||||
// Get the namespace
|
|
||||||
namespace, err := models.GetNamespaceByID(list.NamespaceID)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrNamespaceDoesNotExist(err) {
|
|
||||||
return c.JSON(http.StatusNotFound, models.Message{"Namespace not found."})
|
|
||||||
}
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the user has write acces to that namespace
|
|
||||||
err = user.HasNamespaceWriteAccess(&namespace)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrUserDoesNotHaveAccessToNamespace(err) {
|
|
||||||
return c.JSON(http.StatusForbidden, models.Message{"You don't have access to this namespace."})
|
|
||||||
}
|
|
||||||
if models.IsErrUserDoesNotHaveWriteAccessToNamespace(err) {
|
|
||||||
return c.JSON(http.StatusForbidden, models.Message{"You don't have write access to this namespace."})
|
|
||||||
}
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the new list
|
|
||||||
err = models.CreateOrUpdateList(list)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, list)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateList ...
|
// UpdateList ...
|
||||||
|
@ -116,51 +67,5 @@ func UpdateList(c echo.Context) error {
|
||||||
// "500":
|
// "500":
|
||||||
// "$ref": "#/responses/Message"
|
// "$ref": "#/responses/Message"
|
||||||
|
|
||||||
// Get the list
|
return echo.NewHTTPError(http.StatusNotImplemented)
|
||||||
var list *models.List
|
|
||||||
|
|
||||||
if err := c.Bind(&list); err != nil {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"No list model provided."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the list ID
|
|
||||||
var err error
|
|
||||||
list.ID, err = models.GetIntURLParam("id", c)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"Invalid ID."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the list exists
|
|
||||||
// ID = 0 means new list, no error
|
|
||||||
var oldList models.List
|
|
||||||
if list.ID != 0 {
|
|
||||||
oldList, err = models.GetListByID(list.ID)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrListDoesNotExist(err) {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"The list does not exist."})
|
|
||||||
}
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"Could not check if the list exists."})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current user for later checks
|
|
||||||
user, err := models.GetCurrentUser(c)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
list.Owner = user
|
|
||||||
|
|
||||||
// Check if the user owns the list
|
|
||||||
// TODO use list function for that
|
|
||||||
if user.ID != oldList.Owner.ID {
|
|
||||||
return c.JSON(http.StatusForbidden, models.Message{"You cannot edit a list you don't own."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the list
|
|
||||||
err = models.CreateOrUpdateList(list)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, list)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.kolaente.de/konrad/list/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddNamespace ...
|
// AddNamespace ...
|
||||||
|
@ -65,74 +63,3 @@ func UpdateNamespace(c echo.Context) error {
|
||||||
|
|
||||||
return echo.NewHTTPError(http.StatusNotImplemented)
|
return echo.NewHTTPError(http.StatusNotImplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOrUpdateNamespace Adds or updates a new namespace
|
|
||||||
func addOrUpdateNamespace(c echo.Context) error {
|
|
||||||
|
|
||||||
// Get the namespace
|
|
||||||
var namespace *models.Namespace
|
|
||||||
|
|
||||||
if err := c.Bind(&namespace); err != nil {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"No namespace model provided."})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an ID other than the one in the struct
|
|
||||||
id := c.Param("id")
|
|
||||||
if id != "" {
|
|
||||||
// Make int
|
|
||||||
namespaceID, err := strconv.ParseInt(id, 10, 64)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"Invalid ID."})
|
|
||||||
}
|
|
||||||
namespace.ID = namespaceID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the namespace exists
|
|
||||||
// ID = 0 means new namespace, no error
|
|
||||||
if namespace.ID != 0 {
|
|
||||||
_, err := models.GetNamespaceByID(namespace.ID)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrNamespaceDoesNotExist(err) {
|
|
||||||
return c.JSON(http.StatusBadRequest, models.Message{"The namespace does not exist."})
|
|
||||||
}
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"Could not check if the namespace exists."})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current user for later checks
|
|
||||||
user, err := models.GetCurrentUser(c)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
namespace.Owner = user
|
|
||||||
|
|
||||||
// update or create...
|
|
||||||
if namespace.ID == 0 {
|
|
||||||
err = models.CreateOrUpdateNamespace(namespace)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Check if the user has admin access to the namespace
|
|
||||||
oldNamespace, err := models.GetNamespaceByID(namespace.ID)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
err = user.IsNamespaceAdmin(&oldNamespace)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrUserNeedsToBeNamespaceAdmin(err) {
|
|
||||||
return c.JSON(http.StatusForbidden, models.Message{"You need to be namespace admin to edit a namespace."})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
|
|
||||||
err = models.CreateOrUpdateNamespace(namespace)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, namespace)
|
|
||||||
}
|
|
||||||
|
|
|
@ -44,9 +44,6 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error {
|
||||||
return echo.NewHTTPError(http.StatusForbidden, "You need to have write access on that list.")
|
return echo.NewHTTPError(http.StatusForbidden, "You need to have write access on that list.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if models.IsErrNamespaceDoesNotExist(err) {
|
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "The namespace does not exist.")
|
|
||||||
}
|
|
||||||
if models.IsErrNamespaceNameCannotBeEmpty(err) {
|
if models.IsErrNamespaceNameCannotBeEmpty(err) {
|
||||||
return echo.NewHTTPError(http.StatusNotFound, "The namespace name cannot be empty.")
|
return echo.NewHTTPError(http.StatusNotFound, "The namespace name cannot be empty.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package crud
|
package crud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"git.kolaente.de/konrad/list/models"
|
"git.kolaente.de/konrad/list/models"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -29,11 +28,21 @@ func (c *WebHandler) UpdateWeb(ctx echo.Context) error {
|
||||||
// Do the update
|
// Do the update
|
||||||
err = c.CObject.Update(id, ¤tUser)
|
err = c.CObject.Update(id, ¤tUser)
|
||||||
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 list admin to do that.")
|
return echo.NewHTTPError(http.StatusForbidden, "You need to be list admin to do that.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if models.IsErrNamespaceDoesNotExist(err) {
|
||||||
|
return echo.NewHTTPError(http.StatusNotFound, "The namespace does not exist.")
|
||||||
|
}
|
||||||
|
if models.IsErrNamespaceNameCannotBeEmpty(err) {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "The namespace name cannot be empty.")
|
||||||
|
}
|
||||||
|
if models.IsErrNamespaceOwnerCannotBeEmpty(err) {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "The namespace owner cannot be empty.")
|
||||||
|
}
|
||||||
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.GET("/namespaces", namespaceHandler.ReadAllWeb)
|
a.GET("/namespaces", namespaceHandler.ReadAllWeb)
|
||||||
a.PUT("/namespaces", namespaceHandler.CreateWeb)
|
a.PUT("/namespaces", namespaceHandler.CreateWeb)
|
||||||
a.GET("/namespaces/:id", apiv1.ShowNamespace)
|
a.GET("/namespaces/:id", apiv1.ShowNamespace)
|
||||||
a.POST("/namespaces/:id", apiv1.UpdateNamespace)
|
a.POST("/namespaces/:id", namespaceHandler.UpdateWeb)
|
||||||
a.DELETE("/namespaces/:id", apiv1.DeleteNamespaceByID)
|
a.DELETE("/namespaces/:id", apiv1.DeleteNamespaceByID)
|
||||||
a.GET("/namespaces/:id/lists", apiv1.GetListsByNamespaceID)
|
a.GET("/namespaces/:id/lists", apiv1.GetListsByNamespaceID)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue