From 462dfc8868622b29f082196ea775cf7667f78a23 Mon Sep 17 00:00:00 2001 From: kolaente Date: Thu, 5 Jul 2018 08:52:05 +0200 Subject: [PATCH] Implemented method to add a list to a namespace --- Featurecreep.md | 2 +- models/error.go | 20 +++++- models/helper.go | 16 +++++ models/namespaces.go | 12 ++++ public/swagger/swagger.v1.json | 80 ++++++++++++---------- routes/api/v1/lists_add_update.go | 107 ++++++++++++++++++++---------- routes/routes.go | 3 +- 7 files changed, 163 insertions(+), 77 deletions(-) create mode 100644 models/helper.go diff --git a/Featurecreep.md b/Featurecreep.md index 98c8166a864..bcedcb34bac 100644 --- a/Featurecreep.md +++ b/Featurecreep.md @@ -118,7 +118,7 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten. * [x] Erstellen * [x] Ansehen * [x] Bearbeiten - * [ ] Löschen + * [x] Löschen * [ ] Teams hinzufügen. Der Nutzer kriegt nur Teams angezeigt die er erstellt hat. * [x] Alle Listen eines Namespaces anzeigen * [ ] Listen diff --git a/models/error.go b/models/error.go index 37dda24202f..058b1f22763 100644 --- a/models/error.go +++ b/models/error.go @@ -255,7 +255,7 @@ func IsErrUserDoesNotHaveAccessToNamespace(err error) bool { } func (err ErrUserDoesNotHaveAccessToNamespace) Error() string { - return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) + return fmt.Sprintf("You need to have access to this namespace to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } // ErrUserNeedsToBeNamespaceAdmin represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) @@ -271,5 +271,21 @@ func IsErrUserNeedsToBeNamespaceAdmin(err error) bool { } func (err ErrUserNeedsToBeNamespaceAdmin) Error() string { - return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) + return fmt.Sprintf("You need to be namespace admin to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) +} + +// ErrUserDoesNotHaveWriteAccessToNamespace represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) +type ErrUserDoesNotHaveWriteAccessToNamespace struct { + NamespaceID int64 + UserID int64 +} + +// IsErrUserDoesNotHaveWriteAccessToNamespace checks if an error is a ErrNamespaceDoesNotExist. +func IsErrUserDoesNotHaveWriteAccessToNamespace(err error) bool { + _, ok := err.(ErrUserDoesNotHaveWriteAccessToNamespace) + return ok +} + +func (err ErrUserDoesNotHaveWriteAccessToNamespace) Error() string { + return fmt.Sprintf("You need to have write access to this namespace to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } diff --git a/models/helper.go b/models/helper.go new file mode 100644 index 00000000000..368168fc4f9 --- /dev/null +++ b/models/helper.go @@ -0,0 +1,16 @@ +package models + +import ( + "github.com/labstack/echo" + "strconv" +) + +func GetIntURLParam(param string, c echo.Context) (intParam int64, err error) { + + id := c.Param(param) + if id != "" { + intParam, err = strconv.ParseInt(id, 10, 64) + } + + return intParam, err +} diff --git a/models/namespaces.go b/models/namespaces.go index 542b86d85bd..561efc7d990 100644 --- a/models/namespaces.go +++ b/models/namespaces.go @@ -58,6 +58,18 @@ func (user *User) HasNamespaceAccess(namespace *Namespace) (err error) { return ErrUserDoesNotHaveAccessToNamespace{UserID: user.ID, NamespaceID: namespace.ID} } +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} +} + func GetNamespaceByID(id int64) (namespace Namespace, err error) { namespace.ID = id exists, err := x.Get(&namespace) diff --git a/public/swagger/swagger.v1.json b/public/swagger/swagger.v1.json index 9499eb77eb0..2dd74690e07 100644 --- a/public/swagger/swagger.v1.json +++ b/public/swagger/swagger.v1.json @@ -122,42 +122,6 @@ "$ref": "#/responses/Message" } } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "lists" - ], - "summary": "Creates a new list owned by the currently logged in user", - "operationId": "addList", - "parameters": [ - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/List" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/List" - }, - "400": { - "$ref": "#/responses/Message" - }, - "403": { - "$ref": "#/responses/Message" - }, - "500": { - "$ref": "#/responses/Message" - } - } } }, "/lists/{listID}": { @@ -561,6 +525,50 @@ "$ref": "#/responses/Message" } } + }, + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "lists" + ], + "summary": "Creates a new list owned by the currently logged in user in that namespace", + "operationId": "addList", + "parameters": [ + { + "type": "string", + "description": "ID of the namespace that list should belong to", + "name": "namespaceID", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/List" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/List" + }, + "400": { + "$ref": "#/responses/Message" + }, + "403": { + "$ref": "#/responses/Message" + }, + "500": { + "$ref": "#/responses/Message" + } + } } }, "/register": { diff --git a/routes/api/v1/lists_add_update.go b/routes/api/v1/lists_add_update.go index 1e755444edc..566301084cd 100644 --- a/routes/api/v1/lists_add_update.go +++ b/routes/api/v1/lists_add_update.go @@ -4,20 +4,25 @@ import ( "git.kolaente.de/konrad/list/models" "github.com/labstack/echo" "net/http" - "strconv" ) func AddList(c echo.Context) error { - // swagger:operation PUT /lists lists addList + // swagger:operation PUT /namespaces/{namespaceID}/lists lists addList // --- - // summary: Creates a new list owned by the currently logged in user + // summary: Creates a new list owned by the currently logged in user in that namespace // consumes: // - application/json // produces: // - application/json // parameters: + // - name: namespaceID + // in: path + // description: ID of the namespace that list should belong to + // type: string + // required: true // - name: body // in: body + // required: true // schema: // "$ref": "#/definitions/List" // responses: @@ -30,7 +35,55 @@ func AddList(c echo.Context) error { // "500": // "$ref": "#/responses/Message" - return addOrUpdateList(c) + // Get the list + 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) } func UpdateList(c echo.Context) error { @@ -61,12 +114,6 @@ func UpdateList(c echo.Context) error { // "500": // "$ref": "#/responses/Message" - return addOrUpdateList(c) -} - -// AddOrUpdateList Adds or updates a new list -func addOrUpdateList(c echo.Context) error { - // Get the list var list *models.List @@ -74,22 +121,16 @@ func addOrUpdateList(c echo.Context) error { return c.JSON(http.StatusBadRequest, models.Message{"No list model provided."}) } - // Check if we have an ID other than the one in the struct - id := c.Param("id") - if id != "" { - // Make int - listID, err := strconv.ParseInt(id, 10, 64) - - if err != nil { - return c.JSON(http.StatusBadRequest, models.Message{"Invalid ID."}) - } - list.ID = listID + // 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 - var err error if list.ID != 0 { oldList, err = models.GetListByID(list.ID) if err != nil { @@ -107,22 +148,16 @@ func addOrUpdateList(c echo.Context) error { } list.Owner = user - // update or create... - if list.ID == 0 { - err = models.CreateOrUpdateList(list) - if err != nil { - return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) - } - } else { - // Check if the user owns the list - if user.ID != oldList.Owner.ID { - return c.JSON(http.StatusForbidden, models.Message{"You cannot edit a list you don't own."}) - } + // 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."}) + } - err = models.CreateOrUpdateList(list) - if err != nil { - return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) - } + // 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) diff --git a/routes/routes.go b/routes/routes.go index 04ecf6b03be..a61f7441bc1 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -84,7 +84,6 @@ func RegisterRoutes(e *echo.Echo) { a.POST("/tokenTest", apiv1.CheckToken) a.GET("/lists", apiv1.GetListsByUser) - a.PUT("/lists", apiv1.AddList) a.GET("/lists/:id", apiv1.GetListByID) a.POST("/lists/:id", apiv1.UpdateList) a.PUT("/lists/:id", apiv1.AddListItem) @@ -99,5 +98,5 @@ func RegisterRoutes(e *echo.Echo) { a.POST("/namespaces/:id", apiv1.UpdateNamespace) a.DELETE("/namespaces/:id", apiv1.DeleteNamespaceByID) a.GET("/namespaces/:id/lists", apiv1.GetListsByNamespaceID) - //a.PUT("/namespaces/:id/lists") // Creates a new list in that namespace + a.PUT("/namespaces/:id/lists", apiv1.AddList) }