implemented namespace update via interface method

This commit is contained in:
konrad 2018-07-12 00:30:31 +02:00 committed by kolaente
parent 0aa84e653f
commit 261aaba315
No known key found for this signature in database
GPG key ID: F40E70337AB24C9B
9 changed files with 103 additions and 224 deletions

View file

@ -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)
}

View file

@ -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

View file

@ -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
}

View file

@ -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
} }

View file

@ -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)
} }

View file

@ -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)
}

View file

@ -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.")
} }

View file

@ -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, &currentUser) err = c.CObject.Update(id, &currentUser)
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)
} }

View file

@ -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)
} }