diff --git a/Featurecreep.md b/Featurecreep.md index 2c2c1051..139a0416 100644 --- a/Featurecreep.md +++ b/Featurecreep.md @@ -156,5 +156,6 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten. * [ ] Alle Packages umziehen * [x] Swagger UI aufsetzen +* [ ] Globale Limits für anlegbare Listen + Namespaces * [ ] mgl. zum Emailmaskieren haben (in den Nutzereinstellungen, wenn man seine Email nicht an alle Welt rausposaunen will) * [ ] Mgl. zum Accountlöschen haben (so richtig krass mit emailverifiezierung und dass alle Privaten Listen gelöscht werden und man alle geteilten entweder wem übertragen muss oder auf provat stellen) \ No newline at end of file diff --git a/models/error.go b/models/error.go index cd514723..afbc1551 100644 --- a/models/error.go +++ b/models/error.go @@ -206,3 +206,39 @@ func IsErrNeedToBeItemOwner(err error) bool { func (err ErrNeedToBeItemOwner) Error() string { return fmt.Sprintf("You need to be item owner to do that [ItemID: %d, UserID: %d]", err.ItemID, err.UserID) } + + +// ================= +// Namespace errors +// ================= + +// ErrNamespaceDoesNotExist represents a "ErrNamespaceDoesNotExist" kind of error. Used if the namespace does not exist. +type ErrNamespaceDoesNotExist struct { + ID int64 +} + +// IsErrNamespaceDoesNotExist checks if an error is a ErrNamespaceDoesNotExist. +func IsErrNamespaceDoesNotExist(err error) bool { + _, ok := err.(ErrNamespaceDoesNotExist) + return ok +} + +func (err ErrNamespaceDoesNotExist) Error() string { + return fmt.Sprintf("Namespace does not exist [ID: %d]", err.ID) +} + +// ErrNeedToBeNamespaceOwner represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) +type ErrNeedToBeNamespaceOwner struct { + NamespaceID int64 + UserID int64 +} + +// IsErrNamespaceDoesNotExist checks if an error is a ErrNamespaceDoesNotExist. +func IsErrNeedToBeNamespaceOwner(err error) bool { + _, ok := err.(ErrNeedToBeNamespaceOwner) + return ok +} + +func (err ErrNeedToBeNamespaceOwner) Error() string { + return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) +} \ No newline at end of file diff --git a/models/namespaces.go b/models/namespaces.go index ddb9e3fd..fd59247b 100644 --- a/models/namespaces.go +++ b/models/namespaces.go @@ -5,9 +5,9 @@ type Namespace struct { ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"` Name string `xorm:"varchar(250) autoincr not null" json:"name"` Description string `xorm:"varchar(700) autoincr not null" json:"description"` - OwnerID int64 `xorm:"int(11) autoincr not null" json:"owner_id"` + OwnerID int64 `xorm:"int(11) autoincr not null" json:"-"` - Owner User `xorm:"-"` + Owner User `xorm:"-" json:"owner"` Created int64 `xorm:"created" json:"created"` Updated int64 `xorm:"updated" json:"updated"` @@ -36,7 +36,7 @@ const ( NamespaceRightAdmin ) -func (user User) IsNamespaceAdmin(namespace Namespace) (ok bool, err error) { +func (user *User) IsNamespaceAdmin(namespace *Namespace) (ok bool, err error) { // Owners always have admin rights if user.ID == namespace.Owner.ID { return true, nil @@ -48,6 +48,38 @@ func (user User) IsNamespaceAdmin(namespace Namespace) (ok bool, err error) { return } +func (user *User) HasNamespaceAccess(namespace *Namespace) (has bool, err error) { + // Owners always have access + if user.ID == namespace.Owner.ID { + return true, nil + } + + // Check if the user is in a team which has access to the namespace + + + return +} + +func GetNamespaceByID(id int64) (namespace *Namespace, err error) { + namespace.ID = id + exists, err := x.Get(namespace) + if err != nil { + return namespace, err + } + + if !exists { + return namespace, ErrNamespaceDoesNotExist{ID:id} + } + + // Get the namespace Owner + namespace.Owner, _, err = GetUserByID(namespace.Owner.ID) + if err != nil { + return namespace, err + } + + return namespace, err +} + // CreateOrUpdateNamespace does what it says func CreateOrUpdateNamespace(namespace *Namespace) (err error) { // Check if the User exists @@ -56,6 +88,8 @@ func CreateOrUpdateNamespace(namespace *Namespace) (err error) { return } + namespace.OwnerID = namespace.Owner.ID + if namespace.ID == 0 { _, err = x.Insert(namespace) if err != nil { diff --git a/models/user_add_update.go b/models/user_add_update.go index d1e7a1be..54592511 100644 --- a/models/user_add_update.go +++ b/models/user_add_update.go @@ -50,6 +50,12 @@ func CreateUser(user User) (newUser User, err error) { return User{}, err } + // Create the user's namespace + err = CreateOrUpdateNamespace(&Namespace{Name: newUserOut.Username, Description: newUserOut.Username + "'s namespace.", Owner:newUserOut}) + if err != nil { + return User{}, err + } + return newUserOut, err } diff --git a/routes/api/v1/namespace_add_update.go b/routes/api/v1/namespace_add_update.go new file mode 100644 index 00000000..f9dd4f8d --- /dev/null +++ b/routes/api/v1/namespace_add_update.go @@ -0,0 +1,131 @@ +package v1 + +import ( + "git.kolaente.de/konrad/list/models" + "github.com/labstack/echo" + "net/http" + "strconv" +) + +func AddNamespace(c echo.Context) error { + // swagger:operation PUT /namespaces namespaces addNamespace + // --- + // summary: Creates a new namespace owned by the currently logged in user + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/Namespace" + // responses: + // "200": + // "$ref": "#/responses/Namespace" + // "400": + // "$ref": "#/responses/Message" + // "403": + // "$ref": "#/responses/Message" + // "500": + // "$ref": "#/responses/Message" + + return addOrUpdateNamespace(c) +} + +func UpdateNamespace(c echo.Context) error { + // swagger:operation POST /namespaces/{namespaceID} namespaces upadteNamespace + // --- + // summary: Updates a namespace + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: namespaceID + // in: path + // description: ID of the namespace to update + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/Namespace" + // responses: + // "200": + // "$ref": "#/responses/Namespace" + // "400": + // "$ref": "#/responses/Message" + // "403": + // "$ref": "#/responses/Message" + // "500": + // "$ref": "#/responses/Message" + + return addOrUpdateNamespace(c) +} + +// 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 owns the namespace + oldNamespace, err := models.GetNamespaceByID(namespace.ID) + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) + } + if user.ID != oldNamespace.Owner.ID { + return c.JSON(http.StatusForbidden, models.Message{"You cannot edit a namespace you don't own."}) + } + + err = models.CreateOrUpdateNamespace(namespace) + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) + } + } + + return c.JSON(http.StatusOK, namespace) +} diff --git a/routes/routes.go b/routes/routes.go index 4a37a619..7a0fddca 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -92,4 +92,11 @@ func RegisterRoutes(e *echo.Echo) { a.DELETE("/item/:id", apiv1.DeleteListItemByIDtemByID) a.POST("/item/:id", apiv1.UpdateListItem) + + a.GET("/namespaces") + a.PUT("/namespaces", apiv1.AddNamespace) + a.GET("/namespaces/:id") + a.POST("/namespaces/:id", apiv1.UpdateNamespace) + a.PUT("/namespaces/:id") + a.DELETE("/namespaces/:id") }